A Simple VCL SDI Application Framework
试图实现以下功能:
模块Module与表现UI分离的工程框架
只用一行代码来添加或者移除模块
模块功能的复用
统一控制菜单/工具栏
这是第一部分.
转载注明作者Daniel(写得还是挺辛苦的),抛砖引玉,写得不通的地方,请多多指教
需要文中源码,可以发送邮件至liyangyao@gmail.com
------------------------------------------------------------------------
从这里开始
------------------------------------------------------------------------
建立CustomModule.pas单元,所有的模块都继承自该单元的TfrmCustomModule
File -> New Frame,Frame的Name为TfrmCustomModule
我们为所有的模块添加一个Destroy事件
TfrmCustomModule = class(TFrame)
private
FOnDestroy: TNotifyEvent;
public
destructor Destroy; override;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
TfrmCustomModuleClass = class of TfrmCustomModule;
destructor TfrmCustomModule.Destroy;
begin
if Assigned(OnDestroy) then
OnDestroy(self);
inherited;
end;
------------------------------------------------------------------------
管理模块
------------------------------------------------------------------------
我们再创建一个模块管理单元modules.pas,其中包含了2个类:TModuleInfo模块信息类和TModuleManger模块管理类.
TModuleInfo类包含模块的名称,属性,可以在需要显示的时候,通过字符串的名称来创建模块.
TModuleManager类包含已注册的模块列表.其中,RegisterModule方法注册一个新模块,ShowModule来显示模块在一个特定的Windows控件上Count和Items可以用来访问当前拥有的所有的模块
在该单元中,还有一个ModuleInfoManager的全局函数来控制对一个TModuleManger对象实例的访问
//模块信息类
TModuleInfo = class
private
FModuleClass: TfrmCustomModuleClass;
FModule: TfrmCustomModule;
FName: string;
function GetActive: Boolean;
procedure DoModuleDestroy(Sender: TObject);
protected
//创建和销毁一个模块实例
procedure CreateModule;
procedure DestroyModule;
public
constructor Create(const AName: string; AModuleClass: TfrmCustomModuleClass);
destructor Destroy; override;
//显示和隐藏模块
procedure Hide;
procedure Show(AParent: TWinControl);
//模块是否处于激活状态
property Active: Boolean read GetActive;
property Module: TfrmCustomModule read FModule;
property Name: string read FName;
end;
//模块信息类的实现
constructor TModuleInfo.Create(const AName: string;
AModuleClass: TfrmCustomModuleClass);
begin
FName := AName;
FModuleClass := AModuleClass;
end;
procedure TModuleInfo.CreateModule;
begin
FModule := FModuleClass.Create(nil);
FModule.OnDestroy := DoModuleDestroy;
end;
destructor TModuleInfo.Destroy;
begin
DestroyModule;
inherited Destroy;
end;
procedure TModuleInfo.DestroyModule;
begin
if (FModule <> nil) and not (csDestroying in Module.ComponentState) then
begin
FModule.Free;
FModule := nil;
end;
end;
procedure TModuleInfo.DoModuleDestroy(Sender: TObject);
begin
FModule := nil;
end;
function TModuleInfo.GetActive: Boolean;
begin
Result := ModuleInfoManager.ActiveModuleInfo = Self;
end;
procedure TModuleInfo.Hide;
begin
FModule.Hide;
end;
procedure TModuleInfo.Show(AParent: TWinControl);
begin
if Module = nil then
CreateModule;
Module.Parent := AParent;
Module.Align := alClient;
Module.Show;
end;
//Module管理类的定义部分
TModuleInfoManager = class
private
FModuleList: TList;
FActiveModuleInfo: TModuleInfo;
function GetCount: Integer;
function GetItem(Index: Integer): TModuleInfo;
public
constructor Create;
destructor Destroy; override;
//通过名称,返回模块信息类实例
function GetModuleInfoByName(const AName: string): TModuleInfo;
//注册模块
procedure RegisterModule(const AName: string; AModuleClass: TfrmCustomModuleClass);
//显示模块
procedure ShowModule(const AName: string; AParent: TWinControl);
//当前激活的模块信息类实例
property ActiveModuleInfo: TModuleInfo read FActiveModuleInfo;
property Count: Integer read GetCount;
property Items[Index: Integer]: TModuleInfo read GetItem; default;
end;
//Module管理类的 实现部分
constructor TModuleInfoManager.Create;
begin
FModuleList := TList.Create;
end;
destructor TModuleInfoManager.Destroy;
begin
FModuleList.Free;
inherited;
end;
function TModuleInfoManager.GetCount: Integer;
begin
Result := FModuleList.Count;
end;
function TModuleInfoManager.GetItem(Index: Integer): TModuleInfo;
begin
Result := TModuleInfo(FModuleList[Index]);
end;
function TModuleInfoManager.GetModuleInfoByName(
const AName: string): TModuleInfo;
var
I: Integer;
begin
Result := nil;
for I := 0 to FModuleList.Count - 1 do
if (CompareText(Items[I].Name, AName) = 0) then
begin
Result := Items[I];
break;
end;
end;
procedure TModuleInfoManager.RegisterModule(const AName: string;
AModuleClass: TfrmCustomModuleClass);
var
AModuleInfo: TModuleInfo;
begin
//检查模块名称是否已存在
if GetModuleInfoByName(AName) <> nil then
raise Exception.CreateFmt('名称为"%s"的模块已经存在', [AName]);
AModuleInfo := TModuleInfo.Create(AName, AModuleClass);
FModuleList.Add(AModuleInfo)
end;
procedure TModuleInfoManager.ShowModule(const AName: string;
AParent: TWinControl);
var
AModuleInfo: TModuleInfo;
begin
AModuleInfo := GetModuleInfoByName(AName);
//检查模块,不存在则抛出异常
if AModuleInfo = nil then
raise Exception.CreateFmt('模块"%s"不存在', [AName]);
if AModuleInfo <> ActiveModuleInfo then
begin
if ActiveModuleInfo <> nil then
ActiveModuleInfo.Hide;
AModuleInfo.Show(AParent);
FActiveModuleInfo := AModuleInfo;
end;
end;
//定义全局函数 ModuleInfoManager
//返回 TModuleInfoManager 对象全局实例
function ModuleInfoManager: TModuleInfoManager;
//全局函数 ModuleInfoManager实现
var
FModuleInfoManager: TModuleInfoManager = nil;
function ModuleInfoManager: TModuleInfoManager;
begin
if FModuleInfoManager = nil then
FModuleInfoManager := TModuleInfoManager.Create;
Result := FModuleInfoManager;
end;
initialization
finalization
FreeAndNil(FModuleInfoManager);
end.
------------------------------------------------------------------------
随便搞2个测试模块
------------------------------------------------------------------------
在工程中创建2个用来示例的简单的模块并注册,TfrmModule1,TfrmModule2
File->New->Others,选中工程名的标签卡,继承自TFrmCustomModule
为了展示2个Module的不同,我们可以选点不同的背景颜色或者加些控件
并引用Modules单元,初使化单元时注册模块,比如
initialization
ModuleInfoManager.RegisterModule('Module1', TfrmModule1);
------------------------------------------------------------------------
终于开始写主窗体的程序了
------------------------------------------------------------------------
最后,万事具备,只欠东风了,在主窗体中来看看效果吧
添加一个ListBox控件来导航lblNavigation,添加一个菜单"查看"mView,预备通过其子菜可选择要查看的模块,再添加一个Panel来作为显示区域pnlWorkingArea
引用Modules单元,会使用到全局函数ModuleInfoManager
1.初使化时遍历所有的模块,添加到ListBox和Menu中,并使用Tag标识它们,Menu的查看子菜单需要赋值mViewClick事件
for I := 0 to ModuleInfoManager.Count - 1 do
begin
lblNavigation.Items.Add(ModuleInfoManager[I].Name);
AMenuItem := TMenuItem.Create(self);
mView.Add(AMenuItem);
AMenuItem.Caption := ModuleInfoManager[I].Name;
AMenuItem.Tag := I;
AMenuItem.OnClick := mViewClick;
end;
2.根据模块名称显示相应的模块
procedure TfrmMain.ShowModule(const AName: string);
begin
//显示模块
LockWindowUpdate(Handle);
try
ModuleInfoManager.ShowModule(AName, pnlWorkingArea);
finally
LockWindowUpdate(0);
RedrawWindow(Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_ALLCHILDREN);
end;
end;
3.单击ListBox导航时
if lblNavigation.ItemIndex < 0 then exit;
ShowModule(lblNavigation.Items[lblNavigation.ItemIndex]);
4.View子菜单的单击事件 mViewClick
ShowModule(lblNavigation.Items[TMenuItem(Sender).Tag]);
------------------------------------------------------------------------
总结
------------------------------------------------------------------------
按下F9运行,正如你看到的,我们通过不多的代码,实现了Module与UI的分离
当然这个Framework并没有解决所有的细节问题,比如模块访问权限控制,需要的话,我们可通过注册模块时加以限制.
在添加模块具体功能时,只要记住一点,在基本模块中添加的功能,会自动继承到其子模块中,比如,CustomModule -> CustomDBModule -> CustomGridModule,从而最大可能实现代码复用.
还有什么问题吗?
1.UI太丑
2.添加工具栏后,Module对工具栏的控制,能否做到与UI的分离
第一部分结束
Daniel 2008-8-10 周日凌晨
阅读(448) | 评论(0) | 转发(0) |