MFC:子对话框行为

Posted

技术标签:

【中文标题】MFC:子对话框行为【英文标题】:MFC: child dialog behavior 【发布时间】:2009-07-29 05:41:28 【问题描述】:

我正在尝试将我的子对话框创建为主应用程序类的成员,如下所示:

class ParentWindow : public CWinApp

public:
    // Other MFC and user-implemented classes before this line
    MiscSettings    activeMiscSettings;

public:
    ParentWindow();
    ~ParentWindow();

// Overrides
    virtual BOOL InitInstance();

// Implementation
    afx_msg void OnAppAbout();
    afx_msg void OnMiscSettingsPrompt();
    DECLARE_MESSAGE_MAP()
;

我希望 MiscSettings 描述的对话框在程序启动时被实例化,在程序退出时被破坏,并根据用户是否选择特定菜单选项与用户单击“对话框的“确定”或“取消”按钮。但是,当我按如下方式实现 OnMiscSettingsPrompt() 处理函数时:

void ParentWindow::OnMiscSettingsPrompt()

    float temp;
    INT_PTR status = activeMiscSettings.DoModal();
    switch(status)
    
    case IDOK:
        temp = activeMiscSettings.GetSpeed();
        break;
    case IDCANCEL:
    default:
        break;
    

我无法访问 activeMiscSettings.GetSpeed() 方法 b/c 在 DoModal() 调用后句柄无效。我使用这种方法类似于显示子对话框的其他示例。但是,ParentWindow 类无法访问 activeMiscSettings 的内容。我知道我可以将处理程序放在 MiscSettings 类中,以便将编辑控件的 OK 按钮处理程序和其他用户控件设置中的内容正确传输到应用程序其余部分的适当类内容。在这一点上,我不确定将子弹出对话框上的设置传输到应用程序的其余部分的最干净的方法是什么。

我试图实现的另一个规范是杂项。当用户第一次选择菜单选项时,设置弹出对话框显示预配置的设置。更改一些设置并按下确定后,如果用户再次打开设置窗口,我希望当前设置显示在用户控件中,而不是显示之前在第一个实例中看到的预配置设置。这是一个容易实现的目标吗?

提前感谢 cmets。

【问题讨论】:

【参考方案1】:

你可以用一个无模式的对话框来实现你想要的,虽然它有点奇怪: - 在ParentWindow.OnCreate 中调用对话框Create。将 ParentWindow 作为父级传递 - 需要以不可见的方式创建对话框,IIRC 您需要为此覆盖 CMyDialog::PreCreateWindow - 要打开对话框,请使用 dlg.ShowWindow(SW_SHOW)parent.EnableWindow(false) - 关闭,使用dlg.ShowWindow(SW_HIDE)parent.EnableWindow(true)

但是,我建议不要这样做。

它甚至没有尝试将视图与控制器分开,但这可能是可以原谅的。 它将对话框绑定到一个父级,即不能从另一个窗口显示。 它不允许正确实现“取消” 最后一点,感觉很奇怪 - 这可能是代码气味或口味问题。

这是我认为“正常”的:

我所有的设置对话框都与一个设置类相关联,最终大致遵循以下界面:

class CSettings

   double speed;
   EDirection direction;
   bool hasSpeedStripes;

   bool IsValid(CString & diagnostics);
;

class CSettingsDialog

   CSettings m_currentSettings;
public:
   // that's the method to call!
   bool Edit(CWnd * parent, CSettings & settings)
   
      m_currentSettings = settings; // create copy for modification
      if (DoModal(parent) != IDOK)
        return false;
      settings = m_currentSettings;
      return true;
   

   OnInitDialog()
   
     // copy m_cuirrentSettings to user controls
   

   OnOK()
   
     // copy user controls to m_currentSettings
     CString diagnostics;
     if (!m_currentSettings.IsValid(diagnostics))
     
       MessageBox(diagnostics); // or rather, a non-modal error display
       return;
     
     EndDialog(IDOK);
   
;

副本是验证所必需的。我再次将设置类用于“currentSettings”,因为我不太赞成 MFC 的 DDX/UpdateData() 机制,并且经常手动进行传输。

但是,如果你遵循 MFC 的想法,你会

使用类向导将控件映射到数据成员,您可以在其中进行基本的范围验证 在 OnInitDialog 中,将设置复制到数据成员并调用 UpdateData(false) 在 OnOK 中,调用 UpdateData(true),并“返回”数据成员。

您甚至可以手动编辑 DoDataExchange 以将控件直接映射到 m_currentSettings 成员,但这并不总是有效。


应该对副本进行相互依赖验证,因为用户可能会更改值,看到新值不正确,然后按取消,期望保留原始值。示例:

if (speed < 17 && hasSpeedStripes)

   diagnsotics = "You are to slow to wear speed stripes!";
   return false;

验证应该与对话框类分开(尽管有人可能会争辩说生成诊断也不属于设置类,在这种情况下,您确实需要第三个“控制器”实体。虽然我通常会得到没有)

【讨论】:

【参考方案2】:

DoModal调用后句柄无效,可能是GetSpeed方法试图访问控制变量。它不是关闭对话框本身的无效句柄,而是包含速度值的子控件。

如果您将 Speed 值复制到 OnOK 中的非控制成员,则您的代码可以在模式对话框关闭后访问其值。

【讨论】:

【参考方案3】:

您应该放弃在启动时创建对话框并隐藏/显示它的想法。这仅适用于无模式对话框。您可以无模式地创建它,然后模拟模式行为,但这是一项乏味的工作。

我假设您想要显示/隐藏您的对话框,因为这样它会在您的应用程序中保留设置,并且您希望使用对话框来存储设置。您应该将带有设置的对话框与设置分开他们自己。创建一个只包含设置并且不是从 CDialog 派生的类。然后,让您的对话框从该类中填充其字段。将您的设置对象存储在您的 CWinApp 派生类中,并在每次要显示它时创建一个现在对话框(使用 .DoModal)。

(再次阅读其他答案,这可能是peterchen也在说的)

【讨论】:

【参考方案4】:

从它的声音来看,您需要一个无模式对话框,您可以通过从对话框派生来创建它。

您可以像上面一样在您的应用中声明它,并在闲暇时创建/销毁。如果您将其称为模态对话框,您将阻塞应用程序的其余部分,尽管当您描述您的要求时,听起来模态行为不是您想要的,例如

并根据是否显示/隐藏 用户选择特定的菜单选项

【讨论】:

无模式对话框在关闭后不需要访问对话框的属性。【参考方案5】:

我最终决定在父对话框类中创建一个包含要在子对话框中配置的设置的结构,在调用构造函数时传递指向该结构的指针,并让子对话框的 OK 按钮处理程序修改struct 的内容,因为它是一个指针。我认为这与我现在可以实现的一样干净。

【讨论】:

以上是关于MFC:子对话框行为的主要内容,如果未能解决你的问题,请参考以下文章

MFC响应快捷键

通过COM创建多个MFC对话框,奇怪的行为

MFC 调整大小对话框和子窗口绘制问题

在 MFC 中拖动子对话框时如何移动父对话框?

MFC 模式子对话框没有收到消息或覆盖

Windows MFC:将子对话框调整为选项卡控件显示区域