`
宋科明
  • 浏览: 101612 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

数据感知控件之浮想联翩

阅读更多

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;

  end;

 

注意:将全局的Form变量删掉,重新在TAboutEdit类中声明一个新的私有变量。

覆盖Edit函数:

procedure TAboutEdit.Edit;

begin

  FFrmAbout := TfrmAbout.Create(Application);

  FFrmAbout.ShowModal;

  FFrmAbout.Free

end;

用来创建销毁窗体对象。

覆盖GetAttributes函数:

function TAboutEdit.GetAttributes: TPropertyAttributes;

begin

  Result := [paDialog,paReadOnly];

end;

 

告诉IDE将以何种工作方式进行显示,关于TPropertyAttributes可以参考帮助看看,这里不再赘述。我们这里是以只读对话框的方式

显示的,Object Inspector将在About属性旁边出现一个省略号按钮。当用户单击这个按钮,就调用Edit方法。

覆盖GetValue函数,是为了省略号按钮旁出现“(About)”的字样,并且只读。

 

function TAboutEdit.GetValue: string;

begin

  Result := (About)

end;

 

好了,工作基本完成,如果你还想进一步控制,还可以在研究TPropertyEditor类的代码。这里就不细说了。

也许你产生疑问了:这样能行了吗?控件怎么知道这个Package的属性方法,没关联呀。对了,要想把编辑器和控件关联起来还要2项

工作:

1、  控件增加一个string 类型的”About”属性

2、  在编辑器的单元里注册属性编辑器

第一步,很简单,大家早就熟悉增加一个属性的方法了:

private

   FAbout: string

Published

   Property About: string read FAbout write FAbout;

 

第二步,也只是在属性编辑器单元增加一个注册的全局过程函数:

procedure Register

beging

   //只有一句话:

   RegisterPropertyEditor(TypeInfo(string),TMyDataBaseEdit,About,TAboutEdit);

end;

 

现在,我来解释一下RegisterPropertyEditor函数:它用来注册一个属性编辑器,将控件中的一个属性和编辑器关联起来。

l         第一个参数:TypeInfo

(string),参数原型:PropertyType:PTypeInfo它是一个指针,指向要编辑的属性的运行期类型信息,我们通过TypeInfo()获取

l         第二个参数:TMyDataBaseEdit,参数原型:Component: Calss,用于指定这个属性编辑器所作用的组件类。

l         第三个参数是一个字符串,用于指定被作用属性的名称。

l         第四个参数用于指定属性编辑器的类型。

假如第二个参数设为nil,第三个参数设为空串,会如何呢?嘿嘿,所有string类型的属性全部变成About框了,哈哈。

大家注意,我们在这里只是简单的做了一个About对话框,目的是为了能够让大家快速清晰地了解属性编辑器的设计原理,我们也可

以把它做成一个复杂的有返回值的对话框,这样我们就可以真正用对话框来编辑控件的属性了。这就要看各位的实际需要了,但万便

不离其宗,呵呵。

有兴趣的话大家还可以研究一下Delphi的Source\Property Editors目录下的StringsEdit单元。(关于属性编辑器更详细的各种类型

、定义可以参考《Delphi5开发人员指南》第三部分“关于组件的开发”或仔细研究DesignEditors单元)

包的设计思想
在进行编辑器设计的时候我们新建了一个Package组件包。组件包,大家应该都比较了解它了,它类似于Dll,不过只是在Delphi和

CBuilder环境中通用。很好的利用此包,可以使我们的程序模块清晰,能最大限度的代码重用,使程序的体积尽量减小,而且可以在

这两种语言环境中互用。这里就不多说了,否则跑题太远,有兴趣的话大家可以跟我联系,共同探讨。

现在,我来说一下为什么要和控件的包分开,单独新建一个Package组件包。

首先,分块划分好管理,这不用多说了,一个运行期包,一个设计期包,使应用程序的体积变小,省得所有关于的单元全部链接到执

行程序中去(Delphi5及前版本),即使你只在程序中静态地进行链接,混合运行时和设计时的代码也将使你的代码膨胀。因为你的

设计时代码在运行时不会被执行,但是编译器并不知道,所以把它也一起链接进去了。

其实重要的一个原因是,Delphi6版本以来,VCl结构对Delphi5及前版本有所调整,Borland用DesignIntf替换了DsgnIntf,而且属性

编辑器也被放进DesignEditors、DesignMenus、DesignWindows和其它的一些设计文件里。特别是DesignEditors使用了其它的一个名

叫Proxies的IDE文件。而Proxies被编译进了DesignIDE.bpl文件中。DesignIDE.bpl已经不再是一个可以分发的文件,我们只能在开

发环境中使用,不用说,这时候编译应用程序时就出现“找不到文件”的问题了。这时,我们将运行期包和设计期包分开,将设计期

包加入designide.dcp文件,分别编译包文件,就能保证正确无误,编译通过!

所以我们遵守一些约定,将使我们的代码结构达到最优化,一般设置如下:

设计时包应该包括:

1、  所有的注册声明(最好放在一个单独的单元中);

2、  所有的属性编辑器;

3、  所有的组件编辑器;

4、  将需要DesignIDE支持的包。

注:组件编辑器是通过在组件上弹出快捷菜单,象Table等组件。它的制作比较类似,是从TComponentEditor下继承来的。这里不再

多讲了。

运行时包应该包括:

1、  组件本身。

2、  组件可能在运行时调用的窗体(属性编辑器用到的)。

Package部分由于版本不同而对不同的版本要进行不同的设计,Delphi 6.0的编辑器版本是VER140,Delphi 5.0的编辑器版本是

VER130,如果我们想适应不同的版本需要使用 {$IFDEF VER140}。。。{$ELSE}。。。{$ENDIF};在Uses域进行控制,这点大家稍加注

意。

 

 

 

 

分享到:
评论

相关推荐

    数据感知月历控件

    数据感知月历控件

    基于.NET的具有数据感知的树控件设计.rar

    本项目“基于.NET的具有数据感知的树控件设计”关注的就是如何在.NET环境中利用数据绑定技术来创建一个能够动态显示和操作数据的树形控件。 树形控件(TreeView)是一种常见的UI元素,它以层级结构展示数据,广泛...

    具有数据感知的树控件设计源码

    本话题聚焦于一种特定的UI组件——数据感知的树控件,这是在Visual Basic .NET (VB.NET)环境下用于展示层次结构数据的强大工具。源码来源于《Visual Basic .NET 2005数据库编程技术与实例》一书,这意味着我们将讨论...

    NET自动感知输入控件

    自动感知输入控件的核心机制是实时的输入事件监听和后台数据查询。当用户在输入框中输入文字时,控件会触发一个AJAX请求到服务器,服务器端接收到请求后,根据输入内容查询数据库或其他数据源,找到匹配的项,然后将...

    运用ASP.NET的数据模板控件开发数据报表

    在这个框架中,数据模板控件是其强大的功能之一,特别适用于创建动态数据驱动的Web报表。这些控件允许开发者以声明性的方式定义数据呈现的格式,无需深入到复杂的HTML和JavaScript代码中。本文将深入探讨如何利用ASP...

    【delphi】Android系统状态广播消息感知控件及演示程序源代码(老吴)

    【delphi】Android系统状态广播消息感知控件及演示程序源代码,详细介绍了Android系统消息广播感知原理。 控件感知功能包括: 1. 感知蓝颜状态变化 2. 感知WiFI状态变化 3. 感知电源状态变化 4. 感知网络状态变化 5....

    WinForm下的数据绑定分页控件

    1. 数据源为ArrayList类型的自动分页的可排序数据绑定控件; 2. 数据源为ArrayList类型的手动分页的可排序数据绑定控件; 3. 数据源为DataTable类型的自动分页的可排序数据绑定控件; 4. 数据源为DataTable类型的...

    C# Winform进度条 数据加载等待控件

    "C# Winform进度条 数据加载等待控件"就是这样一个组件,它在数据加载或执行耗时操作时提供视觉反馈,告诉用户程序仍在运行,并且预计何时会完成。 这个控件的设计原则是简单易用,开发者只需一行代码就能快速地将...

    通过FlexCelVCL读取xls电子表格的Delphi数据控件TDataSet

    从TDataSet派生的控件。它可以通过FlexCelVCL读取Excel的xls电子表文件。由于其从标准TDataSet派生,可用于任何TDataSet兼容控件,如TDBGrid,TDBGridEh等数据感知控件。带有demo.

    delphi增删改查小模块

    数据感知控件是Delphi中一种强大的特性,它允许开发者直接将数据库字段绑定到界面上的控件,如DBGrid(数据网格)、DBEdit(数据编辑框)等,实现数据的自动显示和更新,大大简化了UI与数据库的交互。 首先,我们来...

    一款vb.net柱状图/三维图表及数据曲线控件源码

    《VB.NET柱状图/三维图表及数据曲线控件源码详解》 在软件开发过程中,图表和曲线的可视化展示是至关重要的,它们能够帮助用户更直观地理解复杂的数据信息。VB.NET作为Microsoft .NET Framework的一部分,提供了...

    VC 实时数据显示曲线控件

    在IT领域,尤其是在Windows应用程序开发中,"VC 实时数据显示曲线控件"是一个常见的需求,特别是在数据监控、分析和可视化应用中。这个标题所指的可能是一个基于Microsoft Visual C++ (VC++) 开发的控件,它允许...

    财务数据输入控件(vb6.0)

    本文将详细解析一个自定义的“财务数据输入控件”及其核心功能,帮助开发者理解和运用此类控件提升软件的用户体验。 首先,我们要理解VB6.0中的控件是什么。控件是Visual Basic编程环境中预定义或自定义的图形用户...

    VB 读取Excel表格数据并在ListView控件中显示

    在VB(Visual Basic)编程中,常常需要处理与Excel数据的交互,比如读取Excel表格中的数据并将其显示在ListView控件中。ListView控件是一个非常实用的组件,可以用来展示列表形式的数据,包括多种视图模式,如图标、...

    标准控件及数据窗口的tooltip示例

    在PowerBuilder(PB)环境中,"标准控件"和"数据窗口"是两个核心元素,而"tooltip"则是一种非常实用的功能,可以显示在鼠标悬停时提供额外帮助文本的工具提示。本示例主要探讨如何在SLE(Single Line Edit,单行编辑...

    delphi自定义控件myDbComBobox

    本文将深入探讨“myDbComBobox”这个自定义控件,它是针对ComboBox控件的一个数据感知增强版本,特别适用于数据库相关的应用程序。 控件`MyDbComboBox`的核心特性包括: 1. **数据感知**:`MyDbComboBox`控件能够...

    C#自定义控件库

    自定义控件可以支持数据绑定,使得控件的值能直接反映数据源的变更,这在MVVM(Model-View-ViewModel)架构中尤为重要。 5. **控件状态管理**:理解控件的不同状态(如鼠标悬停、按下、禁用等)并正确处理这些状态...

    自定义控件 - WinForm下的数据绑定分页控件

    1. 数据源为ArrayList类型的自动分页的数据绑定控件; 2. 数据源为ArrayList类型的手动分页的数据绑定控件; 3. 数据源为DataTable类型的自动分页的数据绑定控件; 4. 数据源为DataTable类型的手动分页的数据绑定...

    【delphi】开发Android系统广播消息App自动感知控件演示程序(APK)

    该程序演示了Delphi开发的开发Android系统广播消息App自动感知控件的功能。 包括感知如下事件广播消息 1. 蓝牙 2. WIFI 3. 电源 4. 网络

    大量数据显示处理控件

    控件功能:具有datagrid的编辑功能和msflexgrid的显示功能,主要用于大量数据显示和编辑(包括添加,删除,修改功能) 代码编写:王新良 编写时间:2002-12-5 修改时间:2002-12-18

Global site tag (gtag.js) - Google Analytics