views:

280

answers:

2

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...

+1  A: 

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;
Remy Lebeau - TeamB
Also, have a look at this question: http://stackoverflow.com/questions/2116013/howto-set-event-handlers-with-arbitrary-type-with-rtti-in-delphi-2010
Remy Lebeau - TeamB
I've seen that question. ;) But it doesn't help with this issue, unfortunately.
Mason Wheeler
"basically just an advanced wrapper around the older TypInfo RTTI"That is a bit of an overstatement. There were significant changes to the compiler to generate a lot more information for these "advanced wrappers" to operate on. Yes, the new data is an extension of the existing RTTI, but it is a rather significant extension.
Allen Bauer
True. I was merely pointing out that the internal logic of the new RTTI classes use the TypInfo RTTI structures internally for much of there data.
Remy Lebeau - TeamB
+4  A: 

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.
Barry Kelly
Aha! OK, that makes sense. Thanks!
Mason Wheeler