开发者

Is there a way to auto-assign a dynamically created component's event handlers in Delphi 6?

开发者 https://www.devze.com 2023-03-18 06:08 出处:网络
I have a design and run-time component that contains a large number of event handlers. I\'ll call it TNewComp for now.I create an instance of TNewComp on a TForm and fill in the event stubs with speci

I have a design and run-time component that contains a large number of event handlers. I'll call it TNewComp for now. I create an instance of TNewComp on a TForm and fill in the event stubs with specific code via the property editor at design 开发者_JAVA百科time, and realize I would like to be able to create new instances of TNewcomp that use the current set of event handler code.

To do this now, I call TNewComp's constructor and then "manually" assign each of the new instance's event handlers the corresponding event stub code resident on the form that contains the TNewComp instance created at design time. So if I have an instance of TNewComp assigned to a variable named FNewComp on a form called TNewForm, for each event handler I would do:

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

This works fine, but it is cumbersome and worse, if I add a new event handler to TNewComp, I have to remember to update my "newTComp()" function to make the event handler assignment. Rinse and repeat this process for every unique component type that I create new instances of dynamically.

Is there way to automate this process, perhaps using property inspection or some other Delphi 6 introspection technique?

-- roschler


I used the following code. Be careful with the Dest owner when creating, the safest way is to pass Nil and free the component by yourself later.

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;


One option would be to save "the properly set up component" into stream and then load that strem into new, dynamically created component as if it is done by Delphi IDE/runtime.

Another option is to use RTTI, the TypInfo unit. There you have function GetPropList witch will enable you to query for available events (TypeKind tkMethod) and then you can use GetMethodProp and SetMethodProp to copy eventhandlers from one component to other.


I tweaked Maksee's solution to the following:

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;

So that a new component is returned by the function rather than passing in an existing component reference (the Dest parameter in Maksee's version). If anyone can see a flaw or problem that will result from this variant please comment.

0

精彩评论

暂无评论...
验证码 换一张
取 消