views:

469

answers:

3

My application have several modules, each in one tab on the mainform. When using a dialog it is convenient to call ShowModal because you know when the dialog is finished. But for the user it is not good as it lock the whole program until the dialog closes.

I want to have a locally modal dialog. So one module can open a dialog and it locks current module only. The user can still switch to another module and continue to work. If the user return to the first module the dialog is there waiting for close before the user can continue to work in that module.

I have to make some kind of framework for this that all dialogs in the application can use. I have a baseclass for all dialogs TAttracsForm and I think here is the place to add my Show() method.

This should lock access to all wincontrols only in the current module. It should simulate a call to ShowModal(). How can I achieve this ?

Regards

+2  A: 

You will have to do the following:

  1. Have an identity for each module
  2. Have a flag that is active or inactive for each module
  3. Have a flag that stores the modality of the attached dialog. If it is modal and the module is active, then call the show method in the appropriate eventhandler. Remember to update these values in the onshow and onclose events of each dialog.

You may have to fine tune this suggestion till you achieve the exact functionality that you require.

mm2010
I realize there is some properties that must be added at the dialog and module. Let's forget that for a moment.But the main issueis how can I locally disable all components in my module while the dialog is shown. It is a bad solution to just looping through them because they are so many.
Roland Bengtsson
Depending on the type of control that your controls are contained within, you should be able to just disable the parent. For example, if your components are all on a TTabSheet, disabling the TTabSheet prevents access to all of those controls.
Scott W
Just got another idea. Can I simply set the Enabled property to False of the module (that inherits from TForm) to lock all components on it?A quick test seems promising :)
Roland Bengtsson
Aha, we got the same idea at the same time :)
Roland Bengtsson
+2  A: 

Do you still want to implement this with "you know when the dialog is finished" metaphor? So like

DoSomethingBeforeDialog(); 
Form:=TFakeFormDialog.Create(Nil);
try
   Form.FakeShowModal();
finally
  Form.Free;
end;
DoSomethingAfterDialog();

if the answer is yes, you would try to implement this with threads, like Google Chrome do this with tab pages. But without threads only you can catch message processing with a code like this

function TFakeModalDlg.FakeShowModal(FormParent: TWinControl): boolean;
begin
  Parent:=FormParent;
  SetBounds((FormParent.Width - Width) div 2, (FormParent.Height - Height) div 2,
    Width, Height);
  Show;
  while NoButtonIsPressed() do
  begin
    Application.HandleMessage;
  end;
  Hide;
end;

And you even have code below...

Form:=TFakeModalDlg.Create(Nil);
try
  (Sender as TButton).Caption:='Going modal...';
  Form.FakeShowModal(TabSheet1);
  (Sender as TButton).Caption:='Returned from modal';
finally
  Form.Free;
end;

called multiply times from your tabs, but the problem is the these "dialogs" should be closed in "stack order" i.e. reverse to the order they were showed. I think it's impossible to force users to close forms in developers preference order :)

Maksee
+1  A: 

I have actually almost implemented local modal dialogs now. It is built around that when a TForms Enabled property is set To False the whole Form is locked from input. And my modules is just a descendant from TForm.

My ViewManager class that decide what modules is current add/close modules etc got 2 new methods. LockCurrentView and UnLOckCurrentView.

function TViewManager.LockCurrentView: TChildTemplate;
begin
  Result := CurrentView;
  Result.Enabled := False;
  Result.VMDeactivate;         // DeActivate menus and toolbas for this module
end;

procedure TViewManager.UnLockCurrentView(aCallerForm: TChildTemplate);
begin
  aCallerForm.VMActivate;           // Activate menus and toolbas for this module
  aCallerForm.Enabled := True;
end;

TAttracsForm is the baseclass of all dialogs. I Override FormClose and add a new method ShowLocalModal to call instead of ShowModal. I also have to add a TNotifyEvent OnAfterDestruction to be called when the dialog is closed.

procedure TAttracsForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Assigned(fCallerForm) then      
  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;

Some test with simple dialogs looks promising. So the module just have to call ShowLocalModal(myMethod) which have a TNotifyEvent as parameter. This method is called when the dialog is closed.

Roland Bengtsson