如何将一个或多个值返回到我的事件方法?

时间:2009-08-26 06:12:03

标签: delphi modal-dialog

我仍在使用本地模态对话框(LMD)。有关详细信息,请参阅this question。它现在适用于简单的情况,但有时在对话框中有一个结果我想通知调用者。由于调用与Show()异步,我不能简单地在调用后得到结果。

所以我的问题是如何从方法TLMD_Dialog.btnOkClick向方法TModule.myEvent返回一个或多个值?

我有3个单位参与其中: (注意TLMD_Dialog继承自TAttracsForm)

// Module.pas
procedure myEvent(Sender: TObject);

procedure TModule.btnCallDlg(Sender: TObject);
begin
  if Supports(lhaHandle.CurrentBoldObject, IObject, vMyObject) then
    TModalDialog.Execute(param1, param2, myEvent);
end;

procedure TModule.myEvent(Sender: TObject);
begin
  // Some code that react on result of the LMD dialog
end;

// AttracsForm.pas
type
  TAttracsForm = class(TForm)
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    fCallerForm: TForm;      // May be replaced by check PopupParent but a separate variable may be safer
    fOnAfterDestruction: TNotifyEvent;
  published
    procedure ShowLocalModal(aNotifyAfterClose: TNotifyEvent=nil);
  end;

procedure TAttracsForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Assigned(fCallerForm) then      // fCallerForm not assinged means that ShowLocalModal is not called. The old way to show dialog is used
  begin
    ClientMainForm.ViewManager.UnLockCurrentView(fCallerForm as TChildTemplate);

    if Assigned(OnAfterDestruction) then
      OnAfterDestruction(Self);

    Action := caFree;
  end;
end;

{ Call to make a dialog modal per module.
  Limitation is that the creator of the module must be a TChildtemplate.
  Several modal dialogs cannot be stacked with this method.}
procedure TAttracsForm.ShowLocalModal(aNotifyAfterClose: TNotifyEvent);
begin
  fCallerForm := ClientMainForm.ViewManager.LockCurrentView;    // Lock current module and return it
  PopupParent := fCallerForm;
  OnAfterDestruction := aNotifyAfterClose;
  Show;
end;

// LMD_Dialog.pas (inherit from TAttracsForm)
class procedure Execute(aParam: IBoldObject; aNotifyEvent: TNotifyEvent);
class procedure TLMD_Dialog.Execute(aParam: IBoldObject; aNotifyEvent: TNotifyEvent);
begin
  with Self.Create(nil) do
  begin
    // Do preparation
    ShowLocalModal(aNotifyEvent);
  end;
end;

procedure TLMD_Dialog.btnOkClick(Sender: TObject);
begin
  // Do something before close down
  // Set Result of the dialog
  Close;
end;

2 个答案:

答案 0 :(得分:2)

实际上非常简单,您不使用TNotifyEvent,而是使用自定义事件类型以及要返回的信息的附加参数。

获取文件名和其他参数的简单示例(例如ZIP文件的名称及其压缩级别:

type
  TReturnSaveZipFileDataEvent = procedure(Sender: TObject;
    const AFileName: string; ACompressionLevel: Cardinal) of object;

现在,您不是声明Execute()方法的最后一个参数属于TNotifyEvent类型,而是声明它具有您的特殊事件类型。

请注意,IMHO实现此类功能的更好方法是使用接口。自定义界面将传递给对话框,该对话框可以使用它来执行更多操作,而不仅仅是回调结果。例如,接口可以使用另一种方法来检查输入数据的有效性,对话框将在OnCloseQuery处理程序中调用该数据。

答案 1 :(得分:1)

我偶尔会做的是向基类添加一个事件(在你的案例表格中),如:

//untested code, no doubt there are errors in it, it's the idea I want to pass

type
  TEventData = class(TObject)
  public
    property SomeCommonFieldForAncestorAndDescendants: String;
  end;

  TSomeBaseEvent = procedure (ASender: TObject; AEventData: TEventData) of object;

  TSomeBaseClassOrForm = class(TForm)
  protected
    FSomeEventData: TEventData;

    function GiveSomeDataClass: TClass; virtual;
    procedure DoOnSomeThing; virtual;
  public
    constructor Create; override;

    property OnSomething: TSomeBaseEvent;
  end;

现在你可以让构造函数实例化一个调用GiveSomeDataClass.Create的TEventData或者后代bij;您的后代只需要覆盖GiveSomeDataClass并返回它想要使用的TEventData的后代。现在你有一个事件,在祖先声明,返回一个发送者和一个数据对象,后者可能在它包含的数据类型上有所不同。处理程序现在可以使用if(AEventData是TEventSpecialData)并相应地执行操作。在调用DoOnSomething之前,您可以设置FSomeEventData的值,即使在后代的DoOnSomeThing覆盖中也是如此。 ; - )

替代方法:您可以将事件数据设为公共属性,并使用常规事件中的Sender从处理程序步进此属性。