MFC非模态对话框教程
简介
Modeless dialog boxes have often puzzled newbie programmers. Basically a modeless dialog box is one that allows us to interact with other windows even when the modeless dialog is still on screen. If you keep in mind a few nifty little tricks then programming modeless dialogs will be a piece of cake.
非模式对话框常常感到困惑的新手程序员。基本上是一个非模式对话框是一个使我们能够互动,与其他的Windows ,甚至当非模式对话框仍然是在屏幕上。如果您请记住,几个漂亮的小动作,然后编程非模式对话框,将非常简单。
创建非模式对话框
The straightforward way to create a modeless dialog is using Create(). Pass the name of the dialog's template resource and an optional CWnd* which points to the parent window. If you don't pass a parent window pointer the main application window will be used as the parent window. Create() will return true if the call was successful.
简单的方法创建一个非模式对话框是使用Create()
。通过名称的对话框中的模板资源和一个可选的CWnd*
这点,向父窗口。如果您不通过一个父窗口指针的主要应用程序窗口将被作为父窗口。 Create()
将返回true
如果调用是成功的。
Since Create() returns immediately unlike DoModal(), you must never declare your modeless dialog as a local variable with scope and lifetime only within the function where it is declared. Instead allocate the modeless dialog on the heap. If you don't do that, the modeless dialog will be destroyed the moment, you exit the function within which you declared it.
自Create()
立即传回,不像DoModal()
, you must never declare your modeless dialog as a local variable与范围及寿命只有功能函数的地方,这是申明。而不是分配非模式对话框就堆。如果你不这样做,非模式对话框会被销毁时刻,你退出功能函数,使你申明它。
An alternative solution is to declare your modeless dialog as a heap member object of your main frame window or your CWinApp derived class. An advantage with this method is that you actually have control over the modeless dialog, since you have a pointer to it.
另一种解决办法是要申明您的非模式对话框作为一个堆的成员对象你的主要框架窗口或您的CWinApp
派生类。一的优势,用此方法是,你实际上有控件权的非模式对话框,由于你有一个指针。
By the way, unlike modal dialogs, modeless dialogs need to have the WS_VISIBLE style set if you want them to be visible immediately after creation. Otherwise you'll have to explicitly call ShowWindow() with SW_SHOW. In fact I recommend that you do this, instead of going all over the place, changing default styles.
由的方式不同,非模式对话框,非模式对话框需要有WS_VISIBLE
风格设置如果你不想让他们看见后,立即进行创作。否则,你就会有明确要求ShowWindow()
与SW_SHOW
。事实上,我建议您这样做,而不是去所有的地方,改变默认的风格。
CModeLess *m_pmodeless = new CModeLess(this); m_pmodeless->Create(CModeLess::IDD); m_pmodeless->ShowWindow(SW_SHOW);
父问题
The usual practice is to make the parent window the main window of your application, which is typically the main frame window. Now one issue with this is that the modeless dialog will remain on top of this parent window. It allows you to interact with the main frame window, perhaps it contains a CView derived view. But it may be annoying and undesirable to have the modeless dialog remain on top. The solution here is to create the modeless dialog as a child of the desktop. Use GetDesktopWindow() to get a pointer to the Desktop and pass that as the parent window for the modeless dialog in your call to Create().
惯常的做法是使父窗口的主窗口对您的应用程序,这是典型的主框架窗口。现在的一个问题是与这是因为非模式对话框,将继续留在上方的这父窗口。它可以让您的互动与主框架窗口,或许它还包含一个CView
得出的视图。但它可能是恼人的和不可取的有非模式对话框留在顶端。该解决方案在这里是创建非模式对话框作为一个子的桌面上。使用GetDesktopWindow()
得到一个指针到桌面上,并通过表示,由于父窗口为非模式对话框,在您的调用Create()
。
m_pmodeless->Create(CModeLess::IDD,GetDesktopWindow());
销毁非模式对话框
Since we have allocated memory on the heap, we must delete it when the modeless dialog is destroyed, otherwise we'll soon run into big trouble with memory leaking left, right and center. When the dialog is destroyed the last message our handler class receives is the WM_NCDESTROY message. The OnNcDestroy function is invoked and this in turns calls the virtual function PostNcDestroy. That's exactly where we can delete our modeless dialog. First call the base class function so that it does it's own cleaning up.
既然我们已分配内存就堆,我们必须将它删除时,非模式对话框是摧毁,否则我们很快会遇到很大的麻烦与内存泄漏,左,右和中心。当对话框是摧毁了最后一则留言,我们收到处理类是WM_NCDESTROY
消息。该OnNcDestroy
功能函数是引用,这在原来调用虚拟函数PostNcDestroy
。这正是我们可以delete
我们的非模式对话框。 First Call调查的基类的功能函数,以便它是否自己的清理。
void CModeLess::PostNcDestroy() { CDialog::PostNcDestroy(); delete this; }
问题与成员对象
If the modeless dialog is a member object of the parent window class, we have a slight issue here. The member variable still holds a pointer reference, but the memory it references has been deleted. There are workarounds to this problem. One method is to post an user defined message to the parent window and handle it in the parent class, by setting the modeless dialog member variable to NULL. Another method is to use GetParent() to get the parent window, if any and then cast it to the actual parent class. Now we have access to the parent class's member variable that holds the pointer to the modeless dialog. Set that to NULL. The latter method is portrayed later where I discuss how to restrict a modeless dialog to one instance. The former method is shown below :-
如果非模式对话框是一个成员对象的父窗口的类,我们有一个轻微的问题在这里。成员变量仍持有指针引用,但它引用的记忆已被删除。有替代这个问题。一种方法是后一用户定义的消息给父窗口和处理它在父类,通过设置非模式对话框成员变量,以NULL
。另一种方法是使用GetParent()
得到父窗口,如有的话,然后在它的实际父类。现在我们已进入到父类的成员变量保存指针向非模式对话框。设置为NULL
。后者的方法是描绘后来我讨论如何限制非模式对话框,以一个实例。前者的方法是,如下所示: -
void CModeLess::PostNcDestroy() { CDialog::PostNcDestroy(); GetParent()->PostMessage(WM_MODELESS_CLOSED,0,0); delete this; }
LRESULT CMainFrame::OnMyMethod(WPARAM wParam, LPARAM lParam) { m_pmodeless = NULL; return 0; }
问题OnOK ( )和OnCancel ( )
In modal dialog boxes, everybody including the queen's cook, has the OK/Cancel buttons. In my opinion, and presumably in many other more learned people's opinions, you'd do good to avoid having OK and Cancel on a modeless dialog. But if for some unavoidable reason, you badly want to have them on your modeless dialog, then you'll need to over-ride both functions.
在模态对话框,每个人都包括女王的厨师,有OK/cancel按钮。我认为,想必在许多其他更了解市民的意见,您要做好,以避免OK和取消对一非模式对话框。但如果因为某种不可避免的原因,您最希望能有他们对您的非模式对话框,那么您将需要超过预约两种功能函数。
Here is my modeless version of the OnCancel() function. As you can see I have simply called DestroyWindow() and I haven't bothered to call the base class. In fact don't call the base class at all. The base class function will call EndDialog() which is associated with DoModal().
这里是我的非模式版本的OnCancel()
功能函数。正如你可以看到,我只是调用的DestroyWindow()
和我没有困扰,调用基类。事实上, don't call the base class at all 。基类函数将拜会EndDialog()
这与DoModal()
。
void CModeLess::OnCancel()
{
DestroyWindow();
}
Okay, now for my modeless version of OnOK(). I have called DestroyWindow() as in the OnCancel(), but there is some extra code too as you can see. I am calling UpdateData, because that's what OnOK() does in a modal dialog. If the DDV macro validations are successful then UpdateData(true) returns true and we destroy the window, else the DDV message box is automatically shown to the user and we refuse to destroy the dialog. Thus we are simulating the behavior of a modal dialog's OK button here.
好吧,现在我的非模式版本的OnOK()
。我有调用的DestroyWindow()
作为,在OnCancel()
,但也有一些额外的代码,也正如你所看到的。我调用UpdateData
,因为这是什么OnOK()
是否在一个模态对话框。如果DDV宏是成功的验证,然后UpdateData(true)
返回我们摧毁的窗口,否则DDV消息框是自动显示给用户和我们拒绝销毁对话框。因此,我们是模拟的行为,一模态对话框的OK按钮在这里。
void CModeLess::OnOK() { if(UpdateData(true)) { DestroyWindow(); } }
传递回数据
In modal dialogs, we can still access the data variables when DoModal() returns because the dialog object has not been destroyed yet, only the underlying dialog window has been destroyed. This is also possible with modeless dialogs using a nifty trick as shown below.
在模态对话框,我们仍然可以使用数据变量时, DoModal()
返回,因为对话框的对象并没有被摧毁,但只有基这个的对话框窗口已被摧毁。这是也有可能与非模式对话框,用漂亮的技巧,如下所示。
void CModeLess::OnOK() { if(UpdateData(true)) { ((CMainFrame*)m_parent)->m_x=m_sss; DestroyWindow(); } }
Here I have assigned the value of the dialog data variable m_sss to the parent class's member variable, m_x. Here, m_parent is a pointer to the parent window. If you are wondering where I got this m_parent from, scroll up and see how I have constructed my modeless dialog object. I'll repeat that single line to refresh your memory, and also to help you avoid scrolling, thus saving you some energy.
在这里,我已经指派的值对话框的数据变量m_sss
到父类的成员变量, m_x
。在这里, m_parent
是一个指针,向父窗口。此处您想知道我得到这个m_parent
从,滚动起来,看看如何我已构造的,我非模式对话框对象。我会重复,单行刷新您的内存,也帮助您避免滚动,从而节省您的一些能源。
CModeLess *m_pmodeless = new CModeLess(this);
As you can see, I have passed this to the constructor. In my case, this is a pointer to my CFrameWnd derived class which App Wizard has named as CMainFrame for me. Now take a look at my CModeLess class's constructor.
你可以看到,我已通过this
向构造。在我而言,这是一个指针,以我的CFrameWnd
派生类,其中应用程序向导已命名为CMainFrame
我。现在看看我的CModeLess
类的构造。
CModeLess::CModeLess(CWnd* pParent /*=NULL*/) : CDialog(CModeLess::IDD, pParent) { //{{AFX_DATA_INIT(CModeLess) m_sss = 0; //}}AFX_DATA_INIT m_parent=pParent; //This is where I point m_parent to my main frame window }
It all slowly makes sense, eh?
这一切,慢慢是有道理的,嗯?
跟踪非模式对话框计数
Let's say you want to have only one instance of the modeless dialog alive at one time. In that case, each time the user initiates some action that results in the bringing up of the modeless dialog you have to check and see if the modeless dialog is already active. Say, m_pmodeless is the modeless dialog member of your class. In the class constructor set m_pmodeless to NULL. Now each time you check to see if m_pmodeless is NULL and if it is NULL, create a new modeless dialog, otherwise, show a MessageBox that the dialog is already active or use SetForegroundWindow() to bring the modeless dialog to the foreground..
让我们说你要只有一个实例,该非模式对话框活在同一时间。在这种情况下,每一次用户启动一些行动,结果,在使该非模式对话框,你必须检查,看看是否该非模式对话框是已经在运作中。也就是说, m_pmodeless
是非模式对话框的成员,你的类类。在类的构造一套m_pmodeless
,以NULL
。现在您每一次检查,看看是否m_pmodeless
是NULL
,如果这是NULL
,创建一个新的非模式对话框,否则,显示MessageBox
认为,对话框是已经活跃或使用 SetForegroundWindow()
把非模式对话框到前景..
Here is how I create my modeless dialog now that I want to restrict them to just one at a time:-
这里是我如何创建我的非模式对话框现在我想限制他们只是一个在时间: -
if(m_pmodeless) { m_pmodeless->SetForegroundWindow(); } else { m_pmodeless = new CModeLess(this); m_pmodeless->Create(CModeLess::IDD); m_pmodeless->ShowWindow(SW_SHOW); }
But when the dialog is destroyed we need to inform the parent class that the pointer it holds is now useless. What we do is to set that pointer to NULL in the PostNcDestroy. In fact it is essential that you do this, otherwise the next time the user tries to activate the modeless dialog, your program will crash as it thinks m_pmodeless is still pointing to a valid dialog window and tried to call SetForegroundWindow() on it. And here is my PostNcDestroy :-
但是,当对话框被摧毁,我们必须告知父类指针,它现在是无用的。我们做什么,是集指针NULL
,在PostNcDestroy
。事实上,这是十分重要的,你这样做,否则下一次用户试图启动非模式对话框,您的程序会崩溃,因为它认为m_pmodeless
仍然是指向一个有效的对话框窗口,并试图调用 SetForegroundWindow()
它。这里是我PostNcDestroy
: -
void CModeLess::PostNcDestroy() { CDialog::PostNcDestroy(); ((CMainFrame*)m_parent)->m_pmodeless = NULL; delete this; }