首页 > 其他分享 >vc的模态对话框和非模态对话框

vc的模态对话框和非模态对话框

时间:2023-06-11 14:06:09浏览次数:35  
标签:模态 vc 窗口 hWndParent Windows 对话框 消息


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_myDlg

CMyView::OnOpenDialogButton()
 {
   CMainFrame* pWnd=(CMainFrame*)AfxGetMainWnd();
   ASSERT_VALID(pWnd);    //定义父窗口指针pWnd
   m_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::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完成删除窗口对象的工作,具体代码如下

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))
   { file://从消息队列中取得消息
   TranslateMessage(&&msg);
   file://检索并生成字符消息WM_CHAR
   DispatchMessage(&&msg);
   file://将消息发送给相应的窗口函数
   }


  
  由此可见,所谓"消息循环",实际是程序循环。
  
  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_ALL
   file://Enable 父窗口
   if (bEnableParent)
   ::EnableWindow(hWndParent, TRUE);
   if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
   ::SetActiveWindow(hWndParent);
   // 删除对话框
   DestroyWindow();
   PostModal();
   … …
   }


  
  可以看到,在此实现代码中,并没有开辟新的线程。系统是在RunModalLoop()中进行消息循环。当 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 loop
   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_ALL
   
   if (bEnableParent)
   ::EnableWindow(hWndParent, TRUE);
   if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
   ::SetActiveWindow(hWndParent);
   // destroy modal window
   DestroyWindow();
   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编程是很有必要的。


标签:模态,vc,窗口,hWndParent,Windows,对话框,消息
From: https://blog.51cto.com/u_130277/6457563

相关文章

  • VC的托盘程序
    VC编程制作系统托盘程序Windows操作系统中的某些程序运行时不显示运行窗口,只在任务栏上显示一个图标,表示程序正在运行,用户可以通过鼠标与应用程序交互,比如金山毒霸等应用程序,我们有时也需要编制一些仅在后台运行的类似程序,为了不干扰前台程序的运行界面和不显示不必要的窗口,应使......
  • VC内存泄露检查工具:Visual Leak Detector
    灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题。当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题。内存泄漏是最常见的内存问题之一。内存泄漏如果不是很严重,在短时间内对程序不会有太大的影响,这也使得内存泄漏问题有很强的隐蔽性,不容......
  • odoo16弹出对话框中显示SearchPanel
    在odoo中,有些数据模型的Search视图可能会定义关于SearchPanel的部分,但这部分定义不会在弹出对话框中显示。如员工的list视图显示如下: 但如果我们在一个挑选员工的弹出对话框中,显示是这样子的。 这样如果员工较多,我们还要进一步输入搜索条件来进行过虑,不太方便操作。那如果......
  • mysql MVCC 原理
    MVCC的定义MVCC,即多版本并发控制,是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC的目的是为了提高数据库的并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。MVCC的目的在MySQL中,InnoDB......
  • MVCC(多版本并发控制)
    1、什么是MVCC(解决读写冲突,写都是最新版本)MVCC的实现依赖于:隐藏字段、Undolog、ReadView。    MVCC多版本并发控制,顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离几倍下执行一致性读操作有了保证。换言之,就是为了......
  • Spring MVC中的调用链路
    在SpringMVC中,请求处理的过程通常分为以下几个步骤:通过HandlerMapping根据请求的URL匹配到相应的Handler(处理器)。通过HandlerAdapter调用对应的Handler处理请求。调用Handler返回一个ModelAndView对象。通过ViewResolver将ModelAndView中的视图逻辑名解析成对应的Vie......
  • SpringMVC WebUploader 分片上传
    ​ 需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以500G来进行限制。PC端全平台支持,要求支持Windows,Mac,Linux支持所有浏览器。支持文件批量上传支持文件夹上传,且要求在服务端保留层......
  • vc6,windows 7 x64 调试 (IS2120@BG57IV3)
    //z2012-12-2416:31:[email protected]在windows7x64上调试vc6console程序时,按了shift+f5,程序并不结束。解决方法:替换这个文件TLLOC.dllwww.dr-hoiby.com/TLLOC.dll//z2012-12-2416:31:[email protected]......
  • VC6.0编译器参数设置
    VC6.0编译器参数的设置主要通过VC的菜单项Project->Settings->C/C++页来完成。我们可以看到这一页的最下面ProjectOptions中的内容,一般如下:/nologo/MDd/W3/Gm/GX/ZI/Od/D"WIN32"/D"_DEBUG"/D"_WINDOWS"/D"_AFXDLL"/D"_MBCS"/Fp"Debug......
  • ASP.NET MVC5.0 筑基到炼气大圆满一篇就搞定
     一、ASP.NETMVC过滤器ASP.NETMVC框架支持四种不同类型的过滤器:授权过滤器 -实现IAuthorizationFilter属性。动作过滤器 -实现IActionFilter属性。结果过滤器 -实现IResultFilter属性。异常过滤器 -实现IExceptionFilter属性。 namespaceMVC......