我仍在使用本地模态对话框(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;
答案 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从处理程序步进此属性。