【问题标题】:Fake modal dialog using Show?使用 Show 的假模态对话框?
【发布时间】:2009-08-22 18:03:34
【问题描述】:

我的应用程序有几个模块,每个模块都在主窗体上的一个选项卡中。 使用对话框时调用 ShowModal 很方便,因为您知道对话框何时结束。但对于用户来说,这并不好,因为它会锁定整个程序,直到对话框关闭。

我想要一个本地模式对话框。所以一个模块可以打开一个对话框,它只锁定当前模块。用户仍然可以切换到另一个模块并继续工作。如果用户返回到第一个模块,对话框会在那里等待关闭,然后用户才能继续在该模块中工作。

我必须为此创建某种框架,应用程序中的所有对话框都可以使用。 我有一个用于所有对话框 TAttracsForm 的基类,我认为这里是添加 Show() 方法的地方。

这应该只锁定对当前模块中所有 wincontrols 的访问。 它应该模拟对 ShowModal() 的调用。我怎样才能做到这一点?

问候

【问题讨论】:

    标签: delphi user-interface modal-dialog


    【解决方案1】:

    您必须执行以下操作:

    1. 每个模块都有一个标识
    2. 每个模块都有一个处于活动或非活动状态的标志
    3. 有一个标志来存储附加对话框的模态。如果它是模态的并且模块处于活动状态,则在适当的事件处理程序中调用 show 方法。请记住在每个对话框的 onshow 和 onclose 事件中更新这些值。

    您可能需要微调此建议,直到您获得所需的确切功能。

    【讨论】:

    • 我意识到必须在对话框和模块中添加一些属性。让我们暂时忘记这一点。但主要问题是如何在显示对话框时本地禁用模块中的所有组件。因为它们太多了,所以只循环遍历它们是一个糟糕的解决方案。
    • 根据您的控件所包含的控件类型,您应该能够禁用父控件。例如,如果您的组件都在 TTabSheet 上,则禁用 TTabSheet 会阻止对所有这些控件的访问。
    • 刚刚有了另一个想法。我可以简单地将模块(从 TForm 继承)的 Enabled 属性设置为 False 以锁定其上的所有组件吗?快速测试似乎很有希望:)
    • 啊哈,我们同时想到了同样的想法 :)
    【解决方案2】:

    你还想用“你知道对话何时结束”的比喻来实现这个吗? 很喜欢

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

    如果答案是肯定的,您可以尝试使用线程来实现这一点,就像 Google Chrome 使用标签页一样。但如果没有线程,您只能使用这样的代码来捕获消息处理

    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;
    

    你甚至还有下面的代码......

    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;
    

    从您的选项卡中调用了多次,但问题是这些“对话框”应该以“堆栈顺序”关闭,即与它们显示的顺序相反。我认为不可能强迫用户按照开发人员的偏好顺序关闭表单:)

    【讨论】:

      【解决方案3】:

      我现在几乎已经实现了本地模式对话框。 它是围绕当 TForms Enabled 属性设置为 False 时,整个表单被锁定而无法输入的。而且我的模块只是 TForm 的后代。

      决定哪些模块是当前添加/关闭模块等的 ViewManager 类有 2 个新方法。 LockCurrentView 和 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 是所有对话框的基类。我重写 FormClose 并添加一个新方法 ShowLocalModal 来调用而不是 ShowModal。我还必须添加一个 TNotifyEvent OnAfterDestruction,以便在对话框关闭时调用。

      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;
      

      一些带有简单对话框的测试看起来很有希望。所以模块只需要调用具有 TNotifyEvent 作为参数的 ShowLocalModal(myMethod) 。该方法在对话框关闭时调用。

      【讨论】:

        猜你喜欢
        • 2021-06-25
        • 1970-01-01
        • 2020-07-27
        • 1970-01-01
        • 1970-01-01
        • 2013-08-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多