1. Trang chủ
  2. » Công Nghệ Thông Tin

1001 Things You Wanted To Know About Visual FoxPro phần 8 potx

47 474 1
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 47
Dung lượng 566,65 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The data path manager class The data path manager class is designed to be instantiated as a transient object in the BeforeOpenTables method of a Form dataenvironment.. However, by doing

Trang 1

Reading the data from the specified source into the form's cursor is handled by the custom

ReadFile() method as follows:

LOCAL ARRAY laTfer[1,2]

LOCAL lcSceFile, lcOldFile

WITH ThisForm

*** Check the source file, clear cursor if a new file is being created

IF chkSource() > 0

IF EMPTY( ALLTRIM( txtFName.Value ) )

*** Creating a new file - just return

*** Specified source is OK, so gather full path and file name

lcSceFile = ALLTRIM( ADDBS( txtDir.Value )) + ALLTRIM( txtFName.Value )

IF JUSTEXT( lcSceFile ) = "DBF"

*** It's a table, so just read it into an array

SELECT heading, item FROM (lcSceFile) ORDER BY sortorder INTO ARRAY laTfer ELSE

*** It's an INI File (maybe) So read it

goIniMgr.ReadIniFile( @laTfer, lcSceFile )

ENDIF

*** Clear Cursor and Copy results in

ZAP IN curIniFile

INSERT INTO curIniFile FROM ARRAY laTfer

*** Strip off heading "[]" - they will be re-written anyway

REPLACE ALL heading WITH CHRTRAN( heading, '[]','') IN curIniFile

RefreshForm()

ENDWITH

Writing the data out from the cursor is handled in the custom WriteFile() method as

follows:

LOCAL ARRAY laTfer[1,2]

LOCAL lcOldFile, lcDestFile

*** Now create a new, empty file ready for writing to

*** We need to do this to ensure that deletions get made properly

lnHnd = FCREATE( lcDestFile )

Trang 2

IF lnHnd < 0

MESSAGEBOX( 'Unable to create new file ' + CHR(13) ;

+ lcDestFile, 16, 'Cannot Contuinue')

RETURN

ELSE

FCLOSE(lnHnd)

ENDIF

*** Now write the new file - ignore empty "heading" fields

SELECT * FROM curinifile WHERE ! EMPTY(heading) INTO ARRAY laTfer

WITH goIniMgr

*** Write file contents

.WriteIniFile( @laTfer, lcDestFile )

One of the most frequently written snippets of code, in almost any application, looks

something like this:

*** Save Current work area

Overview

The SelAlias class is designed to accept the alias name of a table as a parameter and switch to

that table's work area If the table is not open it will open the table for us More importantly itwill 'remember' that it opened the table and will, by default, close it when it is destroyed Theclass provides support for an additional parameter which can be used to specify an alias namewhen it is necessary to open a table with an alias other than the real name of the table

Trang 3

The class has no exposed properties or methods and does all of its work in its Init and Destroy methods By creating an object based on this class, and scoping it as LOCAL, we neednever write code like that shown above again.

A word on creating the selector object

A selector object may be created in the usual way by first loading the procedure file into

memory and then using the CreateObject() function whenever an instance is needed However, Version 6.0 of Visual FoxPro introduced an alternative method, using the NewObject()

function, which allows you to specify the class library from which a class should be

instantiated as a parameter While it is marginally slower, it does mean that you do not need toload and retain procedure files in memory and is useful when you need to create an object 'onthe fly', like this one The syntax for both methods is given below (Note that with

NewObject(), if the class is not a visual class library, Visual FoxPro expects both a 'module or

program' name as the second parameter and either an application name or an empty string or a NULL value as the third.)

*** Using CreateObject()

SET PROCEDURE TO selalias ADDITIVE

loSel = CREATEOBJECT( 'xSelAlias', <Alias>, [|<Table Name>])

*** Using NewObject()

loSel = NEWOBJECT( 'xSelAlias', 'selalias.prg', NULL, <Alias>, [|<Table Name>])

One word of caution – if you use multiple instances of this class in the same procedure ormethod to open tables, either ensure that all objects are created from the same work area or thatthey are released in the reverse order to that in which they were instantiated If you do not dothis you could end up in a work area that was selected as a result of opening a table but which

is now empty

How the selector class is constructed

As mentioned in the overview this class has no exposed properties or methods and does all of

its work in its Init or Destroy methods Internally it uses three protected properties to record:

• the work area in which it was instantiated

• the alias of the table it is managing

• whether the table was already open on instantiation

The selector class Init method

The Init method does four things First it checks the parameters An alias name is the

minimum that must be passed, and in the absence of the optional second parameter – the tablename, it assumes that the table is named the same as the alias If passed, the table name mayinclude an extension and may also include a path (Notice the use of ASSERT in this part of themethod The objective here is to warn developers of errors that may arise in the calling syntaxwithout impacting the run time code.)

Trang 4

PROCEDURE INIT( tcAlias, tcTable )

tcAlias = UPPER( ALLTRIM( tcAlias ))

IF VARTYPE( tcTable ) # "C" OR EMPTY( tcTable )

*** If already in correct work area - do nothing

IF UPPER(ALLTRIM( ALIAS() )) == tcAlias

RETURN F.

ENDIF

Then it determines whether the required alias is already in use and, if not, tries to open the

table under the specified alias If it succeeds it sets its 'lWasOpen' property to .F. This allowsthe same table to be opened more than once under different aliases If the table cannot beopened, a value of .F will be returned and the object will not be instantiated (NOTE: A

"production" version of this class should also check that the file exists, and that it is a validVisual FoxPro table, before attempting to open it with a USE command Such code has alreadybeen covered elsewhere and has been deliberately omitted from this class to keep it as simple

as possible See the ISDBF() function in Chapter 7, "How to compare the structures of two

tables" for one solution.)

*** If Specified Alias not open - Open it

IF ! USED( tcAlias )

USE (tcTable) AGAIN IN 0 ALIAS (tcAlias) SHARED

*** And Check!

llRetVal = USED( tcAlias )

*** If Forced Open, Note the fact

Finally it stores the currently selected work area number and the alias name to its

'nOldarea' and 'cAlias' properties and switches to the required work area The object is,

therefore, only instantiated when everything has worked as expected:

*** IF OK, save current work area and

*** Now Move to the specified Work Area

Trang 5

The selector class Destroy method

The Destroy method handles the tidying up of the environment If the selector opened the table,

it is closed – otherwise it is left open The work area in which the object was instantiated isthen selected and the object released:

Using the selector class

The class is intended to be used to instantiate a local object in a procedure or method whenever

it is necessary to change work areas The example program (ChgArea.prg) shows how it may

be used:

**********************************************************************

* Program : ChgArea.prg

* Compiler : Visual FoxPro 06.00.8492.00 for Windows

* Abstract : Illustrate the use of the SELALIAS class for controlling

* : and changing Work Areas Output results to screen

**********************************************************************

*** Make sure we are all closed up

CLEAR

CLOSE TABLES ALL

*** Open Clients table

USE sqlcli ORDER 1 IN 0

? 'Using Selector with Just an Alias'

? '================================='

?

? "USE sqlcli ORDER 1 IN 0"

? "Area:"+PADL(SELECT(),2)+" Using Table "+JUSTSTEM(DBF())+" as Alias "+ALIAS()

?

*** Create a Client Selection Object

loSelCli = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'SqlCli' )

? "loSelCli = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'SqlCli' )"

Trang 6

? "Area:"+PADL(SELECT(),2)+" Using Table "+JUSTSTEM(DBF())+" as Alias "+ALIAS()

?

*** Open Invoices Table (temporarily)

loSelInv = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'SqlInv' )

? "loSelInv = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'SqlInv' )"

? "Area:"+PADL(SELECT(),2)+" Using Table "+JUSTSTEM(DBF())+" as Alias "+ALIAS()

*** Open Clients Again under new Alias

loSelCli = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'Clients', 'SqlCli' )

? "loSelCli = NEWOBJECT('xSelAlias', 'SelAlias.prg', NULL, 'Clients',

'SqlCli')"

? "Area:"+PADL(SELECT(),2)+" Using Table "+JUSTSTEM(DBF())+" as Alias "+ALIAS()

?

*** Open Invoices Table (temporarily)

loSelInv = NEWOBJECT( 'xSelAlias', 'SelAlias.prg', NULL, 'Invoices', 'SqlInv' )

? "loSelInv = NEWOBJECT('xSelAlias','SelAlias.prg', NULL, 'Invoices',

Trang 7

How can I manage paths in a form's dataenvironment?

The form's dataenvironment provides many benefits including the facility to auto-open andclose tables, to set the buffering of individual tables and, at design time, to use drag and drop tocreate data bound controls on a form However, there is one perennial problem with using theform's dataenvironment - the way in which it handles the issue of paths for the tables it

contains is, to say the least, convoluted

Every cursor created in the dataenvironment has two properties that are involved with the

table name and path information, namely 'Database' and 'CursorSource' However, they are

used differently depending on whether the table in question is free or bound to a databasecontainer The actual way in which information gets stored depends upon the location of the

tables at design time according to the following rules:

Table 10.2 Cursor properties that determine the location of source data

Table Type and Location Database CursorSource

Bound Table, DBC on current

The following examples show the results of adding a table to the DE of a form while

running a VFP session with drive "G:"set as the default drive and "\VFP60\" as the current

Trang 8

The 'no code' solution!

The easy answer to this issue is, therefore, to keep all tables (free or bound) and databasecontainers in the same directory and to make sure it is defined as a sub-directory of yourdevelopment directory This ensures that Visual FoxPro only ever stores the relative path fortables (See examples 2 and 5 above.)

When you distribute your application, ensure that a subdirectory (named the same as theone used during development) is created under the application's home directory and that alldata files are installed there However, there are many times when this solution is just notpossible, most obviously when the application is being run on client machines but using shareddata stored on a server So what can we do about it?

The hard-coded solution!

A form's native dataenvironment cannot be sub-classed (although we can, of course, create ourown dataenvironment classes in code) This means that there is no way of writing code into aform class at design time to handle the resolution of paths, because such code would have to be

placed into the dataenvironment BeforeOpenTables method (Why BeforeOpenTables?

Because the OpenTables method creates the cursor objects and then calls BeforeOpenTables

after the objects are created but before the information in them is used to actually open the

tables.) So one approach is to add some code to the BeforeOpenTables method of every form to set the paths for the contained tables as necessary This will work, but seems rather an 'old- fashioned' way of doing it Apart from anything else it would make maintaining an application

with a lot of forms a major undertaking There must be a better way!

The data-driven object solution!

If we cannot sub-class the native dataenvironment, perhaps we could create our own class tohandle the work, and simply limit the code that has to be added to each instance of a form class

to a single line? Indeed we can do just that, and if we use a table to hold path information wecan also greatly simplify the task of maintaining the application Such a solution is presented inthe next section of this chapter

The data path manager class

The data path manager class is designed to be instantiated as a transient object in the

BeforeOpenTables method of a Form dataenvironment It's function is to scan through all of

Trang 9

the member objects of the dataenvironment and, for each cursor object that it finds, perform alook up in a separate 'system' table which defines the paths to be used for its tables at run time.

While we still need to add code to the BeforeOpenTables method of the dataenvironment in

every form that we create, we only need to add one line The code executed is contained in asingle class and uses a single table of pre-defined structure Maintenance is, therefore, a minormatter when you adopt this strategy

The path management table

The first component that we need in order to implement the strategy outlined above is thelookup table that will hold the information we wish Visual FoxPro to use at run time Thistable has been (imaginatively) named 'datapath.dbf' and although we have included it in theproject's database container we would normally recommend that it be used as a free table Thestructure is as follows:

Structure For: C:\VFP60\CH10\DATAPATH.DBF

SET_PATH C ( 60,0 ) NOT NULL && Drive and Path

SET_DBC C ( 20,0 ) NOT NULL && DBC Name (Bound Tables only)

SET_TABLE C ( 20,0 ) NOT NULL && Name of table in DBC (Bound Tables) && File name and extension (Free Tables)

To speed searches the table is indexed on the table name field and has an index on

DELETED() Since this table would probably be set up locally on a client machine (to handle individual's drive mappings), the issue of bringing down large indexes on DELETED() over the

network is not likely to arise We have our example table populated as illustrated in Figure

10.2 below:

Figure 10.2 Data path mapping table

Trang 10

The path management class (Example: chgpaths.scx)

The actual class, like the work area selector, does its work directly in the Init method, or methods called from Init, and has no exposed methods or properties This means that, when

instantiated, the object automatically carries out its function and can then be released Theclass defines two protected properties for its internal use, an array to hold object references tothe dataenvironment cursors and a property to store the reference to the calling

dataenvironment object itself

The principle behind its operation is that it receives a reference to the calling

dataenvironment (as a parameter) and validates that the reference is both a valid object andactually relates to an object whose base class is 'dataenvironment.' The calling DE is thenparsed to get a reference to each cursor object, which is stored to the internal array Havingopened the lookup table the final step is to retrieve each cursor's reference in turn and

determine the name of the table on which it is based (uses JUSTSTEM() to return the name from the CursorSource property).

The table name is then looked up in the mapping table and depending on the data found (ifany) the Database and CursorSource properties are updated The actual code used is:

**********************************************************************

* Program : DPathMgr.prg

* Compiler : Visual FoxPro 06.00.8492.00 for Windows

* Abstract : Uses lookup table to get correct paths for tables

* : at run time, set the paths in DE Cursor Object

* : Call from BeforeOpenTables Method of a Form DE

* : Expects a reference to the DE to be passed - can use

NEWOBJECT():

* : loPathSet = NEWOBJECT( 'dPathMgr', 'dpathmgr.prg', NULL, THIS )

**********************************************************************

DEFINE CLASS DPathMgr AS relation

*** Define Protected Properties ***

*** Array for list of Cursors

PROCEDURE Init( toDe )

LOCAL lnCursors

*** Check the parameter

IF VARTYPE( toDe ) = "O"

*** Have a valid Object reference

Trang 11

This.oDe = toDe

IF LOWER( This.oDE.BaseClass ) # "dataenvironment"

*** But it's not a DE!

ASSERT F MESSAGE "DPathMgr Class Requires a reference to the " ; + CHR(13) + "DataEnvironment Object which calls it." RETURN F.

The OpenRefTable explicitly tries to open the DataPath table This could, if necessary, be

parameterized but we cannot see any great immediate benefit for doing so (rather the opposite

in fact) There may conceivably be situations in which multiple mapping tables would berequired by an application and it would then be entirely appropriate to pass a parameter for thetable to use However, by doing so you lose one of the main benefits of this approach, which is

that the code to be inserted into the BeforeOpenTables of forms would no longer be the same

for every form:

PROTECTED PROCEDURE openreftable

*** Open up the Reference table

The GetTables method uses the stored object reference to the calling dataenvironment to

populate an array with all member objects This is then scanned and references to cursorobjects are stored to the array property The method returns the number of cursors that itfound:

Trang 12

PROTECTED PROCEDURE GetTables

LOCAL ARRAY laObj[1]

LOCAL lnObjCnt, lnCnt, loObj, lnRows, lcObjName

*** Get a list of all objects in the DE

lnObjCnt = AMEMBERS( laObj, This.oDe, 2)

*** Scan the list

lnRows = 0

FOR lnCnt = 1 TO lnObjCnt

*** Check if this object is actually a Cursor

loObj = EVAL( "This.oDe." + laObj[lnCnt] )

IF loObj.BaseClass = "Cursor"

*** It is, so save its reference to the internal array

*** Add a new row to the cursors array

no entry in the lookup table, the cursor's properties are not changed in any way:

PROTECTED PROCEDURE SetPaths( tnCursors )

LOCAL lnCnt, loObj, lcTable

*** Scan the list

FOR lnCnt = 1 TO tnCursors

*** Retrieve the Object Reference from the array

loObj = This.aCursors[lnCnt]

*** Find the Table Name

lcTable = UPPER( JUSTSTEM( loObj.CursorSource ))

*** Look up the name in the reference table which lists

*** where the data should be taken from

IF SEEK( lcTable, "datapath", "ctable")

*** We have a reference for this table!

IF ! EMPTY( set_dbc )

*** We have a bound table

loObj.Database = ALLTRIM( datapath.set_path ) ;

Trang 13

Using the data path manager

The sample form ChgPaths.SCX uses the data path manager to change the paths of the tables

that have been added to its dataenvironment To experiment with this, simply copy the

CH10.DBC (and all the tables) to an alternate location and change the SET_PATH field in the

copy of the DataPath table that remains in the original location The only code that has been added to the form's dataenvironment is the single line in the BeforeOpenTables method, as

follows:

NEWOBJECT( 'dpathmgr', 'dpathmgr.prg', NULL, THIS )

which instantiates the data path manager object and passes it a reference to the

Trang 14

Figure 10.3 Setup for the SQLCLI tables in the example form DE

Figure 10.4 The example form running – note that tables are now drawn from a

different source

Trang 15

How can I manage forms and toolbars in my application?

There are probably as many answers to this question as there are developers writing

applications using Visual FoxPro A key part of any application framework is the mechanismfor managing forms and all frameworks include a "Form Manager" of some sort The

mechanism for implementing it will depend on your framework but there are certain basic

tasks that any such manager object must perform:

• Instantiation of forms (whether SCX or VCX based)

• Tracking which form (and which instance of a form) is currently active

• Ensuring that the appropriate toolbar is available

• Adding and removing forms to its own list of active forms as they are initialized orreleased

Of course there are many other functions that could be performed by the form manager

(for example, adding/removing items to the Window list or 'cascading' forms as they areinitialized) but the four listed above constitute the basic functionality which the class mustprovide

In order to implement a form manager, it is necessary to create a 'managed' subclass, for

both Forms and Toolbars so that the additional code to interact with the manager can beisolated The following sections present the code for these classes and for a form manager classthat will handle all the basic tasks described above This class has been designed to be

instantiated as a 'global' object, which isn't the only way to do it, but is the simplest to

illustrate We could also have implemented the necessary methods as part of a broader

'application manager' class or even handled the instantiation and referencing of the formmanager indirectly through an application object

The managed form class

Forms intended to work with the form manager belong to a special class ('xFrmStdManaged' in GenForms.vcx) In addition to some necessary code in the Init, Activate and Destroy methods,

three custom properties and two methods are required for interaction with the Form Manager

as follows:

Table 10.3 Custom properties and methods for the managed form class

cInsName Property Instance name, assigned by the form manager when form

initialized cTbrName Property Name of the toolbar used by the form (if any)

lOneInstance Property When T prevents Form Mgr from creating multiple instances of

the form ReportAction Method Call manager's FormAction method

CheckFrmMgr Method Returns an object reference to the form manager

Trang 16

The custom properties

• The cInsName property is used to store the instance name assigned by the form

manager, to a form when it is initialized The form manager stores both the form nameand the assigned instance name in its internal collection This caters for multipleinstances of a form by providing the form manager a means for uniquely identifyingeach instance of a particular form

• The cTbrName is populated at design time with the name of the toolbar class

associated with the form This property will be read by the form manager at run time

to determine which, if any, toolbars are needed and to ensure that when a particularform is activated, the correct toolbar is displayed

• The lOneInstance property may be set to indicate to the form manager that the form is

single instance only When the form manager is instructed to instantiate a form whichalready exists in its collection, it will simply restore and activate the existing form ifthis property is set

Form class ReportAction method

This method provides the "single point of contact" between the form and the form manager Itcan be called by any form method that passes a parameter indicating the type of action

required from the form manager (in our example, this would be either 'ACTIVATE' or 'DESTROY'):

IF VARTYPE( loFrmMgr ) = "O"

*** Tell Form Manager to make this the active form

loFrmMgr.FormAction( tcAction, cInsName )

ELSE

*** No form Manager, so nothing special required

ENDIF

ENDWITH

The responsibility for checking for the existence of the form manager is passed to the

CheckFrmMgr method, which returns either the appropriate object reference or a NULL value If

a valid reference is returned, the method then calls the manager's FormAction method and passes both the required action and the form's instance name to provide an unambiguous

reference for the form manager

Trang 17

Form class Init method

The form class Init method expects to receive either a parameter object containing a property named cInsName or a character string which is the name to be stored to its cInsName property:

LPARAMETERS tuParam

*** Class method expects the Instance Name to be passed

*** either as 'cInsName' in a parameter object or as a string.

*** Could actually test here but what the heck! Live dangerously!

*** (In fact the test should be done in either the instance or the subclass)

IF VARTYPE( tuParam ) = "O"

*** Store cInsName property to form property

LPARAMETERS toParams

*** Extract Instance name from the parameter object

IF VARTYPE( toParams ) = 'O' AND PEMSTATUS( toParams, 'cInsName', 5 )

*** Pass Instance Name up to parent class method

*** No Instance Name specified

*** Take whatever action is appropriate at the time

ENDIF

*** Do whatever else is needed here

One major benefit of using a parameter object like this, as we discussed in Chapter 2, isthat it allows you to use 'named' parameters which simplifies the code needed to read thepassed in values

Trang 18

Form class Activate, Release and QueryUnload methods

In addition, the class includes two lines of code in both the Activate and Release methods to initiate communication with the form manager The code, in each case, calls the ReportAction

method and passes the name of the method which is executing:

ThisForm.ReportAction( JUSTEXT( PROGRAM() ))

DODEFAULT()

Finally, the QueryUnLoad method of this class includes an explicit call to the Release

method to ensure that however the user exits from the form, the Form Manager is notified

(This is because QueryUnLoad normally bypasses the Release method The next event

common to both Release and QueryUnload is Destroy, and this is too late for the Form

Manager.)

The managed toolbar class

The use of toolbars in an application is difficult to address generically Whether you usedifferent toolbars for different forms, or use a single toolbar and enable/disable options asnecessary will affect the design details However, whichever approach you take, the toolbarwill need to interact with your forms Since the Form Manager controls the forms, it seemsentirely reasonable that it should also look after the toolbars, which must, therefore, be

designed accordingly Our managed abstract toolbar class ("xTbrStdManaged" in

GenClass.vcx) has been set up as follows

First, the toolbar's ControlBox property has been set to .F. thereby ensuring that a usercannot inadvertently close a toolbar (now the responsibility of the form manger) and thetoolbar has been given a Private DataSession Three custom methods have been added and

some code added to the native Activate method of the class as follows.

The toolbar class Activate method

The issue addressed here is to ensure that whenever a toolbar is activated, it will synchronize

itself with whatever form is currently active on the screen A toolbar's Activate method is called

whenever the toolbar is shown, and since the form manager will handle toolbars by calling

their Show and Hide methods we can use this to call the method that will synchronize the

toolbar's settings with the current form:

*** Synchronize Toolbar to currently active form

*** Activate is called from Show() so will always fire

*** When Form Manager calls the Toolbar.Show()

This.SetDataSession()

The toolbar class SetDataSession method

When called, this method will either set the toolbar to the same datasession as the currently

active form and then call the toolbar's custom SynchWithForm method If no form is active, it simply calls the custom SetDisabled method:

Trang 19

LOCAL loForm

*** Get reference to active form

IF TYPE( "_Screen.ActiveForm" ) = "O" AND ! ISNULL( _Screen.ActiveForm )

*** Get Reference to Active Form

*** No form, so disable the toolbar!

*** Note: This should never happen because the form manager should

*** always be handling the visibility of the toolbar

This.SetDisAbled()

ENDIF

The toolbar class SynchWithForm method

This is simply a template method to be completed in a concrete class It is called by the custom

SetDataSession method when an active form is found and is where you would handle any

synchronization details (enabling/disabling buttons and so on) It receives, as a parameter, areference to the currently active form

The toolbar class SetDisabled method

This method provides default behavior to disable all controls on the toolbar when no active

form is found This should never happen when running under form manager control, but the

behavior is provided for anyway

The form manager class

The form manager class illustrated here has a very simple public interface There are only three

custom methods ('DoForm', 'FormAction' and 'ReleaseAll') The DoForm method is intended to

be called explicitly in code and is responsible for creating forms and their associated toolbars

The FormAction method is called automatically from the Activate and Destroy methods in the

managed form class but could easily be extended to handle other actions if needed The

ReleaseAll method is designed to be called from the shutdown process but could also be called

from a 'Close All Forms' menu item This class is designed to work together with the ManagedForm and Managed Toolbar classes described in the preceding sections The actual code isdiscussed in the following sections

Form manager definition and Init method

The class defines two arrays and four properties, all of which are protected as follows:

Trang 20

Table 10.4 Custom properties and methods for the form manager form class

Name PEM Purpose

aFmList Array Property The Forms Collection

aTbList Array Property The Toolbars Collection

nFmCount Property Number of forms contained in the Forms Collection

nTbCount Property Number of toolbars contained in the toolbars Collection

nFmIndex Property Index to the currently active form in the Forms Collection

nTbIndex Property Index to the currently active toolbar in the Toolbar Collection

The Init method simply initializes these properties:

DEFINE CLASS xFrmMgr AS RELATION

PROTECTED ARRAY aFmList[1,4], aTbList[1,3]

PROTECTED nFmIndex, nFmCount, nTbCount, nTbIndex

FUNCTION Init

WITH This

*** Initialise Properties

aFmList = "" && Form Collection

nFmCount = 0 && Managed Form Count

nFmIndex = 0 && Index into the Collection for current form

aTbList = "" && Toolbar Collection

nTbCount = 0 && Toolbar Count

nTBIndex = 0 && Index into the Collection for current toolbar

ENDWITH

ENDFUNC

The form manager DoForm method

This custom method is where the form manager creates forms and any associated toolbars It isthe largest single method in the class but does not really lend itself to further decomposition.The method allows for up to three parameters to be passed in, but we would normally expect topass a single parameter object The only reason for this structure is to simplify calling themethod directly from a menu item

The first two parameters are used for the name and method to be used for instantiating the

form When calling an SCX only the form name need be passed, unless there are additional

parameters, because the default value for the second will be .F. - which will invoke the DO FORM mechanism If the form is to be instantiated from a class, the second parameter mustalways be passed explicitly as .T.

The first thing this method does is to check the parameters and generate (using

SYS(2015)) a valid character string which will be used for both the object reference to the formand its "instance" name:

****************************************************************

*** xFrmMgr::DoForm( tcFmName, tlIsClass, tuParm1, tuParm2, tuParm3 )

*** Exposed Method to Run a Form

*** Provision for 3 params, but normally would expect only 1 (as

*** A parameter object)

****************************************************************

Trang 21

FUNCTION DoForm ( tcFmName, tlIsClass, tuParm1, tuParm2, tuParm3 )

LOCAL lnFormParams, lcFmName, loFmRef, lnFmIdx, llRetVal, lnCnt

"Name of a Form, or a Form Class," + CHR(13) ;

+ "Must be passed to Form Manager DoForm()"

*** Check to see if we have this Form already?

.nFmIndex = FmIdx(tcFmName)

*** If we have it, is it single instance

IF nFmIndex > 0

*** Get a reference to the form and see if we can

*** have multiple instances of it.

Trang 22

If the form is not already instantiated, or if it is but is not single instance, then a new form

is required First we generate the object reference and instance name, and then construct aparameter object for the form:

*** Either first run of the form, or a new instance is required

*** Create the parameter object

*** Generate an Instance Name and Object Reference

STORE SYS(2015) TO lcFmName, loFmRef

*** Create the Parameter Object

oParams = NEWOBJECT( "xParam", "genclass.vcx" )

WITH oParams

*** First the Instance Name

AddProperty( 'cInsName', lcFmName )

*** Add a property count

AddProperty( 'nParamCount', lnFormParams )

*** Add any additional parameters to be passed to the form

IF lnFormParams > 0

FOR lnCnt = 1 TO lnFormParams

lcPName = "tuParm" + ALLTRIM(STR(lnCnt))

AddProperty( lcPName, &lcPName )

*** Run as a Form using NAME and LINKED clauses

DO FORM (tcFmName) NAME loFmRef WITH oParams LINKED

ENDIF

*** Update the Collection with the new form details

IF VARTYPE( loFmRef ) = "O"

*** YEP - got a form, so increment form count and populate the collection nFmCount = nFmCount + 1

DIMENSION aFmList[.nFmCount, 4]

.aFmList[.nFmCount, 1] = loFmRef && Object Reference

.aFmList[.nFmCount, 2] = lcFmName && Instance Name

.aFmList[.nFmCount, 3] = tcFmName && Form Name

.aFmList[.nFmCount, 4] = UPPER( ALLTRIM ( loFmRef.cTbrName )) && Toolbar

to use

*** Make this the Active Form

Trang 23

After creating the form we check to ensure that the form really did get created, and then

we populate the Forms collection with the relevant information The last thing to do is to

handle display of the toolbars, which is done by calling the DoToolBar method:

*** Finally sort out the toolbar requirement

The form manager DoToolbar method

This method is called only when a new form, or a new instance of a form, is created Itsfunction is to update the Toolbar collection if the new form requires a toolbar This mayinvolve incrementing the count of an existing toolbar or creating a new toolbar The samemethod handles both contingencies:

****************************************************************

*** xFrmMgr::DoToolBar( tcTbName )

*** Protected method to create or set the named toolbar active

*** Called when creating a form

*** Check to see if we have the toolbar already

lnTbIdx = TbIdx( tcTbName )

IF lnTbIdx > 0

*** We already have this one, so activate it

*** And increment its counter by one

aTbList[ lnTbIdx, 2] = aTbList[ lnTbIdx, 2] + 1

ELSE

*** We need to create it and add it to the collection

Ngày đăng: 05/08/2014, 10:20

TỪ KHÓA LIÊN QUAN