[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7] [ 8 ] [ 9 ]

The framework accesses metatable information through the _DocumentPicker classes. _DocumentPicker is an abstract standard dialog class, which contains a picklist and a couple of buttons. The working _DocumentPicker subclasses each have their own way of using the information in the metatable to perform two tasks:

  1. show the documents in the picklist
  2. run the appropriate action when the user picks a document.

Each subclass stores the relevant metatable fields into an array, which serves as the data source for the listbox in the dialog. The same array holds the metatable information that will eventually act on the user's choice.

The _DocumentPicker superclass has an abstract FillDocumentArray() method, designed to perform the first service during the dialog Init(), and another abstract method called ExecDocument(), which is triggered whenever/however the user makes a selection from the document list.

All the _DocumentPicker working subclasses receive information from the framework to indicate which of two states it is supposed to be in when it instantiates and executes documents. The _DocumentPicker superclass simply makes note of this logical value, leaving it to the subclasses to interpret it.

The various _DocumentPicker's FillDocumentArray() methods concentrate on different document types, and fill the array with the appropriate information for that type. Their ExecDocument() methods call different application object methods depending on their document type and the dialog's current state, sending information from the metatable from the array to method arguments as needed.

The first column in the table below shows you the names of these working classes and the document types that will appear in their lists, courtesy of their FillDocumentArray() method. The other columns show the application methods that call them, and the meaning assigned to their two states when ExecDocument() is triggered. Each application method listed here takes a logical parameter (defaulting to .F., State 1) to indicate for what purpose the class presents its document list.

Table: Document-management elements of the framework

DocumentPicker Subclass App Method State 1 State 2
_NewOpen, for forms DoNewOpen() Edit Add
_ReportPicker, for reports and labels DoReportPicker() Run report/label Modify/Add -- not implemented in the _application superclass
_FavoritePicker, for documents and files of any type DoStartupForm() Run document/file Put document/file on Favorites menu for quick access

In the figure below, you can see an example of each of these _DocumentPicker working subclasses, each in one of its two states. I've displayed _FavoritePicker in State 2, so you can see the relationship between the marked items in the list, one from the metatable and one from disk, to the items already picked for the Favorites list on the menu popup.

Various documentpicker dialogs in action

The Mediator enhances the document-framework connection

I've said earlier that this framework can accept any sort of form or formset, and that's true. The framework will take care of the form or formset at important points in the application's life.

For example, it will make sure that a form -- or even in the forms in a formset -- properly appear inside its MDI frame window in a "top form" application. It will do this even if you've forgotten to fix the ShowWindow property properly, and it takes care of toolbars in the same way.

For another example, when you choose to close the application, the framework will look for buffered data that has been changed and edited throughout all its open documents, and treat them all the same way. The application shouldn't close until the question in the figure below has been satisfactorily opened for all forms in the application, and it should cancel if told to do so for any form in the application. You don't have to do anything special to get this important, Windows-like behavior.

Giving documents consistent behavior is one of the responsibilities of the framework.

However, obviously a form or formset could benefit by having access to the application's features.

To provide "framework knowledge" in a form without editing the form, the framework provides an object I'll call its form mediator here.

The form mediator wraps some of the framework's most important capabilities. It "loads" application-specific attributes into a form or a private data session, on startup of the form, and on demand. It provides a place within a form for the framework to attach information you've placed in the metatable, which describes attributes you want for this form. It also provides a reference to the application object on demand, to the form and its members.

To make unit testing easier, and to allow you to use the same form in both framework- and non-framework settings for other reasons as well, the mediator takes no action if it can't find a framework object. Your form just does without framework capabilities in that case.

How do you get a form mediator in your forms? There are several different ways:

  • If you have the Wizard generate your forms, you'll get form mediators and some appropriate mediator code (courtesy of the Wizard base classes) automatically.
  • You can drop a mediator on any form or formclass you like, and add code (or not) yourself. This gives you a chance to use a different mediator subclass that you've customized, rather than the base _FRAMEWK.VCX version.
  • You can create a project hook subclass that puts a mediator into forms for you, and adds code at the same time. I've created one such subclass, as you'll see later in these notes.
  • You can let the framework add mediator objects to your forms at runtime. (The application property determining this behavior is: lEnableFormsAtRuntime. Its default value is .T. The mediator class instantiated is determined by the app object's cMediatorClass and cMediatorClasslib properties. These properties default to "_FormMediator" and "_framewk.vcx", and return to these values if any errors occur.) This allows a great deal of functionality to be "implanted" in an otherwise framework-ignorant form. The framework can give the form its usual navigation toolbar and menu if the metatable says it should, and it can apply various other standard application attributes. However, adding at runtime will not permit you to add code into the mediator or form methods to takes full advantage of the relationship.

No matter how you add a mediator, there is a default "form mediator member name", which I recommend you use for this purpose and for no other. You can assign this name yourself, using the following #DEFINE in your generated header file:


Using this name for your forms' mediator objects is the fastest way for the application to communicate with a form via the mediator.

The application will always look further for a form mediator by checking for an object with the _FormMediator class in its class hierarchy, if it can't find a member with this name. It will also do so if it finds a member with this name that isn't descended from _FormMediator (so nothing will go wrong if you do use the default form mediator name for some other type of object). When it's creating a mediator at runtime, also, it will generate a unique mediator object name if this name is already in use. However, all these actions are time-consuming compared to simply finding the mediator through its default name.

What can a form do with its mediator? The table below lists many of the important properties and methods you'll find in the _formmediator class, in _FRAMEWK.VCX. Within the table, these items are grouped by their general purpose.

[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7] [ 8 ] [ 9 ]