![]() ULTIMATE Page Control Suite™ |
|
Ultimate Page Control Suite™ Win32 | Delphi™ | C++Builder™ |
|
C.03. HOW TO BUILD SUPERIOR PAGED, MULTIPLE WORKSPACE (PMW) APPLICATIONS |
|
This topic provides guidelines for developing software with the Paged, Multiple Workspace (PMW) model. Why PMW? How can we use PMW, and how should we use PMW? What is the appropriate design and coding approach? This topic attempts to answer these questions, and provides source examples for optimal implementations. |
|
WHAT IS PMW? WHY PMW? |
|
Paged, Multiple Workspace is an application development pattern similar to MDI, but which hosts separate workspaces in pages within a single main application window. Many advantages derive from this model, the nuclei of which are page controls. While MDI was formerly used only for applications which were obligated to host multiple workspaces, the PMW model better serves all the purposes of MDI, and further provides an ideal model for promoting the usual Single Workspace (SW) application into a processor of unlimited workspaces. Typical SW applications can actually be written with smaller code bases in UPC™ PMW, because UPC™ provides usual interface features such as paged toolbars, and because your project does not have to furnish code bases to drive functions such as wrapping or spatial maintenance of interacting regions. The UPC™ PMW pattern strips the usual development duties of SW projects to writing their usual processing core, which is automatically focused on managed workspaces by the UPC™ PMW infrastructure. Both MDI and SW therefore can be written on substantially smaller code bases, while advanced PMW better negotiates the workspaces of MDI, and multiplies the usual SW implementation into a substantially more valuable product, capable of managing unlimited workspaces from the core code base of the former SW implementation. These advantages make PMW a most promising candidate for practically every GUI application. |
|
PROGRAMMING ADVANTAGES |
|
PMW's simple purposes are accomplished by a marriage of two things:
With a prebuilt infrastructure comprising all but the unique elements of the eventual application, the task of development is largely reduced to wiring your processes to instances of the Pages[] array (TList). Instead of operating on one object of a given kind, your processing core operates on member objects or data of a page. As a result,
Members of Pages[] become our operands, with page modules furnishing the entire necessary infrastructure for propagating, negotiating, managing, and focusing the usual processing core on unlimited instances of work. A simple Single Workspace application is transformed into a PMW implementation by placing a page module onto a form and re-wiring its operations to members of pages. Multiple Document Interface applications are transformed by throwing away your child window creation and destruction cycles and your navigation system and moving your interface controls to pages. With whole areas of development eliminated, and with the remaining task reduced to wiring a minimal processing core to Pages[], PMW produces substantially faster development cycles, and exceedingly efficient product. |
|
OPTIMAL DESIGN |
|
PMW is so resilient as to tolerate however you may want to work with it. But there is always such a thing as optimal design, and a purpose of UPC™ is to make optimal design easier and faster to achieve than any other approach, even for neophytes. The techniques outlined by this article are easily accomplished, and will eliminate much unnecessary complexity from your work. The trail we are about to blaze benefits from subclassing. The little time spent mastering this approach will cut weeks or months from many development cycles, as the patterns and code provided allow anyone to deploy optimal designs in minutes. The greatest advantages of the Paged, Multiple Workspace architecture are important to understand, because they are realized by deriving custom page classes. The purpose of deriving page classes is to make our operands formal members of our pages. The purpose of the few lines of code which formally bind member data and objects to our pages is to use the prebuilt functionality of our page module to maintain all of our processed members for us. As the prebuilt architecture manages its integral Pages[] arrays, it propagates, re-orders, and destroys our member data together with the pages. Thus we all want to derive custom page classes wherever this can serve us, because subclassing eliminates the substantial work of writing a redundant second infrastructure (as we would in a conventional application). By subclassing, single algorithms perform universal operations at one juncture of your application. Otherwise, many individually coded algorithms are required to parallel the processes of the prebuilt infrastructure which can do all this for you. We will understand much more clearly how much work and complexity we eliminate, and how much more efficiently a minimal resource footprint will do the same work for us, after we address how to apply your processing core to formal or informal members of your pages. |
|
WHETHER WE WILL BENEFIT FROM SUBCLASSING |
|
First, let's address the ostensible question of whether we will benefit from subclassing, which depends on just two things:
If our pages have any common members, subclassing will always produce the best structure and the opportunity to write the least, most efficient code. Because subclassing is so little work, and because alternate approaches multiply that work, the odds of benefitting extensively are tipped steeply in favor of deriving custom page classes. Altogether, if you have "data" (constants, variables, objects, or methods) which can be made formal members of your pages, you will almost certainly benefit substantially by subclassing pages. |
|
BASIC OVERHEAD OF SUBCLASSING UPC™ PAGES OR MODULES |
|
What is the basic development overhead and technical challenge (if any) of subclassing? UPC™ page modules are designed to reduce deployment of custom page classes to an extremely simple matter. UPC™ PMInterfaces will even propagate and manage unlimited page classes at run time or design time. All the developer has to do is inform the PMInterface what page class the developer wants the PMInterface to propagate. Everything else is automated:
|
|
TWO CASES OF SUBCLASSING |
|
Design time or run time page propagation needs establish two different cases for which we subclass. Complete source for both cases follows:
Either subclassing case therefore is regularly accomplished by a small, simple body of code which most efficiently accomplishes our purposes. The PageCreationClass field is key to this simplicity, because internalization automates all related management. There are no issues but adding what formal members you want to belong to your pages and assigning class identifiers to PageCreationClass before propagating. Before we move to these topics however, we first address how to apply our processing core to members of our pages, because this illustrates how much work we actually eliminate by subclassing. Whether we have to generate custom page classes or not, the principles of applying our processing core to members of our pages are key to developing far more powerful, efficient applications with far less overhead, and for determining the best and most easily delivered application designs for any type of implementation. |
|
C.04.1. APPLYING YOUR PROCESSING CORE TO MEMBERS OF THE FOCUSED PAGE |
|
WHY IT IS PREFERABLE TO SUBCLASS |
|
We first need to explain the differences between integrating formal members with our pages, or maintaining parallel arrays of informal members. In this explanation, "focused page" may apply to a Pages[] member iterated by a process, but the primary meaning of "focused page" will be a page instance focused by the UPC™ infrastructure. As before, "data" may mean constants, variables, objects, or methods. "Members" will be the related data of pages. "Formal" members are formally bound to the page by subclassing. "Informal" members are (necessarily) bound to the page by manually coding the relationship at every necessary juncture. In soundly processing an object of the focused page, a process first verifies that there is a focused page (and/or that the focused page belongs to the intended class). After this routine test, your processing core simply applies its normal operations to formal or informal members of a page. Instead of opening a file and assigning it to a single file variable of the application for instance, the file is opened and assigned to the file variable of the focused page (a formal member), or to an array of file variables kept in parallel to the array of pages (informal members). The latter approach is less preferable, because it requires coding that synchronizes the parallel arrays with the Pages[] array. If a page is inserted at a certain position, elements must be inserted into all of your parallel arrays in the same position. If a page is moved to a new position, all of its elements in the parallel arrays must be moved to the same position. If a page is deleted, all of its elements in the parallel arrays must be deleted in such a way that the resultant condition of the parallel arrays reflects the resultant order, focus, and elements of the remaining, associated pages. While this is not preclusively challenging, subclassing pages is much easier, less confusing, and more efficient. If you build formal members into your pages by subclassing, your UPC™ module inherently manages the related data for you. If a page is inserted, appended, moved, focused, defocused, or deleted, formal members and even focus simply go along for the ride. Moreover, you can address formal members in the preferable manners of PageName.Member, Pages[Index].Member, or MyPageModule.MyPMInterface.FocusedPage.Member. To our greatest possible benefit, formal membership means the very Pages[] array (TList) is the sole vehicle of order and existence — which is wholly managed for us by the PMInterface. Consequently, we can simply iterate and/or focus members of this array to do any of the principal work of our application. There is no need to manually simulate formal binding at all. These are the reasons that optimal PMW designs will involve subclassed pages wherever data can be formally bound to pages. By this simple organization, the same processing core we would deploy in a Single Workspace (SW) implementation can (without any further coding) process unlimited workspaces. |
|
PREFERABLE TECHNIQUE: PROCESSING FORMAL MEMBERS OF THE FOCUSED PAGE |
|
UPC™ provides a FocusedPageIsAssignedAndNotNil function to test whether we can process an ostensible focused page. The following Delphi and C++ examples show how to routinely test for process worthy conditions before processing formal members of your focused page. Note that one instance of any such method processes unlimited pages, and that the very navigation system of your UPC™ module even focuses your processes on the intended workspace: |
|
DELPHI EXAMPLE C++ EXAMPLE |
|
Simple. |
|
MORE OVERHEAD-INTENSIVE TECHNIQUE: PROCESSING INFORMAL MEMBERS OF ARRAYS OR STATIC, INFORMAL COLLECTIONS |
|
To accomplish the same purpose without binding formal members to our pages, alternatively we can,
While this may appear to be relatively efficient, note that in addition to our regular process, we must write an additional OnFocus handler or fork for every page, as well as additional assignments for every simulated member relationship. These multiply our processing footprint and work by PageCount times informal members: |
|
DELPHI EXAMPLE C++ EXAMPLE |
|
Subclassing eliminates the need to write this redundant parallel infrastructure. |
|
C.04.2. SUBCLASSING TADVANCE_Page |
|
DETERMINING WHICH TADVANCE_PAGE CLASS TO DESCEND FROM |
|
TWinControl TADVANCE_Page TADVANCE_Page_MainComposite TADVANCE_Page_MainDataMaster TADVANCE_Page_PagedTools TADVANCE_Page_RelatedAndStatus The first step of deriving a page descendant with formal members is to determine which page class to descend from. You determine the page class you must descend from by identifying the PMInterface class which has to manage your page population. Note that the type of every PMInterface is indicated in the Object Inspector — making it a simple matter to determine your page base class from the PMInterface ClassName and the class tree above. You can drop a module onto a form, select the PMInterface you are going to build custom pages for, and read its class right in the Object Inspector. Having identified the class of your PMInterface, you refer to the class tree above. Page ClassNames refer to the PMInterfaces which manage them. TADVANCE_Page_MainComposite is a multi-purpose class deployed for the main pages of the composite UPC™ modules except the DataMaster. DataMaster modules deploy TADVANCE_Page_MainDataMaster for their main pages.
|
|
CLASS DEFINITION |
|
Now we will write the few lines of code which define our page class and formally bind its members. Class definitions must be placed outside of any other class definition. Because we are not installing our page class in the IDE, our definitions can be placed in the unit of our form. The usual rules apply:
The following examples provide the basic pattern for virtually anything you will want to do, including implementing page methods. Because you will seldom need to implement page methods (because your application will apply universal processes to pages), our usual obligations will be a subset of the following examples. We will usually only declare formal members in the page class definition, create and free member objects in a page constructor and destructor, and initialize member variables in the constructor: |
|
COMPLETE DELPHI PAGE SUBCLASSING EXAMPLE |
|
You write the class definition in your form's interface section: |
|
DELPHI PAGE CLASS DEFINITION |
|
You write the method implementations in the form's implementation section: |
|
DELPHI PAGE CLASS CONSTRUCTOR, DESTRUCTOR, AND METHOD DEFINITIONS |
|
We have derived a custom page class in Delphi™. |
|
COMPLETE C++ PAGE SUBCLASSING EXAMPLE |
|
Our C++ example also demonstrates variable declaration and initialization, object instantiation and destruction, overriding the page constructor and destructor, and implementing methods — practically everything you will need to do in a few lines of code. The class definition is written in the form's header (.h*): |
|
C++ PAGE CLASS DEFINITION |
|
Method definitions are written in the form's source (.cpp): |
|
C++ PAGE CLASS CONSTRUCTOR, DESTRUCTOR, AND METHOD DEFINITIONS |
|
Done. We have derived our custom page and built an extensible object oriented structure to which we can easily add any further formal page members our application may call for. Having this structure, we can create and place any necessary population of controls in the pattern of these examples. As we modify the derived page class, our modifications are compiled with our project. |
|
PROPAGATING OUR CUSTOM PAGES AT RUN TIME |
|
Our needs were to propagate pages only at run time, and so now we have to configure our page module to propagate our custom page class at run time. (Design time propagation is the next major topic.) To propagate the derived page class at run time, we simply assign our class identifier to PageCreationClass in the form's OnCreate event: |
|
DELPHI EXAMPLE C++ EXAMPLE |
|
Now we can even work with our unpopulated module in the development environment. Obviously, you can easily copy these few lines to your unit, revise for your purposes, and hit the ground running in minutes. With these few steps under your belt, you have now mastered custom page propagation for run time implementations. Very little further work will accomplish these same goals for design time page propagation. |
|
C.04.3. DESIGN TIME PROPAGATION — SUBCLASSING A MODULE AND PAGE CLASS IN A PACKAGE |
|
Now we will perform the alternate subclassing case, where we have to propagate custom pages at design time. To do this, we also have to derive and install a module. To install our module, we need to build it in a Borland Package Library (.bpl), which is a non-visual project that can be installed into the IDE. After installation, our module will appear on our Tool Palette. All we are really doing here is moving our page subclass code into the package and deriving a module to write the one familiar line which assigns our page class' identifier to PageCreationClass:
|
|
A. CREATING THE PACKAGE |
Creating a new package in the Borland Developer Studio 2006® C++ personality. |
|
|
B.1. CREATING A SOURCE UNIT AND ADDING IT TO YOUR PACKAGE |
|
Now that we have an empty package Project, we need a unit (Delphi .pas or C++ .h* and .cpp) in which to write our source. To create a unit and add it to our package:
C++ developers must also perform step B.2. Otherwise, instructions resume at B.3. |
|
B.2. CRITICAL C++ STEP — ADDING VCL.DCP AND ADVANCE_UPC_PKG.BPI TO YOUR C++ PACKAGE |
|
|
In C++, you must now add BDS\4.0\lib\release\vcl.dcp and [UPC PACKAGE DIRECTORY]\ADVANCE_UPC_Pkg.bpi to your package or you will get unresolved external reference errors when you compile.
|
|
B.3. TEST COMPILE AND SAVE YOUR PACKAGE |
Now we can write our source. |
|
C. SUBCLASSING YOUR PAGE MODULE AND PAGE CLASS IN THIS NEW PACKAGE — WRITING THE SOURCE |
|
Except for the module class declaration and its overridden constructor, the following examples are largely the same as our page subclassing examples above. Our examples however do a few things differently, merely for the sake of illustration:
|
|
THE REGISTER PROCEDURE |
Our example Register procedure demonstrates the 3 calls you will have to make, which are intuitive: |
|
DELPHI EXAMPLE PACKAGE UNIT |
|
Done. Yes, this is our whole package. We can compile and install the package, drop our custom module onto a form, and it will propagate our custom page class. Below, we do the same with C++. |
|
C++ EXAMPLE PACKAGE .H HEADER |
|
C++ EXAMPLE PACKAGE .CPP SOURCE |
|
A few clicks will now install our custom page module. |
|
D. INSTALLING YOUR PACKAGE |
Any testing you need to perform can be done by creating a dynamic instance of your module on a form. Dynamically add an AuxTool that propagates pages, and if things are working, you are ready to install. |
|
After final compiling, you can install:
Our custom page class, module, and installation are finished. You will find your custom page module on a "MY_ADVANCE_SUBCLASSES" node of the Tool Palette whenever you have a form or form contents selected (which is the behavior of BDS). |
|
SUMMARY |
|
Obviously, here too, we can copy this example code into our package, change what we need to, and be off and running in minutes. This is how to do PMW without having to write redundant parallel infrastructures to do the things you have to do in a conventional application. Wherever we have data which can be formally coupled to pages, subclassing produces the most efficient code base, turnout, and product — PMW software which will process unlimited workspaces from the smallest practical code base and footprint possible. Master these simple techniques. You will soon practice them enthusiastically and artistically — and program circles around your competitors. |
|
A.02. GOALS OF THE PAGED, MULTIPLE WORKSPACE (PMW) APPLICATION DEVELOPMENT MODEL Basic approach of PMW. Interface and application design advantages of UPC™. C.03. HOW TO BUILD SUPERIOR PAGED, MULTIPLE WORKSPACE (PMW) APPLICATIONS How to deploy custom page classes and custom UPC™ modules in minutes. A.03. DOWNLOAD FREE UPC™ DELPHI AND C++BUILDER DEMO APPLICATIONS — DEMO DOCUMENTATION Free C++ and Delphi Ultimate Page Control Suite™ programs navigate huge page populations, demonstrate advantages of UPC™ PMW application designs. |
|
• PRECEDING
|
NEXT •
|
|
|