Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Event handler dynamisch setzen (https://www.delphipraxis.net/212513-event-handler-dynamisch-setzen.html)

Andre1 20. Feb 2023 22:06

Delphi-Version: 10.2 Tokyo

Event handler dynamisch setzen
 
Hallo,

ich möchte einen Event Handler dynamisch setzen. Der Event Handler hat dieselbe Signatur wie TNotifyEvent
in dem dynamischen Coding kann ich aber nicht das Symbol "TNotifyEvent" verwenden, deshalb habe ich ein
neues Symbol "TEventArgsRef" angelegt, das identisch ist.

Das Beispiel ist auf ein minimum reduziert:

Delphi-Quellcode:
program ex1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.classes, Rtti;

type
  TEventArgsRef = procedure(Sender: TObject) of object;

type
  TWrapper = class(TObject)
  private
  public
  published
    procedure Event(Sender: TObject);
  end;

procedure TWrapper.Event(Sender: TObject);
begin
  writeln('Event');
end;

var
  sl: TStringlist;
  Event: TEventArgsRef;
  value: TValue;
  context: TRttiContext;
  rttiType: TRttiType;
  prop: TRttiProperty;
begin
  sl := TStringlist.Create;
  Event := TWrapper.Create().Event;

  context := TRttiContext.Create;
  rttiType := (context.GetType(sl.ClassType));
  prop := rttiType.GetProperty(string('OnChange'));

  // Does not work
  //TValue.Make(@event, prop.PropertyType.handle, value);
  //prop.SetValue(sl, value);

  // Does also not work
  prop.SetValue(sl, TValue.From<TEventArgsRef>(Event));

  sl.Add('foo');
end.
Der Versuch mit TValue.Make führt zu einer Speicherausnahme. Auch TValue.From funktioniert nicht,
hier kommt "Ungültige Typenumwandlung", obwohl TEventArgsRef und TNotifyEvent diesselbe Signatur haben.
Gibt es einen Weg das Coding zum Laufen zu bringen, ohne im dynamischen Coding das Symbol TNotifyEvent
zu benutzen?

Viele Grüße
André

Uwe Raabe 20. Feb 2023 22:29

AW: Event handler dynamisch setzen
 
Warum so kompiliziert?
Delphi-Quellcode:
program ex1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.classes;

type
  TWrapper = class(TObject)
  private
  public
  published
    procedure Event(Sender: TObject);
  end;

procedure TWrapper.Event(Sender: TObject);
begin
  writeln('Event');
end;

var
  sl: TStringlist;
  wrapper: TWrapper;
begin
  wrapper := TWrapper.Create;
  try
    sl := TStringlist.Create;
    try
      sl.OnChange := wrapper.Event;
      sl.Add('foo');
    finally
      sl.Free;
    end;
  finally
    wrapper.Free;
  end;
end.

Andre1 21. Feb 2023 05:41

AW: Event handler dynamisch setzen
 
Danke für die Antwort. Das Beispiel von mir ist eine starke Vereinfachung von meinem Framework,
welches über eine generische Schnittstelle (Dll) Delphi Komponenten aufrufbar macht. Daher muss
jeder Zugriff komplett dynamisch über Extended Rtti erfolgen.

Gibt es hier eine dynamische Lösung?

Viele Grüße
André

mytbo 23. Feb 2023 17:47

AW: Event handler dynamisch setzen
 
Zitat:

Zitat von Andre1 (Beitrag 1518956)
Gibt es hier eine dynamische Lösung?

Ganz habe ich deine Beschreibung nicht verstanden, aber vielleicht suchst du das:
Delphi-Quellcode:
type
  TWrapper = class(TObject)
  public
    procedure DoChange1(pmObject: TObject);
    procedure DoChange2(pmObject: TObject);
  end;
 
procedure TWrapper.DoChange1(pmObject: TObject);
begin
  ShowMessage('Helau!');
end;

procedure TWrapper.DoChange2(pmObject: TObject);
begin
  ShowMessage('Helau! Helau!');
end;

var
  strList: TStringList;
  changeEvent: TNotifyEvent;
begin
  var wrapper: TWrapper := TWrapper.Create;
  try
    strList := TStringList.Create;
    try
      strList.OnChange := wrapper.DoChange1;
      strList.Add('Test1');

      var rttiType: TRttiType := TRttiContext.Create.GetType(TypeInfo(TStringList));
      if rttiType <> Nil then
      begin
        var rttiProp: TRttiProperty := rttiType.GetProperty('OnChange');
        if (rttiProp <> Nil)
          and (rttiProp.PropertyType.TypeKind = tkMethod) then
        begin
          changeEvent := wrapper.DoChange2;
          var eventValue: TValue;
          TValue.Make<TNotifyEvent>(changeEvent, eventValue);
          rttiProp.SetValue(strList, eventValue);
          strList.Add('Test2');
        end;
      end;
    finally
      strList.Free;
    end;
  finally
    wrapper.Free;
  end;
end;
Bis bald...
Thomas

mytbo 23. Feb 2023 18:35

AW: Event handler dynamisch setzen
 
Zitat:

Zitat von Andre1 (Beitrag 1518953)
Gibt es einen Weg das Coding zum Laufen zu bringen, ohne im dynamischen Coding das Symbol TNotifyEvent zu benutzen?

Verstehe nicht den Grund für diese Bedingung, aber bei deiner Umsetzung fliegt dir der TValue.Cast um die Ohren. Vielleicht hilft das:
Delphi-Quellcode:
var
  strList: TStringList;
  changeEvent: TEventArgsRef;
begin
  var wrapper: TWrapper := TWrapper.Create;
  try
    strList := TStringList.Create;
    try
      strList.OnChange := wrapper.DoChange1;
      strList.Add('Test1');

      var rttiType: TRttiType := TRttiContext.Create.GetType(TypeInfo(TStringList));
      if rttiType <> Nil then
      begin
        var rttiProp: TRttiProperty := rttiType.GetProperty('OnChange');
        if (rttiProp <> Nil)
          and (rttiProp.PropertyType.TypeKind = tkMethod) then
        begin
          changeEvent := wrapper.DoChange2;
          var eventValue: TValue := rttiProp.GetValue(strList);
          TValueData(eventValue).FAsMethod.Code := TMethod(changeEvent).Code;
          rttiProp.SetValue(strList, eventValue);
          strList.Add('Test2');
        end;
      end;
    finally
      strList.Free;
    end;
  finally
    wrapper.Free;
  end;
end;
Bis bald...
Thomas

Andre1 23. Feb 2023 19:28

AW: Event handler dynamisch setzen
 
Perfekt, vielen Dank, genau das hatte ich gesucht.

Viele Grüße
André


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:52 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz