(This article is translated from: http://www.devexpress.com/support/BestPractices/vcl/sap/ Posted in 2004 11, December development expert, but the magazine has deleted reprint, please contact the author) in this article In order to discuss a method of better building a Windows client application via Borland VCL. Finally, we will generate a run library and a sample that helps us easily implement the module and user interface separation.
This article is divided into two parts:
First, in the development of the simple application framework, we did not use developer expression library, so the VCL developers of those non-developer express customers can use;
Further, we use developer Express Libraries to improve the application framework. If you want to compile this program, you have to install the following developer Express Libraries:
Expressnavbar Library Ver 1.x
Expressbars Library Ver 5.x
ExpressQuantumGrid Suite Ver 4.x
Expressprinting Library Ver 2.x This article summary classification:
n A better programming method - use Frames in your program main window (framework)
N Use Frames in the module inheritance
n Use the native VCL ActionS layer
N How long can you add a menu and toolbar to another place? This program framework can help you complete it quickly, because you need to do it simply modify the code in a place.
n Set an ExpressNavBar control at runtime
n Use ExpressBars and ExpressGrid in the application framework
n Use expressprinting to increase printing function
We started from a simple task, gradually develop our application framework, then, each step will join new features, which makes the code change as much as possible. The entire process is divided into 6 steps, you can download and build the code for each step.
table of Contents
First, why should I care about the application framework?
Second, create the simplest module independent application framework;
1. Application interface layout;
2. ActionS introduction;
Third, use the developer Express control to improve the application framework;
1. Increase the developer Express Navbar runtime;
2. Add the developer Express Bars running library;
3. Create a developer Express Grid module;
4. Add print function to the application framework;
First, why should I care about the application framework?
Borland introduces many practical classes to VCLs, which can help us quickly and easily establish a Windows from application. So why do we have to join a layer of processing code in our code? I have never thought about this problem for a long time.
About ten years ago, I joined a company that developed customer sales management systems. At that time, they used VB MS SQL development system, however, after I joined the company, Borland released Delphi1, this is the first true Object-oriented RAD tool. When the company decides to use Delphi to develop the next project, we are very exciting, we decide that all modules share an Application so that you can better share each other's code. At that time, most people, including the young people who have just graduated from college, and we have written a different code. Everything is progressing well until we put the code in a real environment test. Suddenly, we found that BUG changes are not as easy as they think. When correcting a module bug, a few new bugs will be generated in other modules. Develop new modules, we must spend more and more time to ensure compatible with the original modules, Many module code logic mixed on menu The / Toolbar is under our nightmare, no one can understand that a lot of "Case / Switch", load the system when the system is determined. In order to complete this project, we can only invest most of our own private time. When the project is completed (about a year of time), everyone is very tired. Most developers left the company to go to vacation, and they didn't come back. If it is because we didn't use the application framework, there were so many problems, it was lying. In fact, there is a lot of errors in the project implementation process. I estimate that almost all possible mistakes in our development process. We have not tried to improve our code. "Automatic Test" - What is it? At that time, we haven't heard it at all. And we didn't do any test at all. Everyone only cares about the part of the module he is responsible, the code sharing is simply a mess. I can continue to give some examples, but I think you have understood what I want to express.
Anyway, I know that there is no use of an application framework is one of the main issues we have encountered in our development, and this problem is actually very easy to solve. When the project is near the end, I spent some time to check the code of most modules. I am very surprised, most of the functional needs of each module is quite simple, even if they achieve their way.
In the next time I am participating in a similar project, I urge you to spend a few days to create a very simple application framework. This framework is allowed:
N can add a module by one line of code
N shared a log library between modules
n unified menu / toolbar usage
The time spent developing this layer of code will be saved back in the development of new features. Since then, most of the programs I develop will slightly modify the plus process. When I served in Developer Express, I browsed some of the code of other colleagues. I feel that some implementation methods are very good, and some of the code is not ideal. Some of the projects here made me think of the first time to write large projects. Sometimes some colleagues object to introduce inheritance to modules, menu / toolbar implementation. I think this article will bring them a lot of help. For those who have written their own application frames, and those that have been successfully used, they can also get some inspiration and useful code from this article. Our developer Express is very happy to hear this article makes your life more and relaxed (one of our work in developer express is helping developers)
Second, create the simplest module independent application framework
The first step, we will create an application framework that can build a module independently. The main form does not know what it will show, and the module is not clear that it will be displayed. This allows you to use this module in different parts of different programs. And you can use the main program in parallel with the test module. In this way, you and your team will feel that your program is very good, this is just a psychological feeling, but it is very effective. 1. Application interface layout
MS Outlook introduced the following classic SDI application layout: Menu and toolbar labeled with blue part, the navigation panel is represented by yellow, and the status bar is represented by green, while the work area is gray.
Let us create an application based on this layout, which will include two modules: module 1 and module 2.
In this program, we will use a standard menu, a Panel control that stops on the left contains a ListBox control (space for empty navigation area). In order to create a work area (gray part), we will add a panel control, set its Dock property to all space. Finally, we placed a splitter control between the navigation and workspaces. To simplify this job, we will not use the task bar in the program.
Now, our goal is to create an application framework for a separate functional module. Developers can increase or remove each module in one line of code.
All modules based on our framework will inherit (direct or indirect) from TFRMCUSTOMMODULE. This class of TFRMCUSTomModule is a TFRAME class that inherits from Delphi. The main form will just know the TFRMCUSTOMMODULE class, and it does not understand its subclass.
In the current step, we will not place too much function to the CustomModule class, you will see just adding an OONSTROY event. We will need it when we register the module later.
UNIT CUSTOMMODULE;
Interface
Uses
Windows, Messages, Sysutils, Variants, Classes, Graphics, Controls, Forms, Dialogs
Type
TFRMCUSTOMMODULE = Class (tframe)
Private
Fondestroy: TNOTIFYEVENT;
public
DESTRUCTOR DESTROY; OVERRIDE;
Property OnDestroy: TNotifyEvent Read Fondestroy Write Fondestroy;
END;
TfRMCUSTOMMODULECLASS = Class of TFRMCUSTOMMODULE
IMPLEMENTATION
{$ R * .dfm}
{Tfrmcustommodule}
Destructor tfrmcustommodule.destroy;
Begin
If Assigned (ONDESTROY) THEN
ONDESTROY (Self);
inherited;
END;
end
Creating all modules when the program is started. It is a bad habit. So, we need to create a unit of registration module. Named Modules.Pas contains two classes: TModuleInfo, TModuleInFomanager.
The TModuleInfo class contains the names of the module and the class related property information in the module. We will use the module's name to represent it contains features. When we need to display that module, we will create an instance of the corresponding module / frame class.
The TModuleInfomanager class contains a list of registration modules in our programs, which you can register a new module through its RegisterModule method. The ShowModule method will display the module to a specified Windows control. Count and Items properties Let's check all registered modules. You can use global function moduleinfomanager to access TModuleInFomanAger. Below is a declaration section of the modules.pas unit. You can download the STEP1 source program to refer to its implementation section.
UNIT MODULES;
Interface
Uses Classes, Controls, Custommodule
Type
/ / About the information contained in the module
TModuleInfo = Class
Private
FmoduleClass: TfRMCUSTOMMODULECLASS;
FModule: tfrmcustommodule;
FNAME: STRING;
Function getActive: boolean;
protected
// Create a module instance
PROCEDURE CREATEMODULE;
// Destroy the module instance
Procedure destroymodule;
public
Constructor Create (const aname: string; amoduleclass: tfrmcustommoduleclass);
DESTRUCTOR DESTROY; OVERRIDE;
// Hide module
Procedure hide;
// Display module into the specified control
Procedure Show (Aparent: TwinControl);
// If of course the module is activated, return True
Property Active: Boolean Read GetActive;
Property Module: TFRMCUSTOMMODULE Read FModule
Property Name: String Read Fname;
END;
// Management module class information
TMODULINFOMANAGER = Class
Private
FModuleList: TLIST;
FactiveModuleInfo: TModuleInfo;
Function getCount: Integer;
Function GetItem (INDEX: Integer): TMODULEINFO;
public
Constructor crete;
DESTRUCTOR DESTROY; OVERRIDE;
/ / Return the information of the corresponding module through the name of the module
Function getModuleInfobyname (const aname: string): TMODULEINFO;
// Register the module to the manager
Procedure registermodule (const aname: string; amoduleclass: tfrmcustommoduleclass);
// Display module into the specified control
Procedure showmodule (const aname: string; aparent: twinControl);
// Information of the current activation module
Property ActiveModuleInfo: TModuleInfo Read FactiveModuleInfo;
/ / Returns the number of current registered modules
Property Count: Integer Read GetCount;
Property Items [Index: Integer]: TModuleInfo Read GetItem; Default;
END;
/ / Return to the instance of the global TModuleInFomanAger object
Function ModuleInfomanager: TModuleInfomanager;
Now we need to set our menu system and navigation control, so that end users can manipulate our modules. Constructor Tfrmmain.create (Aowner: Tcomponent);
Begin
Inherited Create (Aowner);
/ / Install menu and navigation control
Registermodules;
// Display the first module at startup
IF moduleinfomanager.count> 0 THEN
ShowModule (ModuleInfomanager [0] .Name);
END;
Procedure tfrmmain.registermodules;
VAR
I: integer;
Amenuitem: TMenuItem
Begin
// Traverse all modules
For i: = 0 to moduleinfomanager.count - 1 do
Begin
// Add each item to the list box
Lblnavigation.Items.add (ModuleInfomanager [i] .name);
// Add a submenu item
Amenuitem: = TMenuItem.create (Self);
mview.add (amenuitem);
Amenuitem.caption: = moduleinfomanager [i] .name
// Use tag to identify each module
Amenuitem.tag: = i;
Amenuitem.onclick: = mviewclick;
END;
END;
Procedure tfrmmain.showmodule (const aname: string);
Begin
// When displaying the module, lock the refresh of the current main window
LockwindowUpdate (Handle);
Try
ModuleInfomanager.ShowModule (Aname, PnLworkingArea);
Finally
// Refresh the main window
LockwindowUpdate (0);
RedRawwindow (Handle, NIL, 0, RDW_ERASE or RDW_FRAME OR RDW_INVALIDATE OR RDW_ALLCHILDREN);
END;
END;
Procedure tfrmmain.lblnavigationclick (sender: TOBJECT);
Begin
IF lblnavigation.itemindex <0 kilion;
// Display module
ShowModule (lblnavigation.items [lblnavigation.itemindex]);
END;
Procedure tfrmmain.mviewclick (sender: TOBJECT);
Begin
// Display module
SHOWModule (lblnavigation.items [tMenuitem (sender) .tag]);
END;
The final step is to create a new module and register in our application framework.
Unit module1;
Interface
Uses
Windows, Messages, Sysutils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Custommodule, Stdctrls, Modules;
Type
// New module must inherit from Custom Module class
TfrmModule1 = Class (tfrmcustommodule)
Label1: TLABEL;
Private
public
END;
IMPLEMENTATION
{$ R * .dfm}
INITIALIZATION
// The class included in the registration module in the application framework
ModuleInfomanager.registerModule ('Module1', TfrmModule1); END.
Summary
As you can see, we only used a small number of code to complete our target of the application framework that created a module independent application. Of course, this application framework does not have a lot of features, and it must be expanded when used in actual environments. For example, in most programs I have written, the module must be provided with secure authentication. Depending on user permissions to determine each end user access or cannot access a specific module. In the registration part of the module, this feature can be introduced very simple. Here, the base module does not implement any function, but this is not common in actual. In most actual cases, you must introduce some specific functions directly in the base block. Here, you only have to remember that any feature you introduced in the base block will be automatically introduced into other sub-modules. So, you must plan the inheritance plan for the module in advance. For example: CustomModule -> CustomDBModule -> CustomGridModule ... etc, of course, each new module increases new features.
ActionS introduction
In the previous step, we implements a simple application framework that allows you to create a separate module. Now we will consider how to add functions into the module. The primary problem is that we have to add one to how to add a main form UI and business logic. New layer (Layer).
In other words, we hope that there is a menu and toolbar on the main form, and the Visible, Enable, and other attributes of the buttons on the menu on the project and toolbar must be able to reflect the current display module's business logic status, menu items, and toolbar projects. I don't know the details of the current display module, and the module doesn't know the existence of menus and toolbars. We expect to change the different UI controls on the interface without need to modify the code in the module. Such as: replace the standard menu to develop into developer expression expressbar, and vice versa. At the same time, we also hope to test the functionality of the module without creating a program main window.
Basically, we need to introduce a layer (Layer) code between the UI and business logic code, and we call it Actions layer (Layer).
VCLs have a native action, but here, we don't seem to use it directly because it breaks the independence of the modules in our application. However, we can extend VCL Actions to implement the features we need.
Through VCL ACTOINS, it is very simple to implement, first, we create a Datamoudle, drag a TActionManager control to it, write code under the TactionManager's Excute event, and add a few lines of code in DataMoudle. To add a new action, you only need to create a new action in the ActionManager control, then the action property of the UI control to be bound is set to the corresponding Action component. Note that the UI object you use must support Actions. Of course, all controls for the standard VCL and Developer Express support Action technology.
UNIT DMACTIONS;
Interface
Uses
SYSUTILS, CLASS, APPEVNTS, XPStyleActnctrls, Actnlist, Actnman
Type
TDMAPPACTIONS = Class (TDATAModule)
ActionManager: TACTIONMANAGER;
Action1: tact;
Action2: tact;
Action3: TACTION; Procedure ActionManageRexecute (Action: TBasicAction)
Var handled: boolean;
Private
Function startcount: integer;
Function GetAction (INDEX: Integer): TBASICACTION;
// If you do not specify an action event, the bound UI control will be a Disable status.
// The simplest solution is to give it a fake event
Procedure DofakevcLAction (Sender: TOBJECT);
Procedure fakevcLACTION;
public
Constructor Create (Aowner: Tcomponent); OVERRIDE;
/ / Return to Action Number
Property actioncount: integer read getActioncount;
Property Actions [INDEG]: TBasicAction Read getAction;
END;
VAR
Dmappactions: tdmappactions;
/ / Return to a global instance of the Actions class
Function appactions: tdmappactions;
IMPLEMENTATION
Uses forMs, Modules;
{$ R * .dfm}
/ / Return to a global instance of the Actions class
Function appactions: tdmappactions;
Begin
IF (Dmappactions = NIL) THEN
Dmappactions: = TDMAPPACTIONS.CREATE (APPLIC);
RESULT: = Dmappactions;
END;
{TDMAPPACTIONS}}
Constructor TDMAPPACTIONS.CREATE (Aowner: Tcomponent);
Begin
Inherited Create (Aowner);
FakevcLACTION;
END;
Procedure tdmappactions.fakevcLACTIONS;
VAR
I: integer;
Begin
For i: = 0 to actioncount - 1 do
Actions [i] .onExecute: = DOFAKEVCLACTION
END;
Procedure TDMAPPACTIONS.DOFAKEVCLACTION (Sender: TOBJECT);
Begin
// Do not perform any code
END;
Function TDMAPPACTIONS.GETACTIONCOUNT: Integer;
Begin
Result: = actionManager.Actioncount;
END;
Function TDMAPPACTIONS.GETACTION (INDEX: Integer): TBASICACTION;
Begin
Result: = actionManager.Actions [index];
END;
// Process the Execute event of the ActionManager control
Procedure TDMAPACTIONS.ACTIONMANAGEREXECUTE (ACTION: TBASICATION;
Var handled: boolean;
Begin
// Call the executeaction method of the current display module
IF (ModuleInfomanager.ActiveModuleInfo <> NIL) THEN
Handled: = ModuleInfomanager.ActiveModuleInfo.module.executection (Action);
END;
End.
Finally, we must add some functions to the CustomModule class. In order to register the function of supporting Actions, you need to call the registeraction method, the registeraction method overwrites the internal default Registeraction method. Calling the isActionSupported method will return whether it is supporting Action. We will overwrite overdateActionSstate methods to change the enabled and isdown properties of the action.
Below is a declaration section of the CustomModule.Pas unit:
UNIT CUSTOMMODULE;
Interface
Uses
Windows, Messages, Sysutils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, actnlist;
Type
TactionNotification = Procedure (Action: tbasicAction) of object;
TFRMCUSTOMMODULE = Class (tframe)
Private
// Support a list of Actions
FSUPPORTEDACTIONLIST: TLIST;
Fondestroy: TNOTIFYEVENT;
/ / Return to an event
TAACTION NOTIFICATION: TAACTIONNOTIFICATION
protected
// Register Supported Action
Procedure Registection; Anotification: TAATIONNOTIFICATION
// Inheriting the subclass of this class must rewrite an Action of this method for registration
PROCEDURE Registeractions; Virtual;
public
Constructor Create (Aowner: Tcomponent); OVERRIDE;
DESTRUCTOR DESTROY; OVERRIDE;
// Rewrive the ExecuteAction behavior of the parent class TFRMAE
Function Executection (Action: TBasicAction): Boolean; OVERRIDE;
// Return true if the current module supports ACTION
Function isActionSupported (ACTION: TBASICATION): Boolean
// When the module is activated, all support Actions will be displayed without supporting.
Procedure; virtual;
/ / Update Action Status
PROCEDURE UPDATACTIONSSSSTATE; Virtual
Property OnDestroy: TNotifyEvent Read Fondestroy Write Fondestroy;
END;
TfRMCUSTOMMODULECLASS = Class of TFRMCUSTOMMODULE
Below is a simple example of using Actions in TFRMCUSTOMMODULE subclasses:
Unit module1;
Interface
Uses
Windows, Messages, Sysutils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Custommodule, Stdctrls, Modules;
Type
// New model must be inherited from the custom module
TfrmModule1 = Class (tfrmcustommodule)
Label1: TLABEL;
Checkbox1: Tcheckbox;
Label2: TLABEL; edit1: tedit;
Procedure Checkbox1click (Sender: TOBJECT);
Private
// Treat Action1
Procedure doaction1 (ACTION: TBASICATION);
protected
// Register Support Action
Procedure Registections; Override;
public
// Update the module status
Procedure UpdateActionState; Override;
END;
IMPLEMENTATION
Uses DMACTIONS;
{$ R * .dfm}
{Tfrmmodule1}
Procedure tfrmmodule1.registeraction;
Begin
Inherited registections;
// Register Action1 to support Actions list
Registeraction (appactions.Action1, doaction1);
END;
Procedure TfrmModule1.UpdateActionSstate;
Begin
Inherited UpdateActionState;
// If CheckBo1 is not selected, let Action1 are operable.
AppActions.Artion1.enabled: = not checkbox1.checked;
END;
// Display the number of times of action1 to be executed to EDIT1
Procedure TfrmModule1.doAction1 (Action: TBasicAction);
Begin
Edit1.Text: = INTTOSTR (STRTOINT (Edit1.Text) 1);
END;
Procedure tfrmmodule1.checkbox1click (sender: TOBJECT);
Begin
UpdateActionState;
END;
INITIALIZATION
/ / Register the corresponding class in our application framework
ModuleInfomanager.registermodule ('Module1', TfrmModule1, 0, 0);
End.
Summary
First, use the developer Express control to improve the application framework
Increase the developer express navbar library
In the previous step, we created the first version of the application framework, it seems that it is running very well. However, if you give us a boss or customer presentation now, they will laugh at our rudiment. In a trend of application uses Listbox as navigation bar, it is not a best choice. The user wants to have a UI appearance with the current level of technology. Developer Provides the ExpressNavBar controls with more than ten different displays that will allow your app to have a new fashionable appearance.
Navbar is very easy to use, you can help you simplify control design work during the design period, but unfortunately, the main module does not know the details of other modules we have to introduce the system (and we hope to simply increase / remove a line of code. Control module), so we must abandon the design habits of using the IDE drag control, switch to code to complete everything.
First, we have to introduce new features to the application framework. Navbar controls are distinguished by categories. So, we must introduce categories in our module registration class. In addition, in order to completely improve the appearance of the application, we also want to add a picture in the menu items and groups represented by the Navbar control. So we must also introduce image properties in the registration class.
Below is the change we must introduce in modules.pas unit: increase the TcategoryInfo class
// Information included in the category
TcategoryInfo = Class
Private
FNAME: STRING;
FimageIndex: Integer;
Function GetIndex: Integer;
public
CONSTRUCTOR CREGEN
Property Index: Integer Read GetIndex;
Property ImageIndex: Integer Read FimageIndex;
Property Name: String Read Fname;
END;
Add Category and ImageIndex properties for the moduleinfo class. Doing so we can save the information of the module belonging to Category, and save the NavBar control to display the index (INDEX) of the image, below is a local change of the TModuleInfo class:
// Information included in the module
TModuleInfo = Class
Private
FCATEGORY: TCATEGORYINFO;
FimageIndex: Integer;
public
Constructor Create (const aname: String)
AmoduleClass: TFRMCUSTOMMODULECLASS;
Acategory: TcategoryInfo;
AIMAGEINDEX: INTEGER = -1);
...
Property Category: TcategoryInfo Read Fcategory;
Property ImageIndex: Integer Read FimageIndex;
END;
Increase categorycount, categories properties, and addcategory, getCategorybyName methods to the TModuleInFomanAger class so that we can add categories to our framework and find them when you need it. Next we will use this feature.
// Manage module information
TMODULINFOMANAGER = Class
Private
...
FcategoryList: TLIST;
Function getcategorycount: integer;
Function Getcategory (INDEX: Integer): TcategoryInfo;
public
// Add a new category
Procedure addcategory (const aname: string; imageindex: integer);
Return CategoryInfo object by name
Function getcategorybyName (const name: string): tcategoryInfo;
// Register the module to the manager
Procedure registermodule (const aname: string; tfrmcustommoduleclass) ;. AMODULECLASS: TFRMCUSTOMMODULECLAS
Actegory: tcategoryInfo = nil; AIMAGEINDEX: INTEGER = -1);
// Return to the number of categories
Property CategoryCount: Integer Read GetcategoryCount;
Property Categories [INDEX: Integer]: TcategoryInfo Read Getcategory;
END;
The next step is to create a Navbar Control Group (Items), and Link (Links) through the module in our application framework. We must change the registermodules method of the main form unit. Procedure tfrmmain.registermodules;
VAR
I: integer;
Anavbargroup: TDXNAVBARGROUP;
AnavbarItem: TDXNAVBARITEM;
Begin
// Traverse all categories
For i: = 0 to moduleInfomanager.categorycount - 1 DO
Begin
Increase Navbar group
Anavbargroup: = Navbar.Groups.Add;
/ / Set the title of the Navbar group
Anavbargroup.caption: = moduleinfomanager.categories [i] .name
// Set Navbar Group Eti Picture
Anavbargroup.largeImageIndex: = moduleinfomanager.categories [i] .imageIndex;
// Display pictures into the Navbar group
Anavbargroup.usesmallimages: = false;
END;
// Traverse all modules
For i: = 0 to moduleinfomanager.count - 1 do
Begin
// Add a new item to Navbar
Anavbaritem: = navbar.items.add;
/ / Set the title of the Navbar project
Anavbaritem.caption: = moduleinfomanager [i] .name
/ / Set the image index of the Navbar project
Anavbaritem.smallimageIndex: = moduleinfomanager [i] .imageindex;
Tag module using TAG
Anavbaritem.tag: = i;
// Increase the project to the corresponding Navbar group
Navbar.groups [moduleinfomanager [i] .category.index] .createlink (anavbaritem);
END;
END;
Below is the processing code for the NavBar control corresponding to the event:
Procedure tfrmmain.navbarlinkclick (sender: Tobject)
Alink: TDXNAVBARITEMLINK
Begin
// Display the specified module
ShowModule (ModuleInfomanager.Items [alink.Item.tag] .name);
END;
The final step is to register Categories to the application framework. Can do this, for example, registration in the initialzation section of the main window:
INITIALIZATION
ModuleInfomanager.Addcategory ('category 1', 0);
Summary
After using the developer Express Navbar control as a navigation bar, our program looks more beautiful than before.
1. Increase the developer Express Bars library
Now uses other things to replace the vintage standard menu and the toolbar. Because we define the ActionS layer, this is not a big problem. Regardless of which controls we choose, we only need to modify the form. Below I will show how to transplant the Expressbars control using developer Express.
VCL Actions technology is used in the current framework, so you must check if new controls support VCL ActionS technology before you switch to other menus. Expressbars supports VCL Actions.
Drag and drop a TDXBarManager control to the main window, use TDXBarconverter to replace the original standard main menu to Expressbars's main menu. The module uses the TDXBarlistItem class to communicate with each other. We need to modify the RegisterModules method.
Procedure tfrmmain.registermodules;
VAR
I: integer;
Begin
// Traverse all modules
For i: = 0 to moduleinfomanager.count - 1 do
Begin
/ / Add a project to the BAR list
BarlistItem.Items.Add (ModuleInfomanager [i] .name)
END;
END;
Below is the code for the BarlistItemClick event:
Procedure tfrmmain.barlistItemClick (Sender: TOBJECT);
Begin
// Display code
ShowModule (ModuleInfomanager.Items [BarlistItem.ItemIndex]);
END;
The final step is to create a toolbar and the button above based on our needs. Place the button on the toolbar and bind to the appropriate VCL Actions in the design environment. The process is like this.
Summary
As you can see, work is very simple, we just modify the code in the main form module to complete the needs. How do you transplanted a menu and toolbar to other style controls in your own program? I guess it is definitely a painful process.
1. Create a developer Express Grid module
By using XTranavBar and XTrabars to replace standard control methods, we greatly improve our application's appearance. Now it's time we consider improve the framework according to the module content. In your app, your "final" module does not necessarily inherit from CustomerModule. Most procedures generally have several lists that contain many objects and records. Usually we use the grid to represent these records, and this part may be an important part of the program. You must write the processing code for the GRID, for example: display or hide the corresponding GRID column based on custom forms, and so on. Of course, if you want to write such a code for each module that contains GRID, then it is too interesting. We will create a CUSTOMGRIDMODULE module. It will contain developer expression expressquantumgrid. Just like Grid Action, we will introduce an export Action. Although we have added support from the base block to export Action, these Actions are disabled in the default state. Therefore, the actually inherited GRID module must overwrite these methods to re-enable them to be available.
In order to introduce the GRID module, we need to make some modifications in the main form, the Action data module, and create a new module: CustomGridModule will inherit from CustomModule.
We must modify the main form and the Action data module to increase the Actions, ExpressBars project, and link Action to the corresponding ExpressBars project, just do before. The process is almost exactly the same.
A more interesting task is to add support for CustomModule to export Actions. Exporting Actions is visible to all modules, but the default is disabled, in order to make them available, inherited modules must override two methods: SupportExportTypes and DoExport. Below is the code that exports Action supported in CustomModule:
TexPortType = (Ethtml, ETXML, ETXLS, ETTEXT);
TEXPORTTYPES = set of texporttype; tfrmcustommodule = Class (tframe)
protected
/ / In order to register Actions support, the subclass must overwrite this method
PROCEDURE Registeractions; Virtual;
// Do a corresponding export according to the type you need
Procedure doExport (AexPortType: TexPortType; const AfileName: string); virtual;
/ / Return the corresponding supported export type
Function supportedExporttypes: TexPortTypes; Virtual
END;
Export Actions in CustomGridModule is quite simple:
Procedure TFRMCUSTOMGRIDMODULE.DOEXPORT (AEXPORTTYPE: TEXPORTTYPE; Const AfileName: String);
Begin
Case aexporttype of
Ethtml: ExportGrid4Tohtml (AfileName, Grid);
ETXML: ExportGrid4Toxml (AfileName, Grid);
ETXLS: ExportGrid4ToExcel (AfileName, Grid);
Ettext: ExportGrid4Totext (AfileName, Grid);
END;
END;
Function TFRMCUSTOMGRIDMODULE.SUPPORTEDEXPORTTYPES: TEXPORTTTYPES;
Begin
Result: = [Ethtml, etXml, etXls, etc];
END;
To implement Grid Actions, you will use the TCXGridOperationHelper class, you can find it in the cxgriduihelper.pas file published by the product. It implements standard grid operations for different views.
Constructor TfRMCUSTOMGRIDModule.create (Aowner: Tcomponent);
Begin
Inherited Create (Aowner);
FGridOperationHelper: = tcxgridopertopleHelper.create (Self);
FGridOperationHelper.grid: = Grid;
FGridOperationHelper.onUpdateOperations: = DOGRIDUPDATEOPERATIONS;
FGridOperationHelper.OncustomizationFormVisibleChanged: = DOGRIDUPDATEOPERATIONS;
END;
Procedure tfrmcustomgridmodule.registerActions;
Begin
Inherited registections;
Registeraction (appactions.actionGridgrouping, doactiongridgrouping);
...
Registeraction (appactions.actiongridcolumnscustomization, doactiongridcolumnscustomization);
END;
Procedure tfrmcustomgridmodule.updateActionsstate;
Begin
Inherited UpdateActionState;
AppActions.actionGridGrouping.Enabled: = FGridOperationHelper.IsOperationEnabled [GROP_SHOWGROUPINGPANEL]; AppActions.actionGridGrouping.Checked: = FGridOperationHelper.IsOperationShowing [GROP_SHOWGROUPINGPANEL];
AppAactions.ActionGridColumnscustomization.enabled: = fgridopertyHelper.IsoperationNabled [grop_showcolumncustomizing];
AppActions.ActionGridColumnscustomization.checked: = fgridopertyHelper.IsoPerationShowing [grop_showcolumncustomizing];
END;
Function TFRMCUSTOMGRIDMODULE.FOCUSEDVIEW: TCXCUSTOMGRIDVIEW;
Begin
Result: = Grid.focusedView;
END;
Procedure TfRMCUSTOMGRIDMODULE.DOACTIONGRIDGROUPING (ACTION: TBASICATION);
Begin
FGridOperationHelper.doshowGroupingPanel (not fgridopertyHelper.isgroupingPanelshowing);
END;
Procedure TFRMCUSTOMGRIDMODULE.DOACTIONGRIDCOLUMNSCUSTOMIZATION (Action: tbasicArth);
Begin
FGridOperationHelper.doshowColumncustomizing (not fgridopertyHelper.iscolumnscustomizingshowing);
END;
Procedure TFRMCUSTOMGRIDMODULE.DOGRIDUPDATEOPERATIONS (Sender: TOBJECT);
Begin
UpdateActionState;
END;
Summary
In this step, we created a base list module in the application framework.
It contains additional modules inherited from the parent mesh module. You must install the developer Express's ExpressNavBar control, ExpressBars, and ExpressQuantumGrid support libraries in your development environment, and run this program normally.
1. Use expressprinting to increase printing
The last one is to add to our application framework. We write exported Actions, we introduce and implement printed Actions to the base module.
After adding printing supports Actions to CustomModule, we have three additional virtual protection methods: Hasprinting, Doprint, and DopReview.
These methods will be overwritten in CustomGridModule to increase the function of printing ExpressQuantumGrid. Drag a TDXComponentprinter control from the ExpressPrinting panel to CustomgridModule, then create a report link for Grid in the module. With this control, print support for adding CustomGridModule is a fairly simple task.
// Returns true if the module supports printing
Function tfrmcustomgridmodule.haasprinting: boolean;
Begin
RESULT: = TRUE;
END;
Procedure tfrmcustomgridmodule.doprint; begin
PrinterLinkGrid.Print (false, nil);
END;
Procedure tfrmcustomgridmodule.dopreview;
Begin
PrinterLinkGrid.preview;
END;
Summary
In this step, we introduced print support to the application framework and implemented in the base class of the GRID module. You must install the developer Express ExpressnavBar control, Expresprinting, ExpressBars, and ExpressQuantumGrid support libraries in your working environment, can compile and run this demo (Demo).