六、控件手拉手――控件关联的实现
控件的关联在Delphi中也是很常见的,我们可以设定一个控件的某个属性指向另一个控件。比如我们在窗体上放上Tedit,TpopupMenu两个控件,然后设定Tedit的PopupMenu属性为TpopupMenu控件,运行后在Tedit点击右键就会弹出刚才设定的那个TpopupMenu菜单,也就是说Tedit,TpopupMenu联手完成了任务。再比如TDBEdit控件的DataSource属性就可以指向一个TdataSource控件,这样就可以在TDBEdit控件中显示TdataSource输出的某个字段的值了。
下面我们将写一个简单的实现控件关联的控件。这个控件派生于Tedit,它可以与一个Tlabel控件关联,在控件的编辑框中输入文字时,与它关联的Tlabel控件的文字将随着它而变化。代码如下:
unit MyEdit;
interface
uses
SysUtils, Classes, Controls, StdCtrls;
type
TMyEdit = class(TEdit)
private
FLinkLabel: TLabel;
procedure FSetLinkLabel(AValue: TLabel);
protected
procedure Notification(AComponent: TComponent;Operation: TOperation);
override;
procedure Change;override;
public
published
property LinkLabel: TLabel read FLinkLabel write FSetLinkLabel;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Linco', [TMyEdit]);
end;
procedure TMyEdit.Change;
begin
inherited;
if LinkLabel <> nil then
LinkLabel.Caption := Text;
end;
procedure TMyEdit.FSetLinkLabel(AValue: TLabel);
begin
FLinkLabel := AValue;
if AValue <> nil then
FLinkLabel.FreeNotification(self);
end;
procedure TMyEdit.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = LinkLabel) then
LinkLabel := nil;
end;
end.
代码解释:
(1)、我们只要将控件的任意一个属性的类型设定为另外一个控件的类名称,那么我们就可以在控件的Object Inspector中将这个属性指向那个控件(或那个控件的派生控件)的一个实例。比如本例中我们增加了LinkLabel属性,它的类型为 Tlabel,所以我们就可以把LinkLabel属性指向一个标签控件。
(2)、请注意FsetLinkLabel中的这段代码:
if AValue <> nil then
FLinkLabel.FreeNotification(self);
如果我们将控件关联属性指向了一个控件,可是后来又将被指向的控件删除了,那么我们的控件关联属性是不会自动删除的,这样就会造成控件关联属性指向的控件不存在的现象。我们必须自动感知被关联控件的删除并重新设定控件关联属性为不指向任何控件,这样就避免了错误的发生。
FLinkLabel.FreeNotification(self);的作用就是这样的。它调用控件的FreeNotification方法(在Tcomponent中定义)向被指向的控件注册一个“消息”,当被指向控件被删除时,会向所有向他注册的控件发送一个它被删除的消息,此时向他注册的控件就会触发Notification方法,这样我们就可以自动感知被指向控件的状态了。这是设计模式中Observer(观察者)模式的典型应用。
既然向他注册的控件就会触发Notification方法,我们就覆盖父类的Notification方法,写出如下的代码:
if (Operation = opRemove) and (AComponent = LinkLabel) then
LinkLabel := nil;
这句话的意思是:如果控件被删除并且被删除的控件(因为我们的控件可能向多个控件注册了消息)是LinkLabel,那么我们就设定LinkLabel属性不指向任何控件。
(3)覆盖父类的Change调度方法。在此方法里为连接的LinkLabel的Caption赋值就达到我们的目的了。
思考题:
1、做一个Label控件,给它增加一个DataSource属性,该属性可以指向一个TdataSource类型的控件,它有一个GetRecordCount方法。当调用此方法时,就在Label控件中显示这个DataSource对应的数据集中的记录的条数。