|
Delphi制作数据感知控件之浮想联翩
王维康 编写
知识点
本文共有6个关于控件的知识点:
1、基本控件的制作;2、感知属性的添加;3、关联控件的销毁处理;
4、事件方法的赋值; 5、属性页的制作; 6、组件包设计思想
关于制作感知控件的文章有不少,但涉及的内容大都比较单一,读者只能依照文章的陈述按部就班地操作,无法很好的理解控件的制作机制。本文试图通过讲解一个控件的制作流程来着重阐述制作感知控件的思路和编程思想,让读者真正了解VCL控件的制作机制,而非仅仅达到了解此控件的制作方法为目的,希望能使读者在阅读以后达到“一叶落而知天下秋”的效果,哈,夸张了。
本文以制作一个类似于DBEdit的控件为例。首先使用组件向导从TEdit下继承下来,命名TMyDataBaseEdit,单元名为MyDataBaseEdi.pas,安装在一个新的包文件中,起名为MyDataEditStd60.Dpk。
包的命名没有规则,但是我们建议遵守包的命名约定:包的命名与包的版本相关,包的名称前面几个字符通常表示作者或公司名,也可以是控件的一个描述词,后面紧跟的Std表示运行期包,Dsgn表示设计期包,然后是版本号。关于包的设计方式我们将随后详细说明。
数据感知
准备就绪,我们开始编辑控件的功能,首先添加数据源感知属性 .DataSource,实现此属性只需Pubished域添加一句话:
property DataSource: TDataSource read GetDataSource write SetDataSource;按下shift +ctrl+ C组合键(complete class at cursor)完成属性的自动结构化功能,private域自动添加两个函数:
procedure SetDataSource(const Value: TDataSource);
function GetDataSource: TDataSource;
这样,安装组件到面板,可以看到该组件已经拥有了.DataSource属性,并实现感知TDataSource控件功能。怎么样,简单吧,呵呵。其实我们由此可以得到:控件属性的感知只不过是将它的一个属性声明为将要感知的控件类而已,如果感知Image控件,则:
property MyImage:TImage read GetImage write SetImage;
但是,假如我们要将一个控件作为这个控件的子属性,即将这两个控件的代码合并,则不能单单需要上述那一句话了:
首先,去掉添加控件的注册函数;
其次,将其从TComponent继承改为从TPersistent继承下来。大家可试试看。
事件赋值
不过,该控件并不能真正与数据库相连,只是一个样子而已,要真正实现功能,我们需要添加代码,这时候我们用到了一个重要的类TfieldDataLink。
它是控件内部的数据联接对象,从TDataLink继承下来,它的作用是与TDataSource组件相互通信,连接单个字段进行数据提取。我们将要处理这个对象的OnDataChange事件,这样,当字段或记录有所改变时就会得到通知,进行相应的数据处理。Ok,我们声明对象并创建:
private域声明 FDataLink:TFieldDataLink;构造函数中创建
constructor TMyDataBaseEdit.Create(AOwner: TComponent);
begin
inherited;
FDataLink := TFieldDataLink.Create;
FDataLink.OnDataChange := DataChange;
end;
DataChange是我们private域声明的一个过程:procedure DataChange(Sender: TObject);这里面实现了我们这个控件的实际功能,并和FDataLink.OnDataChange事件相连。创建成功之后我们实现GetDataSource、SetDataSource函数过程:
function TMyDataBaseEdit.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
procedure TMyDataBaseEdit.SetDataSource(const Value: TDataSource);
begin
if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
begin
FDataLink.DataSource := Value;
end;
if Value <> nil then
begin
Value.FreeNotification(Self);
end;
end;
关联控件的销毁
实现了上述代码,这样数据才真正的与控件关联起来,要个性化处理数据就要添加DataChange过程的代码了。
那么Value.FreeNotification(Self)代码是什么意思呢?请大家想一想:我们的组件需要和DataSource控件和DataSet控件相组合才能实现数据库数据的读写,那么当我们删除其中一个时,如果其他两个控件不知道,那么是不是会出现异常呢?答案是肯定的。
那么我们怎样才能做到通知其他组件呢?Yes, Value.FreeNotification(Self)就是做这个工作的!FreeNotification(self)将会把我们的组件置入其通知对象列表中,被撤消时,它会依次调用通知对象列表中所有对象的Notification方法,我们只需要在组件中重载它就行:
protected
{ Protected declarations }
procedure Notification(AComponent: TComponent;Operation: TOperation);override;
代码实现如下:
procedure TMyDataBaseEdit.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (FDataLink <> nil) and
(AComponent = DataSource) then DataSource := nil;
end;
这样,当关联的参考控件被删除时,控件就会得到消息,来处理事件,防止异常的发生,否则将会导致Delphi开发环境的不稳定甚至死机,切记!
同时,不要忘了重载Destroy函数:
destructor TMyDataBaseEdit.Destroy;
begin
inherited;
FDataLink.Free;
FDataLink := nil;
end;
确保使用完之后销毁FDataLink的实例,释放空间。无论是写组件还是写程序我们都要严格注意声明的对象,一定要在使用完之后释放!
添加DataField属性
我们使用此控件的功能目的是想连接好数据库组件后,通过选择字段名,将其字段值显示在Edit.Text中。那么我们还缺一个属性.DataField。
published域声明:
property DataField: string read GetFieldName write SetFieldName;
实现如下:
function TMyDataBaseEdit.GetFieldName: string;
begin
Result := FDataLink.FieldName;
end;
procedure TMyDataBaseEdit.SetFieldName(const Value: string);
begin
FDataLink.FieldName := Value;
end;
OK,这样数据库中的字段就将列在下拉框中了。
下面我们填写我们的数据处理过程:
procedure TMyDataBaseEdit.DataChange(Sender: TObject);
begin
if FDataLink.Field <> nil then
begin
Text := FDataLink.Field.Text;
end;
end;
哈,现在基本上可以运行起来了。现在,我们将Table,DataSource连接起来,来看看当前记录发生改变时,MyDataBaseEdit控件的Edit框中显示所选字段的字段值
添加属性页(About)
现在基本功能已经具备了,下面我们给它加上一个属性页,我们这里只是做一个About对话框演示,开发者可根据自己的实际功能要求自行定制。
我们实现的效果是要求:在属性编辑浏览器中出现一项About属性,右侧显示:(About)… 点击“…”按钮弹出一个对话框。
下面我们开始新建一个Package,按照命名规则起名:MyDataEditDsgn60.Dpk,然后新建一个Form,作为About对话框,我们可以根据需要自行设计界面。我们新建一个类TAboutEditor使其从TPropertyEditor下继承过来:
TAboutEdit = class(TPropertyEditor)
FFrmAbout: TfrmAbout;
function GetAttributes: TPropertyAttributes;override;
function GetValue: string;override;
procedure Edit;override;
[1] [2] 下一页 |