the time spent on coding this layer was paid back many times during further development. from that time, i have used a modification of that framework in most of my windows applications. while working at developer express, i have looked at the code of projects written by our customers. there have been some good approaches but some implementations were not good. there were some projects that reminded me of my first experiments in writing large applications. sometimes people were fighting with introducing inheritance in the modules, menu and toolbar systems etc. i feel that this article will definitely help them enormously. those who have already written their own application framework and use it successfully may well be able to borrow some ideas and code. we at developer express would be happy to know that this article will make your life a little easier (helping developers is the main reason we are working here at developer express).
create the simplest module-independent application framework
in this first step, we will create an application framework library that will allow the creation of independent modules. the main form, on which modules will be shown, will not know anything about the content of such modules. the modules themselves will not know where they are shown and located. it will allow you to use the module within different parts of your application(s) and develop and/or test modules separately from the main application code. you and your team will get the impression and feeling that your application is well written. this is a more psychological thing, but it is very effective.
application layout
here is the typical layout of an sdi application, first introduced in ms outlook.

the menu/toolbar system is marked in blue, the navigation panel in yellow, the status bar/panel in green and the working area in gray.
let's create an application using this layout. it will contain two modules: module 1 and module 2.
in this application, we will use a standard menu, a panel control docked to the left with containing a list box (to create the navigation area). to create the working area (in gray), we will add a panel that has its dock property set to fill the area. finally we will place a splitter control between the navigator and the working area. to simplify the task, we will not include a task bar into our application at this time.
the current task is to create an application framework that allows creation of independent modules with a developer being able to add/remove a single line of code for adding/removing each module
all modules within applications written based on our framework will be inherited (directly or indirectly) from tfrmcustommodule. this class is inherited from delphi's tframe class. the main form class will only know about the tfrmcustommodule class and it should not have a clue about its descendants.
in the current step, we will not put any functionality into the custommodule class. as you may see it just adds an ondestroy event. we will need it later in the module registration unit.
| [delphi] 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 |
since it is a very bad approach to create all modules on application start-up, we need to create a module registration unit. i named it modules.pas. there are two classes in it: tmoduleinfo, tmoduleinfomanager.
the tmoduleinfo class contains the module's name and module class properties. we will use the module name for identification purposes and the module class for creating an instance of the module/frame class when we need to show the module.
the tmoduleinfomanager class contains a list of registered modules in our application. you should use the registermodule method to register a new module. the showmodule method will show the module on a particular windows control. the count and items properties let us examine all registered modules.
you should use the moduleinfomanager global function to get access to the tmoduleinfomanager object.
here is the interface part of the modules.pas unit. please download the step1 application source to review its implementation section.
| [delphi] unit modules; interface uses classes, controls, custommodule; type // contains information about a module tmoduleinfo = class private fmoduleclass: tfrmcustommoduleclass; fmodule: tfrmcustommodule; fname: string; function getactive: boolean; protected // create the module instance procedure createmodule; // destroy the module instance procedure destroymodule; public constructor create(const aname: string; amoduleclass: tfrmcustommoduleclass); destructor destroy; override; //make the module invisible procedure hide; //show the module on a particular control procedure show(aparent: twincontrol); // return true if the module is active currently property active: boolean read getactive; property module: tfrmcustommodule read fmodule; property name: string read fname; end; //manage module info classes tmoduleinfomanager = class private fmodulelist: tlist; factivemoduleinfo: tmoduleinfo; function getcount: integer; function getitem(index: integer): tmoduleinfo; public constructor create; destructor destroy; override; // return the module info by its name function getmoduleinfobyname(const aname: string): tmoduleinfo; // register module module in the manager procedure registermodule(const aname: string; amoduleclass: tfrmcustommoduleclass); // show the module on the control procedure showmodule(const aname: string; aparent: twincontrol); // the module info of the currently active module property activemoduleinfo: tmoduleinfo read factivemoduleinfo; // return the number of registered modules property count: integer read getcount; property items[index: integer]: tmoduleinfo read getitem; default; end; // return the instance of the global tmoduleinfomanager object function moduleinfomanager: tmoduleinfomanager; |