Save and restore event handlers

2019-06-28 05:28发布

问题:

My class contains dataset (TDataSet). Users of my class can assign event handlers for this dataset:

ds.FieldByName('ID').OnChange := @ID_OnChange;

Then I have to reopen dataset:

ds.Close;
ds.Open;

After this all event handlers are gone:

if Assigned(ds.FieldByName('ID').OnChange) //returns false

So, I need to save handlers and restore them after reopen. I use TStringList for it:

var
  EventHandlers: TStringList;
...
  //I do this for every event of every field
  if Assigned(ds.FieldByName('ID').OnChange) then
    EventHandlers.AddObject('ID', @ds.FieldByName('ID').OnChange);

The problem is how to restore handlers:

ds.FieldByName('ID').OnChange := TFieldNotifyEvent(ObjToInt(EventHandlers.Objects[0]));//Invalid typecast error

How can I assign stored address to event handler? Thanks.

回答1:

If you really want to save the events, you can use TMethod Record:

unit Unit6;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm6 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    MTD : TMethod;
  end;

var
  Form6: TForm6;

implementation

{$R *.dfm}

procedure TForm6.btn1Click(Sender: TObject);
begin
  ShowMessage('Hello World!');
end;

procedure TForm6.btn2Click(Sender: TObject);
begin
  ShowMessage('I am copy cat!');
  MTD := TMethod(btn1.OnClick);
  btn2.OnClick := TNotifyEvent(MTD);
end;

end.

The First Click on Btn2 will show "I am copy cat!" and The 2nd one will show Hello World.

Edit : Improve assign event to MTD(TMethod). More simple and allow events from other objects.



回答2:

I myself subclass my own dataset and has options to create all fields before opening the table and mapping the field events. In doing so, the field (and their events) will not disappear after close.

This can also be done in OnBeforeOpen Event.

If CreateFIeldBeforeOpen
  If FieldDefs.Count = 0 then
    FieldDefs.Update;
  for I := 0 to FieldDefs.Count - 1 do
    If not Assigned(FindField(FieldDefs[I].Name)) then
      FieldDefs[I].CreateField(Self, nil, FieldDefs[I].Name);