简体   繁体   English

MFC:子对话框行为

[英]MFC: child dialog behavior

I'm trying to make my child dialog box to be created as a member of the main application class as follows: 我试图将我的子对话框创建为主应用程序类的成员,如下所示:

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()
};

I would like to have the dialog box described by MiscSettings to be instantiated when the program starts up, destructed when the program exits, and show/hide according to whether the user select a particular menu option vs. the user clicking a "OK" or "Cancel" button of the dialog box. 我想让MiscSettings描述的对话框在程序启动时实例化,在程序退出时销毁,并根据用户选择特定菜单选项还是用户单击“确定”或显示/隐藏来显示/隐藏对话框的“取消”按钮。 However, when I implemented the OnMiscSettingsPrompt() handler function as follows: 但是,当我实现OnMiscSettingsPrompt()处理函数时,如下所示:

void ParentWindow::OnMiscSettingsPrompt()
{
    float temp;
    INT_PTR status = activeMiscSettings.DoModal();
    switch(status)
    {
    case IDOK:
        temp = activeMiscSettings.GetSpeed();
        break;
    case IDCANCEL:
    default:
        break;
    }
}

I cannot access activeMiscSettings.GetSpeed() method b/c the handle is invalid after the DoModal() call. 我无法访问activeMiscSettings.GetSpeed()方法b / c,在调用DoModal()之后,该句柄无效。 I used this method similar to other examples on showing child dialog boxes. 在显示子对话框时,我使用了与其他示例类似的方法。 However, the contents of activeMiscSettings were not accessible by ParentWindow class. 但是,activeMiscSettings的内容不能由ParentWindow类访问。 I know I can put handlers in MiscSettings class to transfer the contents properly in the OK button handler of the edit control and other user control settings to the appropriate class contents of the rest of the application. 我知道我可以将处理程序放在MiscSettings类中,以将内容正确地转移到编辑控件和其他用户控件设置的“确定”按钮处理程序中,并传递到应用程序其余部分的适当类内容。 At this point, I'm not sure what would be the cleanest way of transferring the settings on the child popup dialog to the rest of the application. 此时,我不确定将子弹出对话框上的设置转移到应用程序其余部分的最干净的方法是什么。

Another specification that I am trying to achieve is to have the misc. 我正在尝试实现的另一个规范是杂项。 settings pop-up dialog to show pre-configured settings when it first appears when the user selected the menu option for the first time. 设置弹出对话框,用于在用户首次选择菜单选项时首次显示预配置的设置。 After changing some settings and pressing ok, if the user opens the settings window again, I would like to have the current settings show up in the user controls rather than showing the preconfigured settings previously seen in the very first instance. 更改某些设置并按OK后,如果用户再次打开设置窗口,我希望在用户控件中显示当前设置,而不是显示以前在第一个实例中看到的预先配置的设置。 Is this an easily achievable goal? 这是一个容易实现的目标吗?

Thanks in advance for the comments. 预先感谢您的评论。

You can achieve what you want with a modeless dialog, though it's a bit strange: - call the dialogs Create in ParentWindow.OnCreate . 尽管有点奇怪,但是您可以通过无模式对话框实现所需的功能:-在ParentWindow.OnCreate调用对话框Create Pass ParentWindow as parent - the dialog needs to be created invisibly, IIRC you need to override CMyDialog::PreCreateWindow for that - to open the dialog, use dlg.ShowWindow(SW_SHOW) and parent.EnableWindow(false) - to close, use dlg.ShowWindow(SW_HIDE) and parent.EnableWindow(true) 将ParentWindow作为父级传递-需要隐式创建对话框,IIRC需要重写CMyDialog::PreCreateWindow打开对话框,请使用dlg.ShowWindow(SW_SHOW)parent.EnableWindow(false) -要关闭,请使用dlg.ShowWindow(SW_HIDE)parent.EnableWindow(true)

However, I'd advice against that. 但是,我建议不要这样做。

  • It doesn't even attempt to separate view from controller, but that might be forgivable. 它甚至没有尝试将视图与控制器分开,但这可能是可以原谅的。
  • It binds the dialog to one parent, ie it can't be shown from another window. 它将对话框绑定到一个父对话框,即无法从另一窗口显示它。
  • It doesn't allow to implement "Cancel" correctly 不允许正确实施“取消”
  • Last not least, it feels very strange - which might be a code smell or a matter of taste. 最后一点,它感觉很奇怪-可能是代码气味或味道问题。

Here's what I'd consider "normal": 这就是我认为的“正常”情况:

All my settings dialogs are associated with a Settings class, and end up following roughly the following interface: 我所有的设置对话框都与Settings类相关联,并最终遵循以下大致界面:

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);
   }
};

The copy is necessary for the validate. 该副本是验证所必需的。 I use the settings class for the "currentSettings" again, since I am not much in favor of MFC's DDX/UpdateData() mechanism, and often do the transfer manually. 我再次将设置类用于“ currentSettings”,因为我不太喜欢MFC的DDX / UpdateData()机制,并且经常手动进行传输。

However, if you follow MFC's ideas, you would 但是,如果您遵循MFC的想法,您将

  • use class wizard to map the controls to data members, where you can do basic range validation 使用类向导将控件映射到数据成员,您可以在其中进行基本范围验证
  • In OnInitDialog, copy the settings to the data members and call UpdateData(false) 在OnInitDialog中,将设置复制到数据成员并调用UpdateData(false)
  • In OnOK, call UpdateData(true), and "return" the data members. 在OnOK中,调用UpdateData(true),然后“返回”数据成员。

You could even manually edit the DoDataExchange to map the controls directly to m_currentSettings members, but that doesn't work always. 您甚至可以手动编辑DoDataExchange以将控件直接映射到m_currentSettings成员,但这并不总是有效。


A interdependency validation should be done on the copy since the user might change the values, see that the new values aren't ok, and then press cancel, expecting the original values to be preserved. 由于用户可能会更改值,请检查副本中的值是否不正确,然后再按“取消”键,并希望保留原始值,因此应该在副本上进行相互依赖性验证。 Example: 例:

if (speed < 17 && hasSpeedStripes)
{
   diagnsotics = "You are to slow to wear speed stripes!";
   return false;
}

the validation should be separate from the dialog class (though one could argue that generating the diagnostics don't belong into the settings class either, in that case you'd need a third "controller" entity indeed. Though I usually get by without) 验证应该与对话框类分开(尽管可能会争辩说,生成诊断也不属于设置类,在这种情况下,您确实需要第三个“控制器”实体。尽管我通常不这样做)

As for the handle being invalid after the DoModal call, probably the GetSpeed method is attempting to access a control variable. 至于在DoModal调用之后句柄无效,可能是GetSpeed方法正在尝试访问控制变量。 It's not an invalid handle on the closed dialog itself, but a child control that contains the speed value. 这不是关闭对话框本身的无效句柄,而是包含速度值的子控件。

If you copy the Speed value to a non-control member in OnOK, your code can access its value after the modal dialog is closed. 如果将“速度”值复制到OnOK中的非控制成员,则在关闭模式对话框后,您的代码即可访问其值。

You should abandon your idea of creating the dialog on startup and hiding/showing it. 您应该放弃在启动时创建对话框并隐藏/显示对话框的想法。 This will only work properly with a modeless dialog. 这仅适用于无模式对话框。 You can create it modelessly, then emulate modal behaviour, but this is tedious work. 您可以无模式地创建它,然后模拟模态行为,但这是繁琐的工作。

I'm assuming you want to show/hide your dialog because that way it keeps the settings across your application, and you want to use the dialog to store the settings in. You should separate the dialog with the settings from the settings themselves. 我假设您要显示/隐藏对话框,因为那样会在整个应用程序中保留设置,并且您想使用该对话框将设置存储在其中。您应该将对话框与设置本身分开。 Create a class that contains only the settings and is not derived from the CDialog. 创建一个仅包含设置而不从CDialog派生的类。 Then, make your dialog fill its fields from that class. 然后,使对话框填充该类的字段。 Store your settings object in your CWinApp derived class and create a now dialog (with .DoModal) every time you want to show it. 将设置对象存储在CWinApp派生类中,并在每次要显示它时创建一个Now对话框(使用.DoModal)。

(reading the other answers again this is probably what peterchen is also saying) (再次阅读其他答案,这可能是彼得兴所说的话)

From the sound of it you need a modeless dialog which you can create by deriving from dialog. 从它的声音来看,您需要一个无模式对话框,您可以通过从对话框派生来创建该对话框。

You can declare it in your app as you did above and create/destroy at your leisure. 您可以像上面一样在应用中声明它,并在闲暇时创建/销毁。 If you call it as a modal dialog you will be blocking the rest of application although when you describe your requirements it sounds like a modal behavior is not what you want eg 如果您将其称为模式对话框,则将阻止应用程序的其余部分,尽管在描述您的需求时听起来像不是您想要的模式行为,例如

and show/hide according to whether the user select a particular menu option 并根据用户是否选择特定菜单选项显示/隐藏

I have ended up deciding to create a struct containing the settings to be configured in the child dialog in the parent dialog class, passing in the pointer to the struct when calling a constructor, and have the child dialog's OK button handler modify the struct's contents as it is a pointer. 我最终决定创建一个结构,该结构包含要在父对话框类的子对话框中配置的设置,在调用构造函数时传递指向该结构的指针,并让子对话框的OK按钮处理程序将其内容修改为它是一个指针。 I think this is as clean as I can make the implementation for now. 我认为这很干净,因为我现在可以执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM