vc 的模态对话框和非模态对话框2009-08-09 09:56 Generally Speaking(1)创建一非模态对话框CMyDlg* pMainWnd = new CMyDlg; pMainWnd->Create();(2)创建一模态对话框CMyDlg dlg;dlg.Domdal();非模态的对话框是独立的,就是说和主程序可以同时交换数据,而模态的对话框 则只能和自己交换换句话说,模态对话框很霸道,你开了它就无法在同一个应 用程序中的其他地方干活了,比如那个经常用的openfile dialog非模态对话 框就很随和,开了它你还可以爱干嘛干嘛还有一个区别在于内存分配,new在 堆中, 另一在栈中, 一般说两者一样, 但如果你在对话框中开了很大的空间做成员, 第二种就会有问题了,因为栈空间是受限的但据说解决栈溢出用static申明, 在全局数据区分配内存就可以了(我没试过)!模态消息不进message loop; 非模态消息进入 message loop如何使用 vc 的非模态对话框1、 非模态对话框的模板必须具有 Visible 风格,否则对话框将不可见,而模态 对话框则无需设置该项风格。
更保险的办法是调用 CWnd::ShowWindow(SW_SHOW) 来显示对话框,而不管对话框是否具有 Visible 风格2、 非模态对话框对象是用new操作符在堆中动态创建的,而不是以成员变量的 形式嵌入到别的对象中或以局部变量的形式构建在堆栈上通常应在对话框的拥 有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话框 对象3、 *通过调用 CDialog::Create 函数来启动对话框,而不是 CDialog::DoModal, 这是模态对话框的关键所在由于 Create 函数不会启动新的消息循环,对话框 与应用程序共用同一个消息循环,这样对话框就不会垄断用户的输入 Create 在显示了对话框后就立即返回,而 DoModal 是在对话框被关闭后才返回的众所 周知,在MFC程序中,窗口对象的生存期应长于对应的窗口,也就是说,不能在 未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉由于在Create返回后, 不能确定对话框是否已关闭,这样也就无法确定对话框对象的生存期,因此只好 在堆中构建对话框对象,而不能以局部变量的形式来构建之4、 代码示例:对话框资源ID为:IDD_MYDIALOG 对话框的类定义为: CMyDialog CMyView 的成员变量为: CMyDialog* m_myDlgCMyView::OnOpenDialogButton(){CMainFrame* pWnd=(CMainFrame*)AfxGetMainWnd();ASSERT_VALID(pWnd); //定义父窗口指针 pWndm_myDlg=new CMyDialog(pWnd); //堆分配非模态对话框内存空间, //CMyDialog(CWnd* pParent二NULL)为构造函数m_myDlg->Create(IDD_MYDIALOG,pWnd);m_myDlg->ShowWindow(SW_SHOW);//可用this指针代替pWnd指针,则省略头两行}5、 必须有一个标志表明非模态对话框是否是打开的。
这样做的原因是用户有可 能在打开一个模态对话框的情况下,又一次选择打开命令程序根据标志来决定 是打开一个新的对话框,还是仅仅把原来打开的对话框激活通常可以用拥有者 窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋 NULL 值,以表明对话框对象已不存在了[注]:在C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向 该对象的指针是否为空这种机制要求程序员将指向该对象的指针初始化为 NULL 值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置 成 NULL 值6、 必须调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对 话框调用CWnd::DestroyWindow是直接删除窗口的一般方法由于缺省的 CDialog::OnOK 和 CDialog::OnCancel 函数均调用 EndDialog,故程序员必须编 写自己的OnOK和OnCancel函数并且在函数中调用DestroyWindow来关闭对话 框7、 因为是用new操作符构建非模态对话框对象,因此必须在对话框关闭后,用 delete操作符删除对话框对象。
在屏幕上一个窗口被删除后,框架会调用 CWnd::Pos tNcDes troy,这是一个虚拟函数,程序可以在该函数中完成删除窗口 对象的工作,具体代码如下void CModelessDialog::PostNcDestroy{delete this; //删除对象本身}这样,在删除屏幕上的对话框后,对话框对象将被自动删除拥有者对象就不必 显式的调用delete来删除对话框对象了也可以通过由的DestroyWindow()引 起的WM_DESTROY消息处理函数OnDestroy ()种实现delete this操作void CModelessDlg::OnDestroy(){CDialog::OnDestroy();delete this;}模态对话框的使用 在下面的一篇文章里叙述的很详尽,所以就摘抄过来,呵呵Visual C++模态对话框消息处理机制剖析摘要:消息驱动机制是Windows操作系统的根本,模态对话框消息处理又是不同 于一般消息处理的特殊形式通过分析这种消息机制的原理,可用来处理类似的 程序设计要求在 Windows 操作系统中,面向用户的 GUI 基本上可分为对话框形式和文档/ 视图两种表现形式。
对话框的显示方式又可分为模态对话框和非模态对话框,以 适应不同的用户交互需求由于对话框和文档/视图框架结构各有特色,能不能 将文档/视图框架结构当作一对话框来使用,或在对话框中实现文档/视图框架结 构内的特色功能呢,答案是肯定的下面,从 Windows 操作系统消息驱动机制开始,进而探讨模态对话框实现 过程的消息封装、传递和处理机制,最后以模态的形式显示应用到文档/视图框 架结构中的实例作为对所讲内容的验证和实践一、Windows消息机制Windows 是一种面向对象的体系结构, Windows 环境和应用程序都是通过消 息来交互的Windows应用程序开始执行后,Windows为该程序创建一个"消息队 列(message queue)",用以存放邮寄给该程序可能创建的各种不同窗口的消息 消息队列中消息的结构(MSG)为:typedef struct tagMSG{HWND hwnd;UINT message;WPARAM wParam;LPARAM lParam;DWORD time;POINT pt;}MSG;其中第一个成员变量是用来标识接收消息的窗口句柄;第二个参数便是消息 标识号,如WM_PAINT;第三个和第四个参数的具体意义同message值有关,均 为消息参数。
前四个参数是非常重要和经常用到的,至于后两个参数则分别表示 邮寄消息的时间和光标位置(屏幕坐标)把消息传送到应用程序有两种方法: 一种是由系统将消息〃邮寄(post)〃到应用程序的"消息队列〃这是"进队消息 "Win32 API有对应的函数:PostMessage(),此函数不等待该消息处理完就返 回;而另一种则是由系统在直接调用窗口函数时将消息〃发送(send) 〃给应用程序 的窗口函数,属于〃不进队消息〃对应的函数是SendMessage()其必须等待该消息 处理完后方可返回对于每一个正在执行的Windows应用程序,系统为其建立一个"消息队列", 即应用程序队列,用来存放该程序可能创建的各种窗口的消息应用程序中含有 一段称作"消息循环"的代码,用来从消息队列中检索这些消息并把它们分发到相 应的窗口函数中消息循环代码是应用程序中主函数winmain ()中类似如下的程序段:while(GetMessage(&&msg,NULL,NULL,NULL)){ fil e://从消息队列中取得消息TranslateMessage(&&msg);file://检索并生成字符消息WM_CHARDispatchMessage(&&msg);fil e://将消息发送给相应的窗口函数}由此可见,所谓"消息循环",实际是程序循环。
Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数, 窗口函数程序代码形式上是一个巨大的switch语句,用以处理由消息循环发送 到该窗口的消息,窗口函数由Windows采用消息驱动的形式直接调用,而不是 由应用程序显示调用的,窗口函数处理完消息后又将控制权返回给Windows二、模态对话框的消息处理由上面我们看到,Windows是一个巨大的消息驱动结构,由用户发出消息, 系统响应处理非模态对话框是响应一个消息,系统处理一个消息,处理完毕后 返回控制权给Windows文档/视图框架结构与其类似模态对话框在对话框创 建后,挂起外部的消息,只是响应对话框内部的消息,而外部消息则全部"过滤" 掉了,直到系统接收到WM_DESTROY或WM_CLOSE后,系统返回控制权给模态对话 框创建前的线程,继续模态对话框创建前的线程将执行下面的代码让我们看看下面的对话框DoModal实现代码:{• • • • • •// Disable 父窗口 (在创建对话框前)HWND hWndParent = PreModal();AfxUnhookWindowCreate();BOOL bEnableParent = FALSE;if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)){::EnableWindow(hWndParent, FALSE);bEnableParent = TRUE;}TRY{// 创建模态对话框AfxHookWindowCreate(this);if (CreateDlgIndirect(lpDialogTemplate,CWnd::FromHandle(hWndParent), hInst)){if (m_nFlags & WF_CONTINUEMODAL){// 进入模式循环DWORD dwFlags = MLF_SHOWONIDLE;if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG;VERIFY(RunModalLoop(dwFlags) == m_nModalResult);}}}CATCH_ALL(e){DELETE_EXCEPTION(e); m_nModalResult = -1;}END_CATCH_ALLfile://Enable 父窗口if (bEnableParent)::EnableWindow(hWndParent, TRUE);if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)::SetActiveWindow(hWndParent);// 删除对话框DestroyWindow();PostModal();• • • • • •}可以看到,在此实现代码中,并没有开辟新的线程。
系统是在RunModalLoopO 中进行消息循环当m_nFlags为WF_CONTINUEMODAL时,系统继续模式状态 RunModalLoop ()函数实际上也是一 for(;;)循环,控制重新分派Windows消息 直到 ContinueModal()返回 FALSE,而当调用 EndModalLoop()时,ContinueModal() 返回FALSE此时,标志着模态显示的结束因此,实现模态对话框消息处理的 核心部分为 RunModalLoop ()和 EndModalLoop()函数三、以模态的形式显示应用到文档/视图框架结构实例(1) 新建一工程文件:ModeFrame,选取 MFC AppWizard(exe)2) 第二步选取Single document (单文档) 3)其余几步均为缺省值4) 用 ClassWizard 添加一新类 CSubModeFrame,以 CFrameWnd 为基类5) 添加 CsubModeFrame 的实现函数 DoMode();int CsubModeFrame::DoModal(){HWND hWndParent = m_hWndPrt;CRect rc(0,0,400,400);CWnd *pParent = CWnd::FromHandle(hWndParent);DWORD dwStyle=WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_THICKFRAME | WS_VISIBLE | WS_SYSMENU | WS_CAPTION;if(!Create(NULL,"模态文档 /试 图框架",dwStyle,rc,pParent,NULL)) return FALSE;BOOL bEnableParent = FALSE;if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)){::EnableWindow(hWndParent,FALSE); ::EnableWindow(m_hWnd,TRUE);bEnableParent = TRUE;}CenterWindow();TRY{// enter modal loopDWORD dwFlags = MLF_SHOWONIDLE; if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG; VERIFY(RunModalLoop(dwFlags) == m_nModalResult);}CATCH_ALL(e){ DELETE_EXCEPTION(e);m_nModalResult = -1;}END_CATCH_ALLif (bEnableParent) ::EnableWindow(hWndParent, TRUE);if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)::SetActiveWindow(hWndParent);// destroy modal windowDestroyWindow();return m_nModalResult;}(6) 添加 CsubModeFrame 的实现函数 EndMode()void CSubFrame::EndModal(){ASSERT(::IsWindow(m_hWnd));if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL)) { EndModalLoop(1);}}(7) 添加 CModeFrameView 的实现函数 OnLButtonDblClk()在此函数的消息处理中:可以象显示对话框一样处理 CsubModeFrame 类。
CSubModeFrame SubModeFrame;If(SubModeFrame.DoMode()){ MessageBox("Mode Ok");}(8) 编译运行工程,双击视图,就会弹出模态的子文档/视图框架结构结论: 通过上面的分析和实例可以看出,深入研究了解 Windows 的消息处理 机制,可利用消息对 Windows 的事件进行任意的定制和处理,不用拘泥于系统原 有的模式对进行深入 Windows 编程是很有必要的。