Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use TValue.AsType<TNotifyEvent> properly?

I'm trying to use RTTI to add an event handler to a control, that may already have an event handler set. The code looks something like this:

var
  prop: TRttiProperty;
  val: TValue;
begin
  prop := FContext.GetType(MyControl.ClassInfo).GetProperty('OnChange');
  val := prop.GetValue(MyControl);
  FOldOnChange := val.AsType<TNotifyEvent>;
  prop.SetValue(MyControl, TValue.From<TNotifyEvent>(self.MyOnChange));
end;

I want this so I can do this in MyOnChange:

begin
  if assigned(FOldOnChange) then
    FOldOnChange(Sender);
  //additional code here
end;

Unfortunately, the compiler doesn't seem to like the line FOldOnChange := val.AsType<TNotifyEvent>;. It says

E2010 Incompatible types: 'procedure, untyped pointer or untyped parameter' and 'TNotifyEvent'

Anyone know why that is or how to fix it? It looks right to me...

like image 855
Mason Wheeler Avatar asked Dec 29 '22 23:12

Mason Wheeler


2 Answers

FOldOnChange is of a method pointer type, while AsType<TNotifyEvent> is a method. The compiler thinks you're trying to assign the method to the method pointer. The solution is to append () to the method call to force it, and use the return value of the method as the value to assign to FOldOnChange.

Here's a complete example:

uses SysUtils, Rtti;

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

  TObj = class
  private
    FEv: TEv;
  public
    property Ev: TEv read FEv write FEv;
    class procedure Meth(Sender: TObject);
  end;

class procedure TObj.Meth(Sender: TObject);
begin
end;

procedure P;
var
  ctx: TRttiContext;
  t: TRttiType;
  p: TRttiProperty;
  v: TValue;
  o: TObj;
  e: TEv;
begin
  t := ctx.GetType(TObj);
  p := t.GetProperty('Ev');
  o := TObj.Create;
  try
    // Set value explicitly
    o.Ev := TObj.Meth;
    // Get value via RTTI
    v := p.GetValue(o);
    //e := v.AsType<TEv>; // doesn't work
    e := v.AsType<TEv>(); // works
  finally
    o.Free;
  end;
end;

begin
  try
    P;
  except
    on e: Exception do
      Writeln(e.Message);
  end;
end.
like image 52
Barry Kelly Avatar answered Dec 31 '22 13:12

Barry Kelly


The new RTTI introduced in 2010 is basically just an advanced wrapper around the older TypInfo RTTI (for now). In TypInfo, event handlers are represented by the TMethod record. Try this (untested):

var 
  prop: TRttiProperty;
  val: TValue;
  evt: TNotifyEvent;
begin
  prop := FContext.GetType(MyControl.ClassInfo).GetProperty('OnChange');
  val := prop.GetValue(MyControl);
  TMethod(FOldOnChange) := val.AsType<TMethod>;
  evt := Self.MyOnChange;
  prop.SetValue(MyControl, TValue.From<TMethod>(TMethod(evt));
end;
like image 40
Remy Lebeau Avatar answered Dec 31 '22 14:12

Remy Lebeau