MFC:子对话行为

时间:2009-07-29 05:41:28

标签: c++ visual-c++ mfc dialog

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

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按钮处理程序和其他用户控件设置中正确地将内容传输到应用程序其余部分的相应类内容。此时,我不确定将子弹出对话框中的设置转移到应用程序其余部分的最简洁方法。

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

提前感谢您的评论。

5 个答案:

答案 0 :(得分:2)

你可以用无模式对话框实现你想要的东西,虽然它有点奇怪:   - 在Create中调用对话框ParentWindow.OnCreate。将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;
}

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

你应该放弃在启动时创建对话框并隐藏/显示它的想法。这只能在无模式对话框中正常工作。你可以无模式地创建它,然后模拟模态行为,但这是一项繁琐的工作。

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

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

答案 3 :(得分:0)

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

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

  

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

答案 4 :(得分:0)

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