Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Grid Class - Adding Header Code Dynamically
Message
General information
Forum:
Visual FoxPro
Category:
Classes - VCX
Miscellaneous
Thread ID:
00784946
Message ID:
00790805
Views:
39
Mr. Nathwani,

To explain this process in detail would required more than one article.

The basic process is simple:

A class library is just a Foxpro table with a funny extension (.vcx/.vct instead of .dbf/.dbt). If Fox can create object from information stored in a data file, then so can I, and from a much simpler column dictionary table.

First, create a base grid class that contains all of the properties and methods that you are likely to use. This grid had no columns.

From here you can subclass the base grid and add whatever columns you need. This is what we do for simple grids that the user cannot add columns to or remove columns from (non-customizable grids).

For customizable grids, the grid is instantiated from the base grid class, and in the Init(), the .ColumnCount is zero. This is the grid's clue that it needs to add column from the column dictionary table.

There are actually two such tables. The master table contains information about all columns in all grids. At run-time this table is read-only (in fact we build it into the executable so it cannot possibly be altered). The other table is the working dictionary that the grid reads from and writes to. This contains information about all of the columns that are on working grids. These columns include (1) the columns that we have designated must be on a grid (required columns) and columns the user has elected to place on the grid (optional columns). When the appliation is first run, the startup process looks to see whether a working grid dictionary table exists. If no, then it creates a working dictionary from the master dictionary assigning to each grid its required columns.

If the Init() of the grid discovers that it has no column, then it calls a grid method, m_Add_Columns() to add to the grid the columns it finds are defined for the grid in the working column dictionary table.

The dictionaries contain at minimum:

1. The name of the grid,
2. The name of each column
3. A column property memo and
4. A column methods memo.

The property and method memos follow the same format as is used in the .vct methods and properties memos.

We experimented with having a separate related table for the controls that are added to columns, but decided over time (and at the risk of sub-normalizing our dictionary table) that a separate table was not needed. (Has anyone else noticed that relations among tables is a lot more fragile now than it was in VFP 5.0a?)

We decided that our column would never have more than two controls (other than the header). So we added additional fields to hold the name, class and class library of each of two controls and the properties and methods of each control. Now each column record holds not only the information about the column, but information about all of the controls on the column. This serves to make creating the column from code a much simpler prospect.

Here is the structure of the column dictionary table:
Structure for table:    C:\CPDEVEL\CPDM50\SYSTEM\COLDATA.DBF
Number of data records: 4739    
Date of last update:    05/20/2003
Memo file block size:   64
Code Page:              1252    
Field  Field Name      Type                Width         Comment
    1  GRIDNAME        Character              20         Name of parent grid
    2  FIELDNAME       Character              20         Field data source
    3  COLNAME         Character              20         Column name
    4  COLPROPS        Memo                    4         Column properties
    5  HDRPROPS        Memo                    4         Column methods
    6  C1CLASS         Character              40         1st Control class
    7  C1CLASSLIB      Character              40         1st Control class library
    8  C1CLASSTYP      Character               3         1st Control type
    9  C1PROPS         Memo                    4         1st Control properties
   10  C2CLASS         Character              40         2nd Control class
   11  C2CLASSLIB      Character              40         2nd Control class library
   12  C2CLASSTYP      Character               3         2nd Control type
   13  C2PROPS         Memo                    4         2nd Control properties
   14  COLMETHODS      Memo                    4         Column methods
   15  HDRMETHODS      Memo                    4         Header methods
   16  C1METHODS       Memo                    4         1st Control methods
   17  C2METHODS       Memo                    4         2nd Control methods
   18  SYSCOLUMN       Logical                 1         True if required on the grid
** Total **                                  260
The column class must be created in code. Even though VFP 8.0 now allows you to specify the class from which to create a column object, it still does not allow a column to be created visually. This is a fine example of too little (still no visual columns), to late (I've had this technology for five years, now), so the new VFP 8.0 feature is no temptation for me to change the way I am doing things now. However, if I were starting new today, I would give it a serious look.

Creating a column class in code is a little tricky. What we do to reduce the problems is to create a base column object, then add any special properties after the column is created.

Here is the code to create a BaseColumn and BaseHeader class. A lot of this refers to properties and method of our base grid class, so will probably mean nothing to you. But for what it's worth...
********************************************************************************
FUNCTION DEFINEBASECOLUMNCLASSES()
********************************************************************************
*	
*   OVERVIEW:   Creates the base column and base header classes for a code grid.
*
*   CALLED BY:   BaseGrid.m_AddColumns()
*
DEFINE CLASS BaseHeader AS HEADER
   
   Alignment   = 2         && Middle Center
   Backcolor   = 14741744   && RGB( 240,240,224)
   FontName    = "Verdana"
   FontSize    = 8
   Visible     = .T.
   WordWrap    = .T.
   
   PROCEDURE Init

      LOCAL lcFieldSource
      WITH this

         lcFieldSource = LOWER( ALLTRIM( .parent.FieldSource ) )
         IF DBFSELECT( 'sdtmeta', .T. )

            LOCATE FOR LOWER( ALLTRIM( objectname ) ) == lcFieldSource .AND. ALLTRIM( rectype ) $ 'FU'
            IF FOUND()

               .Caption = EVALUATE( fcaption )

            ENDIF

         ENDIF

      ENDWITH

   ENDPROC

   PROCEDURE RightClick
   
   *   BaseHeader.RightClick()
   *
   *   OVERVIEW:   Calls the BaseGrid function m_Header_RightClick_Menu()
   *               to display a shortcut menu for the header.
   WITH this
      
      *   Before calling any grid method, ensure it exists.
      IF PEMSTATUS( .parent.parent, "m_Header_RightClick_Menu", 5 )
      
         *   Pass a reference to the column as a paramter to 
         *   m_Header_RightClick_Menu()
         .parent.parent.m_Header_RightClick_Menu( .parent )
         
      ENDIF
         
   ENDWITH

   RETURN
   
   ENDPROC

ENDDEFINE

DEFINE CLASS BaseColumn AS Column

   Backcolor   = 14741744   && RGB( 240,240,224)

   *   Stores the code that created this column as a class.  The assignment
   *   of the actual text string is made in m_Add_Columns().  This is only
   *   for debugging.
   ClassDefinition   = ""

   *   Add the ColumnIndex property.  This property is an integer pointer to
   *   the column in the Columns[] collection. (Not currently used JME 09/23/02)
   ColumnIndex      = 0

   *   Specifies whether the current control in the column contains a
   *   non-empty value.  If a control is "required", then it must contain a
   *   a value that is non-empty.  Whether the value is valid is tested by the control
   *   itself in its own Valid() method.  The grid only tests for a non-empty value
   *   if Column.Complete is true.  See: m_Required_Entries_Completed().
   Complete         = .F.

   *   Add the property to the column, but it does not apply if the column is
   *   ReadOnly.  JME 04/22/03
   ControlEnabled   = .T.

   *   Specifies the original table.field from which the view controlsource for this
   *   column was constructed.
   FieldSource      = ''
   
   FontName         = "Verdana"
   FontSize         = 8

   *   Specifies the column's position in the freeze panel.  If zero, the column
   *   is not in the freeze panel
   FreezeOrder      = 0

   *   Specifies whether the column contains the control with focus.  Set by each
   *   control as it gains and loses focus.  Also set in m_Control_When().  The
   *   form property, "ControlWithFocus" contains the name of the control with
   *   current focus.  Used in setting dynamic colors.  See: m_Set_DynamicColor_Expession()
   HasFocus         = .F.

   *   Holds the full header caption in the event the caption is truncated
   *   for display by m_Column_Resize()
   HeaderCaption    = ""
   
   *   Holds the number of lines required to display the header caption.
   HeaderLines      = 1
         
   *   Specifies whether the column is readonly per security settings.  This property
   *   will be set to .T. if a user is locked out of this column, then the column's
   *   readonly property will also be set to TRUE.  In the event of an attempt to change
   *   the column's readonly property, a readonly_assign method will fire, which will
   *   check ReadOnlyBySecurity, and stop the changing of the value if it's set to .T.
   ReadOnlyBySecurity = .F.

   *   If a non-blank entry is required in a column, the Column.Required property
   *   will be true.  Otherwise it will be false.  Used in m_Required_Entries_Completed()
   *   to determine whether a non-blank value is required in a row before moving to
   *   a new row.
   Required         = .F.
   
   Visible         = .T.
   
   PROCEDURE Init
   
   *   BaseColumns.Init()
   *
   *   OVERVIEW:   Remove the existing header and edit controls from the column
   *               in preparation for adding a new BaseHeader and edit controls
   *               in DEFINECOLUMN().
   *
   LOCAL lnI
   
   WITH this
   
      FOR lnI = .ControlCount TO 1 STEP -1
         .RemoveObject( .Controls[ lnI ].Name )
      NEXT
      
      .ColumnIndex = .parent.ColumnCount
      .Visible     = .T.
      
   ENDWITH
   
   RETURN
   
   ENDPROC
      
   PROCEDURE Moved
   
   *   BaseColumn.Moved()
   *
   *   OVERVIEW:   When this column is moved.  Call the BaseGrid
   *               function m_Column_Moved() to respond to the
   *               event.
   *
   WITH this
      
      *   Before calling any grid method, ensure it exists.
      IF PEMSTATUS( .parent, "m_Column_Moved", 5 )
         .parent.m_Column_Moved( .Name )
      ENDIF
         
   ENDWITH
      
   RETURN
   
   ENDPROC
   
   PROCEDURE ReadOnly_Assign()
   
   *   BaseColumn.ReadOnlyBySecurity()
   *
   *   OVERVIEW:   Prevents a column set ReadOnly by security from being
   *               reenabled.
   *
   LPARAMETERS tlReadOnly
   
   WITH this
   
      IF PEMSTATUS( this, "ReadOnlyBySecurity", 5 ) .AND. .ReadOnlyBySecurity
         .ReadOnly = .T.
      ELSE
         .ReadOnly = tlReadOnly
      ENDIF
      
   ENDWITH
   
   RETURN
   
   ENDPROC
            
   PROCEDURE Resize
   
   *   BaseColumn.Resize()
   *
   *   OVERVIEW:   When this column is resized.  Call the BaseGrid
   *               function m_Column_Resize() to respond to the
   *               event.
   *
   WITH this
      
      *   Before calling any grid method, ensure it exists.
      IF PEMSTATUS( .parent, "m_Column_Resize", 5 )
      
         *   Pass a reference to the Header object to the method.
         *   The header is always .Controls[1]
         .parent.m_Column_Resize( .Controls[ 1 ] )
         
      ENDIF
         
   ENDWITH
      
   RETURN
   
   ENDPROC

ENDDEFINE
There is obviously a little more to this. But this should get you started.

Regards,
Jim Edgar
Jurix Data Corporation
jmedgar@yahoo.com

No trees were destroyed in sending this message. However, a large number of electrons were diverted from their ordinary activities and terribly inconvenienced.
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform