Reputation: 5166
I'm still working with my Local Modal Dialogs (LMD). See this question for more information. It works fine now for simple cases, but in sometimes there is a result in the dialog that I want to notify the caller. As the call is asynchronous with Show() I cannot simply get the result after the call.
So my question is how can I return one or several values from method TLMD_Dialog.btnOkClick to method TModule.myEvent?
I have 3 units involved in this: (Note that TLMD_Dialog inherits from 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;
Upvotes: 0
Views: 796
Reputation: 145
What I do occasionally, is add an event to the base class (in your case form), like:
//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;
Now you could let the constructor instantiate a TEventData or descendant bij calling GiveSomeDataClass.Create; Your descendant only has to override GiveSomeDataClass and return the descendant of TEventData it wants to use. Now you have a single event, declared in the ancestor, returning a sender and a data object and the latter may vary in types of data it contains. The handler can now use if (AEventData is TEventSpecialData) and act accordingly. Before calling DoOnSomething you can set the values of FSomeEventData, even in a DoOnSomeThing override in a descendant, whatever. ;-)
Alternative: You can make the event-data a public property and use Sender from a regular event to step to this property from the handler.
Upvotes: 1
Reputation: 32344
It's quite simple really, you don't use TNotifyEvent
but a custom event type with additional parameters for the information you want to return.
A simple example for getting a file name and another parameter (for example the name of a ZIP file and its compression level:
type
TReturnSaveZipFileDataEvent = procedure(Sender: TObject;
const AFileName: string; ACompressionLevel: Cardinal) of object;
Now instead of declaring the last parameter of your Execute()
method to be of type TNotifyEvent
you declare it to have your special event type.
Note that an IMHO much better way to implement such functionality would be to use interfaces. A custom interface is passed to the dialog, which can use it to do more than just call back with the results. For example the interface could have another method to check for the validity of the entered data, which the dialog would call in the OnCloseQuery
handler.
Upvotes: 2