Есть ли способ автоматически назначать обработчики событий динамически создаваемого компонента в Delphi 6?

У меня есть компонент дизайна и времени выполнения, который содержит большое количество обработчиков событий. Я сейчас назову это TNewComp. Я создаю экземпляр TNewComp в TForm и заполняю заглушки событий конкретным кодом через редактор свойств во время разработки и понимаю, что хотел бы иметь возможность создавать новые экземпляры TNewcomp, которые используют текущий набор кода обработчика событий.

Чтобы сделать это сейчас, я вызываю конструктор TNewComp, а затем «вручную» назначаю каждому из обработчиков событий нового экземпляра соответствующий код заглушки события, находящийся в форме, которая содержит экземпляр TNewComp, созданный во время разработки. Поэтому, если у меня есть экземпляр TNewComp, назначенный переменной FNewComp в форме с именем TNewForm, для каждого обработчика событий я бы сделал:

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

Это работает нормально, но это громоздко и хуже, если я добавляю новый обработчик событий в TNewComp, я должен не забыть обновить мою функцию "newTComp ()", чтобы сделать назначение обработчика событий. Промойте и повторите этот процесс для каждого уникального типа компонента, для которого я динамически создаю новые экземпляры.

Есть ли способ автоматизировать этот процесс, возможно, с помощью проверки свойств или какого-либо другого метода интроспекции Delphi 6?

- росшлер

delphi,properties,introspection,event-handling,delphi-6,

2

Ответов: 3


"за" 4 "за" принято

Я использовал следующий код. Будьте осторожны с владельцем Dest при создании, самый безопасный способ - передать 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;

Один из вариантов - сохранить «правильно настроенный компонент» в потоке, а затем загрузить этот поток в новый динамически создаваемый компонент, как если бы это делалось в среде Delphi IDE / runtime.

Другой вариант - использовать RTTI, блок TypInfo. Там у вас есть функция GetPropListwitch, которая позволит вам запрашивать доступные события (TypeKind tkMethod), а затем вы можете использовать GetMethodPropи SetMethodPropдля копирования обработчиков событий из одного компонента в другой.


Я подправил решение Макси к следующему:

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;

Чтобы функция возвращала новый компонент, а не передавала ссылку на существующий компонент (параметр Dest в версии Макси). Если кто-то может увидеть недостаток или проблему, возникшую в результате этого варианта, пожалуйста, прокомментируйте.

Дельфы, свойства, самоанализ, обработки событий, Делфи-6,
Похожие вопросы