概述
为了更简单地将问题放入文本中,我的控件的继承如下所示:
TCustomListBox
> TMyCustomListBox
> TMyListBox
TMyCustomListBox
的原因是通过添加一些我自己的新属性和方法来扩展TCustomListBox
,然后我会得到其他TMyListBox
组件,这些组件将派生自{{1} },但也可能有自己的属性和方法。
问题
我遇到的问题是无法从我在TMyCustomListBox
中介绍的TMyListBox
中触发该方法。
考虑到这一点,这里是TCustomListBox
的细分摘要,您可以在其中看到我添加的新方法类型( OnAddition ):
TMyCustomListBox
我像这样分配事件:
type
TOnAdditionEvent = procedure(Sender: TObject; Index: Integer; Value: string) of object;
TMyCustomListBox = class(TCustomListBox)
private
FOnAddition: TOnAdditionEvent;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnAddition: TOnAdditionEvent read FOnAddition write FOnAddition;
property OnClick;
property OnDblClick;
// ...
end;
上述内容来自:
if Assigned(FOnAddition) then
begin
FOnAddition(Self, SomeIndex, SomeValue);
end;
现在我们有procedure WndProc(var Message: TMessage); override;
:
TMyListBox
如果我在设计时使用type
TMyListBox = class(TMyCustomListBox)
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
的实例并使用新事件,那么在运行时测试时它会正常工作:
TMyListBox
如何在procedure Form1.MyListBox1Addition(Sender: TObject; Index: Integer; Value: string);
begin
ShowMessage('Added Item: [' + Value + '] whose index is: ' + IntToStr(Index));
end;
组件源代码中执行相同操作,我尝试了一些不同的操作,并且没有任何事情触发事件或我收到错误。
我尝试了什么
1
TMyListBox
2
与上述相同,仅添加type
TMyListBox = class(TMyCustomListBox)
protected
procedure Addition(Sender: TObject; Index: Integer; Value: string);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OnAddition := Addition; // shouldn't this call Addition procedure below?
end;
procedure TMyListBox.Addition(Sender: TObject; Index: Integer;
Value: string);
begin
ShowMessage('test'); // doesnt fire
end;
:
inherited
3
我尝试覆盖procedure TMyListBox.Addition(Sender: TObject; Index: Integer;
Value: string);
begin
inherited; // no difference
ShowMessage('test'); // doesnt fire
end;
方法声明:
Addition
在基类中找不到procedure Addition(Sender: TObject; Index: Integer; Value: string); override;
的错误。
4
我甚至尝试将声明更改为:
Addition
并从构造函数中删除事件处理程序,仍然不会触发。
我也尝试了其他一些东西但是在这一点上我遇到了一个死胡同,所以会欣赏正确方向的推动。我真的应该能够解决这个问题,但我认为我已经花了太长时间坚持这个问题并且我的大脑正在融化,所以我等待最有可能变成一个简单明显的答案:)
感谢。
答案 0 :(得分:6)
constructor TMyListBox.Create(AOwner: TComponent); begin inherited Create(AOwner); OnAddition := Addition; // shouldn't this call Addition procedure below? end;
这项任务应该可以正常工作。不,它不会调用 Addition()
,将Addition()
的地址分配给OnAddition
事件。触发事件后,它将调用 Addition()
。
然而,话虽如此,这是错误的设计。组件应从不为其自己的事件(尤其是published
事件)分配处理程序。正确的解决方案是让TMyCustomListBox
声明一个触发virtual
的{{1}}方法,然后在需要时调用该方法。 OnAddition
(或任何其他后代)可以TMyListBox
该方法,如果/想要触发用户的override
事件处理程序,则调用inherited
。额外的好处是,即使用户没有分配任何事件处理程序,这也允许后代对事件作出反应。
请改为尝试:
OnAddition
type
TOnAdditionEvent = procedure(Sender: TObject; Index: Integer; Value: string) of object;
TMyCustomListBox = class(TCustomListBox)
private
FOnAddition: TOnAdditionEvent;
protected
procedure DoAddition(Index: Integer; Value: string); virtual;
property OnAddition: TOnAdditionEvent read FOnAddition write FOnAddition;
// ...
end;
procedure TMyCustomListBox.DoAddition(Index: Integer; Value: string);
begin
if Assigned(OnAddition) then
OnAddition(Self, Index, Value);
end;
答案 1 :(得分:1)
您的主要问题是,在TMyCustomListbox中,您将OnAdition声明为TOnAdditionEvent的属性,但在TMyListBox中,您尝试将此声明为过程。
所以正确的方法是声明你的TMyListBox如下:
type
TMyListBox = class(TMyCustomListBox)
protected
procedure Addition(Sender: TObject; Index: Integer; Value: string);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnAddition; //This forwards the parent property definition to the child
end;
constructor TMyListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OnAddition := Addition; // shouldn't this call Addition procedure below?
end;
procedure TMyListBox.Addition(Sender: TObject; Index: Integer;
Value: string);
begin
ShowMessage('test'); // doesnt fire
end;
并在TMyListBox类调用之外触发事件:
MyListBox.OnAddition(Self, SimeIndex, SomeValue);
如果你是在课堂上这样做,你可以打电话:
Self.OnAddition(Self, SimeIndex, SomeValue);
注意:如果您想在子类中重用parent属性而不进行任何更改,请使用:
property MyParentProperyName;
您不需要声明属性类型,也不需要声明getter或setter方法,以便对属性的处理方式进行一些更改(例如,将完整的access属性从paret clas转换为仅在子类中读取) )。