【问题标题】:Is there a way to auto-assign a dynamically created component's event handlers in Delphi 6?有没有办法在 Delphi 6 中自动分配动态创建的组件的事件处理程序?
【发布时间】:2011-07-06 17:11:12
【问题描述】:

我有一个包含大量事件处理程序的设计和运行时组件。我暂时称它为 TNewComp。我在 TForm 上创建了一个 TNewComp 的实例,并在设计时通过属性编辑器用特定代码填充事件存根,并意识到我希望能够创建使用当前事件处理程序代码集的 TNewcomp 的新实例。

现在为此,我调用 TNewComp 的构造函数,然后“手动”为每个新实例的事件处理程序分配相应的事件存根代码,该存根代码驻留在包含在设计时创建的 TNewComp 实例的表单上。因此,如果我将一个 TNewComp 实例分配给一个名为 TNewForm 的表单上的一个名为 FNewComp 的变量,那么对于每个事件处理程序我都会这样做:

FNewComp.onSomething = TNewform.onSomething
(... repeat for each event handler belonging to TNewComp ...)

这很好用,但是很麻烦,而且更糟糕的是,如果我向 TNewComp 添加一个新的事件处理程序,我必须记住更新我的“newTComp()”函数来分配事件处理程序。为我动态创建新实例的每个唯一组件类型冲洗并重复此过程。

有没有办法自动化这个过程,也许使用属性检查或其他一些 Delphi 6 内省技术?

-- 罗施勒

【问题讨论】:

  • 我想我只需使用 Alt+F12 并在 .dfm 文件中快速复制/粘贴。我不确定这是否值得定制工具。
  • @David Hefferman - 我不确定我是否理解您的解决方案在运行时创建新实例时的工作原理,可能很多实例,当实例数量在运行时之前未知并且它们都是通过 TNewComp.Create 在运行时创建。
  • 我认为你的问题是在设计时。我不明白为什么现在做你做的事情这么难。您正在考虑的内容需要一些规则来关联事件和处理程序。你有什么考虑?基于名称?

标签: delphi properties introspection event-handling delphi-6


【解决方案1】:

我使用了以下代码。创建时要小心Dest owner,最安全的方法是传递Nil,以后自己释放组件。

implementation uses typinfo;

procedure CopyComponent(Source, Dest: TComponent);
var
  Stream: TMemoryStream;
  TypeData : PTypeData;
  PropList: PPropList;
  i, APropCount: integer;
begin
  Stream:=TMemoryStream.Create;
  try
    Stream.WriteComponent(Source);
    Stream.Position:=0;
    Stream.ReadComponent(Dest);
  finally
    Stream.Free;
  end;

  TypeData := GetTypeData(Source.ClassInfo);
  if (TypeData <> nil) then
  begin
    GetMem(PropList, SizeOf(PPropInfo)*TypeData^.PropCount);
    try
      APropCount:=GetPropList(Source.ClassInfo, [tkMethod], PropList);
      for i:=0 to APropCount-1 do
        SetMethodProp(Dest, PropList[i], GetMethodProp(Source, PropList[i]))
    finally
      FreeMem(PropList);
    end;
  end;
end;

【讨论】:

  • 谢谢,我会试试那个代码。我几乎从不将父级传递给动态创建的组件并选择自己管理它们的生命周期。否则,正如您所指出的,稍后几乎可以保证与 FastMM 进行会话来寻找双重释放或过早释放的对象。
【解决方案2】:

一种选择是将“正确设置的组件”保存到流中,然后将该流加载到新的、动态创建的组件中,就好像它是由 Delphi IDE/运行时完成的一样。

另一种选择是使用 RTTI,即 TypInfo 单元。在那里你有函数GetPropList 可以让你查询可用事件(TypeKind tkMethod),然后你可以使用GetMethodPropSetMethodProp 将事件处理程序从一个组件复制到另一个。

【讨论】:

    【解决方案3】:

    我将 Maksee 的解决方案调整为以下内容:

    function CopyComponent(Source: TComponent; Owner: TComponent = nil): TComponent;
    var
        Stream: TMemoryStream;
        TypeData : PTypeData;
        PropList: PPropList;
        i, APropCount: integer;
    begin
        if not Assigned(Source) then
            raise Exception.Create('(CopyComponent) The Source component is not assigned.');
    
        Result := TComponent.Create(Owner);
    
        Stream := TMemoryStream.Create;
    
        try
            Stream.WriteComponent(Source);
            Stream.Position := 0;
            Stream.ReadComponent(Result);
        finally
            Stream.Free;
        end; // try()
    
        // Get the type data for the Source component.
        TypeData := GetTypeData(Source.ClassInfo);
    
        if (TypeData <> nil) then
        begin
            // Get the property information for the source component.
            GetMem(PropList, SizeOf(PPropInfo) * TypeData^.PropCount);
    
            try
                // Get the properties count.
                APropCount := GetPropList(Source.ClassInfo, [tkMethod], PropList);
    
                // Assign the source property methods to the destination.
                for i := 0 to APropCount - 1 do
                    SetMethodProp(Result, PropList[i], GetMethodProp(Source, PropList[i]))
    
            finally
                // Free the property information object.
                FreeMem(PropList);
            end; // try()
        end; // if (TypeData <> nil) then
    end;
    

    这样函数就会返回一个新组件,而不是传入现有的组件引用(Maksee 版本中的 Dest 参数)。如果有人能看到此变体会导致的缺陷或问题,请发表评论。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-04
      • 1970-01-01
      • 2012-06-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多