Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

1001 Things You Wanted To Know About Visual FoxPro phần 5 ppsx
Nội dung xem thử
Mô tả chi tiết
174 1001 Things You Always Wanted to Know About Visual FoxPro
which you want to display a fully populated grid where the last available record is displayed as
the last line of the grid. This can be done, but it is not a trivial exercise.
At first you may think "Oh, this is a piece of cake! All I have to do is something like this:"
WITH This
GO BOTTOM IN ( .RecordSource )
SKIP - <Number of rows - 1> IN ( .RecordSource )
.SetFocus()
ENDWITH
But if you actually test this approach, you will quickly discover that it doesn't perform
correctly. Even though the grid is positioned on the correct record, this record is in the middle
of the page and you must use the PAGE DOWN key to see the last record. If this is a common
requirement for your grids, this sample code in the Init method of EndOfGrid.scx is generic
and can easily be put in a custom method of your grid class:
LOCAL lnKey, lnMaxRows, lcKeyField
*** Make sure procedure file is loaded
SET PROCEDURE TO Ch06.prg ADDITIVE
*** Display the last page of the grid when the form instantiates
IF DODEFAULT()
WITH Thisform.GrdCustomer
*** Calculate the maximum number of rows per grid page
lnMaxRows = INT( ( .Height - .HeaderHeight - ;
IIF( INLIST( .ScrollBars, 1, 3 ), SYSMETRIC( 8 ), 0 ) ) / .RowHeight )
*** Get the name of the primary key field in the grid's RecordSource
*** GetPKFieldName is a function defined in the procedure file for this
*** Chapter (Ch06.prg)
lcKeyField = GetPKFieldName( .RecordSource )
*** Get the primary or candidate key of the first record to be displayed
*** on the last page of the grid since the goal is to have the grid filled
*** when the form opens
GO BOTTOM IN ( .RecordSource )
SKIP -( lnMaxRows - 1 ) IN ( .RecordSource )
*** Save the primary or candidate key of this record if it has one
IF ! EMPTY( lcKeyField )
lnKey = EVAL( .RecordSource + '.' + lcKeyField )
GO BOTTOM IN ( .RecordSource )
.Refresh()
*** Scroll up one record until we are on the one we want
DO WHILE .T.
.ActivateCell( 1, 1 )
IF EVAL( .RecordSource + '.' + lcKeyField ) = lnKey
EXIT
ELSE
.DoScroll( 0 )
ENDIF
ENDDO
Chapter 6: Grids: The Misunderstood Controls 175
ENDIF
ENDWITH
ENDIF
How do I use a grid to select one or more rows? (Example:
SelGrid.scx)
A multiselect grid is the perfect solution when you must present the user with a large list of
items from which multiple selections may be made. The only requirement is that the table used
as the grid's RecordSource must have a logical field that can be set to true when that row is
selected. If the base table does not have a logical field, it's a simple matter to provide one by
either creating a local view from the table or by using it to construct an updateable cursor. See
Chapter 9 for details on how to construct a RecordSource for the grid containing this selection
flag.
Figure 6.5 Multiselect grid using graphical style checkboxes
Setting up this grid is so easy it doesn't even require a custom grid class. In the example
above, we merely dropped one of our base class grids (Ch06::grdBase) onto our form and
added three lines of code to its SetGrid method:
DODEFAULT()
*** Set up for highlighting ALL Selected Rows
This.SetAll( 'DynamicBackColor', ;
'IIF( lSelected, RGB( 0, 0, 128 ), RGB( 0, 0, 0 ) )', 'COLUMN' )
This.SetAll( 'DynamicBackColor', ;
'IIF( lSelected, RGB( 0,255,255 ), RGB( 255, 255, 255 ) )', 'COLUMN' )
176 1001 Things You Always Wanted to Know About Visual FoxPro
All that remains is to add a graphical style check box to the grid's first column and put two
lines of code in its Click method:
DODEFAULT()
KEYBOARD '{DNARROW}'
This code moves the cursor to the next row in the grid. More importantly, it correctly
highlights the current row depending on whether or not the user selected it. This is because the
SETALL method that is used to highlight selected rows, does not change the grid's appearance
until either a Grid.SetFocus() or a Grid.Refresh() is issued. Constantly refreshing the grid will
degrade performance and moving to the next row accomplishes the same objective and makes
the grid more convenient for the end user.
How do I give my multiselect grid incremental search capability?
(Example: Ch06.VCX::txtSearchGrid)
Technically speaking, this is accomplished by dropping a text box with incremental search
capability into one or more columns of the grid. Obviously, any column with this functionality
must, by definition, be read-only. Otherwise, the user would constantly be changing the data as
he was searching!
The key to creating an incremental search text box for use in a grid is the addition of the
cSearchString property to hold the current search string. This implies that all keystrokes are
intercepted and passed to a custom keystroke handler that either uses the key to build the
search string or passes it through to the control's KeyPress method to be handled by default.
(Navigation keys like TAB, ENTER, and DNARROW can be handled as Visual FoxPro normally
handles such keystrokes.)
The keystroke handler also requires some means of implementing a "time out" condition to
reset the search string. The custom property, nTimeOut, holds the maximum number of seconds
that may elapse between keystrokes before the control times out and its cSearchString property
is reset. We also added the tLastPress property to hold the last time a key was pressed in
DateTime format. These two properties are used by our custom Handlekey method to
accomplish this task.
We gave the text box a SetTag method that includes code to optimize searching by using
index tags if they are available. It runs when the control is instantiated. We assume, as always,
that all single field index tags have the same name as the field on which they are based. This is
how the SetTag method initializes the text box's custom cTag property:
WITH This.Parent
*** If the column is bound, see if there is a tag in the grid's RecordSource
*** that has the same name as the field the column is bound to
IF ! EMPTY( .ControlSource )
*** Make sure the procedure file is loaded
SET PROCEDURE TO Ch06.Prg ADDITIVE
IF IsTag( JUSTEXT( .ControlSource ), .Parent.RecordSource )
This.cTag = JUSTEXT( .ControlSource )
ENDIF
ENDIF
Chapter 6: Grids: The Misunderstood Controls 177
ENDWITH
Most of the work is done in the control's HandleKey method, which is called from its
KeyPress method. If the keystroke is handled successfully by this method, .T. is returned to
the KeyPress method, which then issues a NODEFAULT. If the keystroke is not handled by this
method, .F. is returned and the default Visual FoxPro KeyPress behavior occurs:
LPARAMETERS tnKeyCode
*** First check to see if we have a key that we can handle
*** A 'printable' character, backspace or <DEL> are good candidates
IF BETWEEN( tnKeyCode, 32, 128 ) OR tnKeyCode = 7
WITH This
*** First check to see if we have timed out
*** and reset the search string if we have
IF DATETIME() - .tLastPress > .nTimeOut
.cSearchString = ''
ENDIF
*** So now handle the key
DO CASE
CASE tnKeyCode = 7
*** If the delete key was pressed, reset the search string
*** and exit stage left
.cSearchString = ''
RETURN .T.
CASE tnKeyCode = 127
*** Backspace: Remove the last character from the Search string
IF LEN( .cSearchString ) . 1
.cSearchString = LEFT( .cSearchString, LEN( .cSearchString ) - 1 )
ELSE
.cSearchString = ''
RETURN .T.
ENDIF
OTHERWISE
*** A garden variety printable character
*** add it to the search string
.cSearchString = .cSearchString + CHR( tnKeyCode )
ENDCASE
*** Search for the closest match in the grid's recordsource
.Search()
*** Update value for KeyPress interval timer
.tLastPress = DATETIME()
ENDWITH
ELSE
*** Not a key we can handle. Let VFP handle it by default
This.cSearchString = ''
RETURN .F.
ENDIF