精华内容
下载资源
问答
  • MFC 对话框

    千次阅读 2013-10-30 15:15:47
    MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框。 对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。 模式对话框对象被定义后,...

    MFC 对话框

    1、对话框基本要点和生命周期
    MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框。
    对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。
    模式对话框对象被定义后,通过调用DoModal()函数来显示对话框并进行相关操作,此函数当对话框被关闭时返回。其返回值标明了对话框是点“确定”退 出,还是“取消”。非模式对话框需要与某个View相关联,以便对话框退出时发送消息给对应的Vew进行必要的处理。
    在对话框显示前,系统会调用OnInitDialog() 函数,在这个函数中你可以设置一些控件属性,进行一些初始化工作。比如,设置滚动条的最大最小值,设置List列表的初始值等。其方法是将控件ID作为参 数,调用GetDlgItem函数获得控件的对象的指针(指针类型是CWnd*),然后使用对象提供的函数进行操作。
    ID对于一个组件来说非常重要,通过向导,我们可以将一个变量和一个组件进行关联(映射)来实现数据交换,而这种绑定的关键就是将一个组件的ID与成员变量关联。

    2、数据交换机制
    控件是对话框的重要组成部分,控件的访问可以通过关联变量实现,包括关联数据变量和控制变量。MFC编程中,通过建立类向导中 的操作可以将窗口控件和对应变量绑定,但是代码操作的是变量,用户操作窗口控件如何让他们同步?UpdateData(Bool true|false)函数正是实现这个功能。

    DoDataExchange由框架类调用,用于交换和检验对话框的数据,该函数不直接调用,而是被UpdateData调用。通过update(TRUE)取得控件上的值,处理修改后通过update(FALSE)传回控件。

    UpdateData(TRUE) -- 刷新控件的值到对应的变量
    UpdateData(FALSE) -- 拷贝变量值到控件显示

    UpdataData()---用来刷新对话框

    3、特殊的Radio Button
    Radio Button控件是分组的,同一组的Radio Button只能有一个被选中。这个机制的实现依赖于TAB顺序,在资源视图下按Ctrl-D键将显示对话框的TAB焦点顺序。举一个例子来说明:
    Radio1、Radio2、Radio3是三个不同的Radio Button控件,其焦点顺序为1、2、3。为了实现分组Radio1的Group属性应该为TRUE,其余两个为FALSE。如果又有两个 Radio4、Radio5焦点顺序为6、7。则Radio4的Group属性应为TRUE,Radio4,Radio5被分为一组。
    需要注意的是,Radio以Group属性来分组,为了结束前一个组,你应该将焦点顺序为4、8的控件的Group属性设为TRUE,否则编译器会产生一个警告。

    4、一些技巧
    通过向导,我们可以将一个类成员变量和控件关联以进行数据交换,例如将一个CString类型的变量和Edit控件关联。将一个int变量和一组Radio Button关联。但是,人总有错的时候,当我们修改或需要删除这种关联时,麻烦就来了。
    在我的使用VS2005过程中没有发现提供了删除“已被关联的控件成员变量”的向导,所以我使用的是比较麻烦的手动删除。
    1)在对话框头文件中删除成员变量的定义
    2)在对话框cpp文件中删除构造函数初始化列表中的对应变量的初始化
    3)在对话框cpp文件中,根据变量名删除DoDataExchange函数中的对应语句
    此时,以class view中的向导中,已经可以重新设定控件所关联的成员变量了。

    登录框的制作:
    在显示主窗口之前显示一个模式对话框来提示用户登录一个常用的功能。只需要在PreCreateWindow函数中加入显示对话框的代码就可以完成这个功能。

          有些时候,我们可能需要从一个控件对象来得到它的ID。比如,你的对话框中好几个滚动条,那么这些滚动条的事件都在OnHScroll,OnVScroll中被响应。如何区分是哪个滚动条就需要确定ID。
    在这两个函数中有一个CScrollBar *pScrollBar指针,我们可以通过调用pScrollBar->GetDlgCtrllD()来获得ID,ID是一个整数。

    在对话框编程中往往需要改变某个控件的文字,比如EDIT控件和Static text控件。此时使用SetDlgItemText(int nID,LPCTSTR lpzString)函数比较方便


    MFC窗口销毁过程

    考虑单窗口情况:

    假设自己通过new创建了一个窗口对象pWnd,然后pWnd->Create。则销毁窗口的调用次序:

    1.       手工调用pWnd->DestroyWindow();

    2.       DestroyWindow会发送WM_DESTROY;

    3.       WM_DESTROY对应的消息处理函数是OnDestroy();

    4.       DestroyWindow会发送WM_NCDESTROY;

    5.       WM_NCDESTROY对应的消息处理函数是OnNcDestroy;

    6.       OnNcDestroy最后会调用PostNcDestroy;

    7.       PostNcDestroy经常被用户重载以提供释放内存操作。例如可以使用delete this;

    通过这种方式,窗口对象对应的窗口和窗口对象本身都被释放了。

    如果含有子窗口:

          如果含有子窗口,则调用父窗口的DestroyWindow时,它会向子窗口发送WM_DESTROY和WM_NCDESTROY消息。

          具体调用顺序参考下文的例子。

    DestroyWindowdelete的影响:

          应该说前者对后者并没有什么影响。但经常在DestroyWindow间接导致执行的PostNcDestroy中delete窗口对象指针,即delete this。

          CView::PostNcDestroy中唯一的操作就是delete this;CframeWnd::PostNcDestory也是如此。而默认的CWnd::PostNcDestroy是空操作,CDialog中也没有对其进行重载,即也是空。

    deleteDestroy的影响:

          delete会导致析构函数。CWnd的析构函数中有对DestroyWindow的调用,但必须保证:

    m_hWnd != NULL &&

               this != (CWnd*) & wndTop && this != (CWnd*)&wndBottom &&

               this != (CWnd*)&wndTopMost && this != (CWnd*)&wndNoTopMost。

          Cdialog的析构函数中也有对DestroyWindow的调用,但条件比较松,只需要m_hWnd != NULL。另外Cdialog::DoModal也会调用DestroyWindow。

          CFrameWnd的OnClose中会调用DestroyWindow,但其析构中不会调用DestroyWindow。

          CView的析构也不会调用DestroyWindow。

    一个SDI程序的销毁过程

          有CMainFrame类、CMyView类。并且CMyView有两个子窗口CMyDlg和CmyWnd的实例。

          点击退出按钮,CMainFrame会收到WM_CLOSE消息。CframeWnd(CMainFrame的父类)间接会调用CWnd::DestroyWindow;它首先向CMyView发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyDlg发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数;然后向CMyWnd发送WM_DESTORY和WM_NCDESTROY消息,并引发相应的处理函数。

          具体的执行顺序是:

    1.       调用CMainFrame::DestroyWindow

    2.       CFrameWnd::OnDestroy

    3.       CMyView::OnDestroy

    4.       CmyWnd::OnDestroy

    5.       CmyDlg::OnDestroy

    6.       CmyWnd::PostNcDestroy

    7.       CmyWnd的析构

    8.       CmyDlg::OnDestroy

    9.       CmyDlg的析构

    10.   CMyView::PostNcDestroy

    11.   CmyView的析构

    12.   CMainFrame的析构

    13.   CMainFrame::DestroyWindow退出

    上面情况是假设我们在CmyWnd和CmyDlg的PostNcDestroy中添加了delete this。如果没有添加,则7,10不会执行。

          因为CView::PostNcDestroy中调用了delete this,所以然后会执行CMyView的析构操作。因为CframeWnd::PostNcDestroy中调用了delete this,所以最后执行CMainFrame的析构操作。

          如果自己的CmyDlg和CmyWnd在PostNcDestroy中有delete this;则二者会被析构。否则内存泄漏。当然delete也可以放在CMyView的析构中做,只是不够OO。

    总结

          可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过DestroyWindow。这是比较好的方法,因为最后MFC会自动相应WM_CLOSE导致CframWnd::DestroyWindow被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在PostNcDestroy中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以delete this可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。

          另一种是delete。Delete一个窗口对象指针有的窗口类(如CWnd,Cdialog)会间接调用DestroyWindow,有的窗口类(如CView,CframeWn)不会调用DestroyWindow。所以要小心应对。

          二者是相互调用的,很繁琐。

     

     

    一个MFC窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在m_hWnd成员中的HWND(窗口句柄),二是窗口对象本身是一个C++对象。要删除一个MFC窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。

    删除窗口最直接方法是调用CWnd::DestroyWindow::DestroyWindow,前者封装了后者的功能。前者不仅会调用后者,而且会使成员m_hWnd保存的HWND无效(NULL)。如果DestroyWindow删除的是一个父窗口或拥有者窗口,则该函数会先自动删除所有的子窗口或被拥有者,然后再删除父窗口或拥有者。在一般情况下,在程序中不必直接调用DestroyWindow来删除窗口,因为MFC会自动调用DestroyWindow来删除窗口。例如,当用户退出应用程序时,会产生WM_CLOSE消息,该消息会导致MFC自动调用CWnd::DestroyWindow来删除主框架窗口,当用户在对话框内按了OKCancel按钮时,MFC会自动调用CWnd::DestroyWindow来删除对话框及其控件。

    窗口对象本身的删除则根据对象创建方式的不同,分为两种情况。在MFC编程中,会使用大量的窗口对象,有些窗口对象以变量的形式嵌入在别的对象内或以局部变量的形式创建在堆栈上,有些则用new操作符创建在堆中。对于一个以变量形式创建的窗口对象,程序员不必关心它的删除问题,因为该对象的生命期总是有限的,若该对象是某个对象的成员变量,它会随着父对象的消失而消失,若该对象是一个局部变量,那么它会在函数返回时被清除。

    对于一个在堆中动态创建的窗口对象,其生命期却是任意长的。初学者在学习C++编程时,对new操作符的使用往往不太踏实,因为用new在堆中创建对象,就不能忘记用delete删除对象。读者在学习MFC的例程时,可能会产生这样的疑问,为什么有些程序用new创建了一个窗口对象,却未显式的用delete来删除它呢?问题的答案就是有些MFC窗口对象具有自动清除的功能。

    如前面讲述非模态对话框时所提到的,当调用CWnd::DestroyWindow::DestroyWindow删除一个窗口时,被删除窗口的PostNcDestroy成员函数会被调用。缺省的PostNcDestroy什么也不干,但有些MFC窗口类会覆盖该函数并在新版本的PostNcDestroy中调用delete this来删除对象,从而具有了自动清除的功能。此类窗口对象通常是用new操作符创建在堆中的,但程序员不必操心用delete操作符去删除它们,因为一旦调用DestroyWindow删除窗口,对应的窗口对象也会紧接着被删除。

    不具有自动清除功能的窗口类如下所示。这些窗口对象通常是以变量的形式创建的,无需自动清除功能。

    所有标准的Windows控件类。

    1.       CWnd类直接派生出来的子窗口对象(如用户定制的控件)。

    2.       切分窗口类CSplitterWnd

    3.       缺省的控制条类(包括工具条、状态条和对话条)。

    4.       模态对话框类。

     

    具有自动清除功能的窗口类如下所示,这些窗口对象通常是在堆中创建的。

    1.       主框架窗口类(直接或间接从CFrameWnd类派生)。

    2.       视图类(直接或间接从CView类派生)。

     

    读者在设计自己的派生窗口类时,可根据窗口对象的创建方法来决定是否将窗口类设计成可以自动清除的。例如,对于一个非模态对话框来说,其对象是创建在堆中的,因此应该具有自动清除功能。

    综上所述,对于MFC窗口类及其派生类来说,在程序中一般不必显式删除窗口对象。也就是说,既不必调用DestroyWindow来删除窗口对象封装的窗口,也不必显式地用delete操作符来删除窗口对象本身。只要保证非自动清除的窗口对象是以变量的形式创建的,自动清除的窗口对象是在堆中创建的,MFC的运行机制就可以保证窗口对象的彻底删除。

    如果需要手工删除窗口对象,则应该先调用相应的函数(如CWnd::DestroyWindow)删除窗口,然后再删除窗口对象.对于以变量形式创建的窗口对象,窗口对象的删除是框架自动完成的.对于在堆中动态创建了的非自动清除的窗口对象,必须在窗口被删除后,显式地调用delete来删除对象(一般在拥有者或父窗口的析构函数中进行).对于具有自动清除功能的窗口对象,只需调用CWnd::DestroyWindow即可删除窗口和窗口对象。注意,对于在堆中创建的窗口对象,不要在窗口还未关闭的情况下就用delete操作符来删除窗口对象

     

    对话框经常被使用,因为对话框可以从模板创建,而对话框模板是可以使用资源编辑器方便地进行编辑的。

    一,模式与非模式对话框
    1.模式对话框
        一个模式对话框是一个有系统菜单、标题栏、边线等的弹出式窗口。在创建对话框时指定WS_POPUP, WS_SYSMENU, WS_CAPTION和 DS_MODALFRAME风格。即使没有指定WS_VISIBLE风格,模

    式对话框也会被显示。创建对话框窗口时,将发送WM_INITDIALOG消息(如果指定对话框的DS_SETFONT风格,还有WM_SETFONT消息)给对话框过程。
        对话框窗口被创建之后,Windows使得它成为一个激活的窗口,它保持激活直到对话框过程调用::EndDialog函数结束对话框的运行或者Windows激活另一个应用程序为止,在激活时,用户

    或者应用程序不可以激活它的所属窗口(Owner window)。从某个窗口创建一个模式对话框时,Windows自动地禁止使用(Disable)这个窗口和它的所有子窗口,直到该模式对话框被关闭和

    销毁。虽然对话框过程可以Enable所属窗口,但是这样做就失去了模式对话框的作用,所以不鼓励这样做。
        Windows创建模式对话框时,给当前捕获鼠标输入的窗口(如果有的话)发送消息WM_CANCLEMODE。收到该消息后,应用程序应该终止鼠标捕获(Release the mouse capture)以便于用户能

    把鼠标移到模式对话框;否则由于Owner窗口被禁止,程序将失去鼠标输入。
       为了处理模式对话框的消息,Windows开始对话框自身的消息循环,暂时控制整个应用程序的消息队列。如果Windows收到一个非对话框消息时,则它把消息派发给适当的窗口处理;如果收

    到了WM_QUIT消息,则把该消息放回应用程序的消息队列里,这样应用程序的主消息循环最终能处理这个消息。
       当应用程序的消息队列为空时,Windows发送WM_ENTERIDLE消息给Owner窗口。在对话框运行时,程序可以使用这个消息进行后台处理,当然应该注意经常让出控制给模式对话框,以便它能

    接收用户输入。如果不希望模式对话框发送 WM_ENTERIDlE消息,则在创建模式对话框时指定DS_NOIDLEMSG风格。
       一个应用程序通过调用::EndDialog函数来销毁一个模式对话框。一般情况下,当用户从系统菜单里选择了关闭(Close)命令或者按下了确认(OK)或取消(CANCLE)按钮,::EndDialog

    被对话框过程所调用。调用::EndDialog时,指定其参数nResult的值,Windows将在销毁对话框窗口后返回这个值,一般,程序通过返回值判断对话框窗口是否完成了任务或者被用户取消。

     

    2. 无模式对话框
       一个无模式对话框是一个有系统菜单、标题栏、边线等的弹出式窗口。在创建对话框模板时指定WS_POPUP、WS_CAPTION、WS_BORDER和WS_SYSMENU风格。如果没有指定WS_VISIBLE风格,无

    模式对话框不会自动地显示出来。一个无模式对话框既不会禁止所属窗口,也不会给它发送消息(WM_ENTERIDlE)。当创建一个无模式对话框时,Windows使它成为活动窗口,但用户或者程序

    可以随时改变和设置活动窗口。如果对话框失去激活,那么即使所属窗口是活动的,在Z轴顺序上,它仍然在所属窗口之上。
       应用程序负责获取和派发输入消息给对话框。大部分应用程序使用主消息循环来处理,但是为了用户可以使用键盘在控制窗口之间移动或者选择控制窗口,应用程序应该调

    用::IsDialogMessage函数。
       这里,顺便解释::IsDialogMessage函数。虽然该函数是为无模式对话框设计的,但是任何包含了控制子窗口的窗口都可以调用它,用来实现类似于对话框的键盘选择操作。
    当::IsDialogMessage处理一个消息时,它检查键盘消息并把它们转换成相应对话框的选择命令。例如,当Tab 键被压下时,下一个或下一组控制被选中,当Down Arrow键按下后,一组控制中

    的下一个控制被选择。::IsDialogMessage完成了所有必要的消息转换和消息派发,所以该函数处理的消息一定不要传递给TranslateMessage和DispatchMessage处理。一个无模式对话框不能

    像模式对话框那样返回一个值给应用程序。但是对话框过程可以使用::SendMessage给所属窗口传递信息。
       在应用程序结束之前,它必须销毁所有的无模式对话框。使用::DestroyWindow销毁一个无模式对话框,不是使用::EndDiaLog。一般来说,对话框过程响应用户输入,如用户选择了“取消

    ”按钮,则调用::DestroyWindow;如果用户没有有关动作,则应用程序必须调用::DestroyWindow。

     

    二,对话框的MFC实现
    1.在MFC中,对话框窗口的功能主要由CWnd和CDialog两个类实现。

    1. class CDialog : public CWnd  
    2. {  
    3.     DECLARE_DYNAMIC(CDialog)  
    4. //初始化函数  
    5. public:  
    6.     virtual BOOL Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);  
    7.     virtual BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = NULL);  
    8.     virtual BOOL CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL,void* lpDialogInit = NULL);  
    9.     virtual BOOL CreateIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL);  
    10.         BOOL InitModalIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL,void* lpDialogInit = NULL);  
    11.     BOOL InitModalIndirect(HGLOBAL hDialogTemplate, CWnd* pParentWnd = NULL);  
    12. //构造函数  
    13. public:  
    14.         CDialog();  
    15.     explicit CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);  
    16.     explicit CDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL);  
    17. // 操作函数  
    18. public:  
    19.     // modal processing  
    20.     virtual INT_PTR DoModal();  
    21.   
    22.     // support for passing on tab control - use 'PostMessage' if needed  
    23.     void NextDlgCtrl() const;  
    24.     void PrevDlgCtrl() const;  
    25.     void GotoDlgCtrl(CWnd* pWndCtrl);  
    26.   
    27.     // default button access  
    28.     void SetDefID(UINT nID);  
    29.     DWORD GetDefID() const;  
    30.   
    31.     // termination  
    32.     void EndDialog(int nResult);  
    33.   
    34. //重载函数  
    35. protected:  
    36.     virtual void OnOK();  
    37.     virtual void OnCancel();  
    38. public:  
    39.     virtual BOOL OnInitDialog();  
    40.     virtual void OnSetFont(CFont* pFont);  
    41. public:  
    42.     virtual ~CDialog();  
    43.     virtual BOOL PreTranslateMessage(MSG* pMsg);  
    44.     virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo);  
    45.     virtual BOOL CheckAutoCenter();  
    46.   
    47. protected:  
    48.     UINT m_nIDHelp;                 // Help ID (0 for none, see HID_BASE_RESOURCE)  
    49.     LPCTSTR m_lpszTemplateName;     // name or MAKEINTRESOURCE  
    50.     HGLOBAL m_hDialogTemplate;      // indirect (m_lpDialogTemplate == NULL)  
    51.     LPCDLGTEMPLATE m_lpDialogTemplate;  // indirect if (m_lpszTemplateName == NULL)  
    52.     void* m_lpDialogInit;           // DLGINIT resource data  
    53.     CWnd* m_pParentWnd;             // parent/owner window  
    54.     HWND m_hWndTop;                 // top level parent window (may be disabled)  
    55.   
    56.         virtual void PreInitDialog();  
    57.     HWND PreModal();  
    58.     void PostModal();  
    59. };  

    2.模式对话框的实现
       在Win32 SDK编程下的模式对话框使用了Windows提供给对话框窗口的窗口过程和自己的对话框过程,对话框过程将被窗口过程调用。但在MFC下,所有的窗口类都使用了同一个窗口过程,

    CDialog也不例外。CDialog对象在创建Windows对话框时,采用了类似于CWnd的创建函数过程,采用子类化的手段将Windows提供给对话框的窗口过程取代为AfxWndProc或者AfxBaseWndProc,

    同时提供了对话框过程AfxDlgProc。那么,这些“过程”是如何实现或者协调的呢?下文将予以分析。

    I.MFC对话框过程
    //MFC对话框过程AfxDlgProc的原型和实现如下:

    1. BOOL CALLBACK AfxDlgProc(HWND hWnd,UINT message, PARAM, LPARAM)  
    2. {  
    3.     if (message == WM_INITDIALOG)  
    4.     {  
    5.          //处理WM_INITDIALOG消息  
    6.          CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog,CWnd::FromHandlePermanent(hWnd));  
    7.          if (pDlg != NULL)  
    8.              return pDlg->OnInitDialog();  
    9.          else  
    10.              return 1;  
    11.     }  
    12.   
    13.     return 0;  
    14. }  

       由上可以看出,MFC的对话框函数AfxDlgProc仅处理消息WM_INITDIALOG,其他都留给对话框窗口过程处理。因此,它不同于SDK编程的对话框过程。程序员在SDK的对话框过程处理消息和事

    件,实现自己的对话框功能。AfxDlgProc处理WM_INITDIALOG消息时调用虚拟函数OnInitDialog,给程序员一个机会处理对话框的初始化。

     

    II.模式对话框窗口过程
       AfxWndProc是所有的MFC窗口类使用的窗口过程,它取代了模式对话框原来的窗口过程(Windows提供),那么,MFC如何完成Win32下对话框窗口的功能呢?考查模式对话框的创建过程。

    CDialog::DoModal用来创建模式对话框窗口并执行有关任务,和DoModal相关的是MFC内部使用的成员函数CDialog::PreModal和CDialog::PostModal。下面分别讨论它们的实现。

    1. HWND CDialog::PreModal()  
    2. {  
    3.     ASSERT(m_hWnd == NULL);  
    4.   
    5.     // allow OLE servers to disable themselves  
    6.     AfxGetApp()->EnableModeless(FALSE);  
    7.   
    8.     // 得到父窗口  
    9.     CWnd* pWnd = CWnd::GetSafeOwner(m_pParentWnd, &m_hWndTop);  
    10.   
    11.     // 如同CWnd处理其他窗口的创建,设置一个窗口创建HOOK  
    12.     AfxHookWindowCreate(this);  
    13.   
    14.     //返回父窗口的句柄  
    15.     return pWnd->GetSafeHwnd();  
    16. }  
    17.   
    18. void CDialog::PostModal()  
    19. {  
    20.     //取消窗口创建前链接的HOOK  
    21.     AfxUnhookWindowCreate(); // just in case  
    22.   
    23.     //MFC对话框对象和对应的Windows对话框窗口分离  
    24.     Detach(); // just in case  
    25.   
    26.     // m_hWndTop是当前对话框的父窗口或所属窗口,则恢复它  
    27.     if (::IsWindow(m_hWndTop))  
    28.         ::EnableWindow(m_hWndTop, TRUE);  
    29.     m_hWndTop = NULL;  
    30.     AfxGetApp()->EnableModeless(TRUE);  
    31. }  
    32.   
    33. int CDialog::DoModal()  
    34. {  
    35.     ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL || m_lpDialogTemplate != NULL);  
    36.   
    37.     //加载对话框资源  
    38.     LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;  
    39.     HGLOBAL hDialogTemplate = m_hDialogTemplate;  
    40.     HINSTANCE hInst = AfxGetResourceHandle();  
    41.     if (m_lpszTemplateName != NULL)  
    42.     {  
    43.         hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);  
    44.         HRSRC hResource =::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);  
    45.         hDialogTemplate = LoadResource(hInst, hResource);  
    46.     }  
    47.   
    48.     //锁定加载的资源  
    49.     if (hDialogTemplate != NULL)  
    50.         lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);  
    51.     if (lpDialogTemplate == NULL)  
    52.         return -1;  
    53.   
    54.     //创建对话框前禁止父窗口,为此要调用PreModal得到父窗口句柄  
    55.     HWND hWndParent = PreModal();  
    56.     AfxUnhookWindowCreate();  
    57.     CWnd* pParentWnd = CWnd::FromHandle(hWndParent);  
    58.     BOOL bEnableParent = FALSE;  
    59.     if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))  
    60.     {  
    61.         ::EnableWindow(hWndParent, FALSE);  
    62.         bEnableParent = TRUE;  
    63.     }  
    64.   
    65.     //创建对话框,注意是无模式对话框  
    66.     TRY  
    67.     {  
    68.         //链接一个HOOK到HOOK链以处理窗口创建,  
    69.         AfxHookWindowCreate(this);  
    70.   
    71.         //CreateDlgIndirect间接调用::CreateDlgIndirect,最终调用了::CreateWindowEX来创建对话框窗口。  
    72.         //HOOK过程_AfxCbtFilterHook用子类化的方法取代原来的窗口过程为AfxWndProc。  
    73.         if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst))  
    74.         {  
    75.             if (m_nFlags & WF_CONTINUEMODAL)  
    76.             {  
    77.                 // enter modal loop  
    78.                 DWORD dwFlags = MLF_SHOWONIDLE;  
    79.                 //RunModalLoop接管整个应用程序的消息处理  
    80.                 if (GetStyle() & DS_NOIDLEMSG)  
    81.                     dwFlags |= MLF_NOIDLEMSG;  
    82.                 VERIFY(RunModalLoop(dwFlags) == m_nModalResult);  
    83.             }  
    84.   
    85.             // hide the window before enabling the parent, etc.  
    86.             if (m_hWnd != NULL)  
    87.                 SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);  
    88.         }  
    89.     }  
    90.   
    91.     CATCH_ALL(e)  
    92.     {  
    93.         DELETE_EXCEPTION(e);  
    94.         m_nModalResult = -1;  
    95.     }  
    96.     END_CATCH_ALL  
    97.   
    98.     //Enable并且激活父窗口  
    99.     if (bEnableParent)  
    100.         ::EnableWindow(hWndParent, TRUE);  
    101.     if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)  
    102.         ::SetActiveWindow(hWndParent);  
    103.     //::EndDialog仅仅关闭了窗口,现在销毁窗口  
    104.     DestroyWindow();  
    105.     PostModal();  
    106.   
    107.     // 必要的话,解锁/释放资源  
    108.     if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)  
    109.         UnlockResource(hDialogTemplate);  
    110.     if (m_lpszTemplateName != NULL)  
    111.         FreeResource(hDialogTemplate);  
    112.   
    113.     return m_nModalResult;  
    114. }  

    III.使用原对话框窗口过程作消息的缺省处理
       对话框的消息处理过程和其他窗口并没有什么不同。这里主要分析的是如何把一些消息传递给对话框原窗口过程处理。下面,通过解释MFC对WM_INITDIALOG消息的处理来解释MFC窗口过程

    和原对话框窗口过程的关系及其协调作用。MFC提供了WM_INITDIALOG消息的处理函数CDialog::HandleInitDialog,WM_INITDIALOG消息按照标准Windows的处理送给HandleInitDialog处理。

    HandleInitDialog调用缺省处理过程Default,导致CWnd的Default函数被调用。CWnd::Default的实现如下:

    1. LRESULT CWnd::Default()  
    2. {  
    3. // call DefWindowProc with the last message  
    4. _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
    5. return DefWindowProc(pThreadState->m_lastSentMsg.message,pThreadState->m_lastSentMsg.wParam,pThreadState->m_lastSentMsg.lParam);  
    6. }  

    顺便指出,从Default的实现可以看出线程状态的一个用途:它把本线程最新收到和处理的消息记录在成员变量m_lastSentMsg中。在Default的实现中,CWnd的DefWindowsProc被调用,其

    实现如下:

    1. LRESULT CWnd::DefWindowProc(UINT nMsg,WPARAM wParam, LPARAM lParam)  
    2. {  
    3.     //若“窗口超类(SuperClass)”的窗口过程m_pfnSuper非空,则调用它  
    4.     if (m_pfnSuper != NULL)  
    5.         return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);  
    6.   
    7.     //在MFC中,GetSuperWndProcAddr的作用就是返回m_pfnSuper,为什么还  
    8.     //要再次调用呢?因为虽然该函数现在是Obsolete,但原来曾经是有用的。  
    9.     //如果返回非空,就调用该窗口过程进行处理,否则,由Windows进行缺省处理。  
    10.     WNDPROC pfnWndProc;  
    11.     if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)  
    12.         return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);  
    13.     else  
    14.         return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);  
    15. }  

     

    综合上述分析,HandleInitDialog最终调用了窗口过程m_pfnSuper,即Windows提供给“对话框窗口类”的窗口过程,于是该窗口过程调用了对话框过程AfxDlgProc,导致虚拟函数

    OnInitDialog被调用。OnInitDialog的MFC缺省实现主要完成三件事情:
    调用ExecInitDialog初始化对话框中的控制;调用UpdateData初始化对话框控制中的数据;确定是否显示帮助按钮。所以,程序员覆盖该函数时,一定要调用基类的实现。
    MFC采用子类化的方法取代了对话框的窗口过程,原来SDK下对话框过程要处理的东西大部分转移给MFC窗口过程处理,如处理控制窗口的控制通知消息等。如果不能处理或者必须借助于原来的

    窗口过程的,则通过缺省处理函数Default传递给原来的窗口过程处理,如同这里对WM_INITDIALOG的处理一样。

     

    IV.Dialog命令消息和控制通知消息的处理
    通过覆盖CWnd的命令消息发送函数OnCmdMsg,CDialog实现了自己的命令消息发送路径

    1. BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo)  
    2. {  
    3.     //首先,让对话框窗口自己或者基类处理  
    4.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    5.         return TRUE;  
    6.   
    7.     //如果还未处理,且是控制通知消息或者状态更新消息或者系统命令则停止进一步的发送  
    8.     if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||!IS_COMMAND_ID(nID) || nID >= 0xf000)  
    9.         return FALSE; // not routed any further  
    10.   
    11.     //尝试给父窗口处理  
    12.     CWnd* pOwner = GetParent();  
    13.     if (pOwner != NULL)  
    14.     {  
    15.         ASSERT(pOwner != this);  
    16.         if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    17.         return TRUE;  
    18.     }  
    19.   
    20.     // 最后,给当前线程对象处理  
    21.     CWinThread* pThread = AfxGetThread();  
    22.     if (pThread != NULL)  
    23.     {  
    24.         if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
    25.         return TRUE;  
    26.     }  
    27.   
    28.     return FALSE;  
    29. }  

    从上述实现可以看出,CDialog处理命令消息遵循如下顺序:对话框自身→父窗口→线程对象
    例如,模式对话框产生的WM_ENTERIDLE消息就发送给父窗口处理。
    CDialog::OnCmdMsg不仅适用于模式对话框,也适用于无模式对话框。

     

    V.消息预处理和Dialog消息
       另外,对话框窗口的消息处理还有一个特点,就是增加了对Dialog消息的处理,如同在介绍::IsDialogMessage函数时所述。如果是 Dialog消息,MFC框架将不会让它进入下一步的消息循

    环。为此,MFC覆盖了CDialog的虚拟函数PreTranslateMessage,该函数的实现如下:

    1. BOOL CDialog::PreTranslateMessage(MSG* pMsg)  
    2. {  
    3.     ASSERT(m_hWnd != NULL);  
    4.   
    5.     //过滤tooltip messages  
    6.     if (CWnd::PreTranslateMessage(pMsg))  
    7.         return TRUE;  
    8.   
    9.     //在Shift+F1帮助模式下,不转换Dialog messages  
    10.     CFrameWnd* pFrameWnd = GetTopLevelFrame();  
    11.     if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)  
    12.         return FALSE;  
    13.   
    14.     //处理Escape键按下的消息  
    15.     if (pMsg->message == WM_KEYDOWN &&(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_CANCEL) &&  
    16.     (::GetWindowLong(pMsg->hwnd, GWL_STYLE) & ES_MULTILINE) &&_AfxCompareClassName(pMsg->hwnd, _T("Edit")))  
    17.     {  
    18.         HWND hItem = ::GetDlgItem(m_hWnd, IDCANCEL);  
    19.         if (hItem == NULL || ::IsWindowEnabled(hItem))  
    20.         {  
    21.             SendMessage(WM_COMMAND, IDCANCEL, 0);  
    22.             return TRUE;  
    23.         }  
    24.   
    25.     }  
    26.   
    27.     // 过滤来自控制该对话框子窗口的送给该对话框的Dialog消息  
    28.     return PreTranslateInput(pMsg);  
    29.   
    30. }  

    用到的函数:

    1. BOOL CWnd::PreTranslateMessage(MSG* pMsg)  
    2. {  
    3.     // handle tooltip messages (some messages cancel, some may cause it to popup)  
    4.     AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();  
    5.     if (pModuleState->m_pfnFilterToolTipMessage != NULL)  
    6.         (*pModuleState->m_pfnFilterToolTipMessage)(pMsg, this);  
    7.   
    8.     // no default processing  
    9.     return FALSE;  
    10. }  
    11.   
    12. BOOL CWnd::PreTranslateInput(LPMSG lpMsg)  
    13. {  
    14.     ASSERT(::IsWindow(m_hWnd));  
    15.   
    16.     // don't translate non-input events  
    17.     if ((lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST) &&  
    18.         (lpMsg->message < WM_MOUSEFIRST || lpMsg->message > AFX_WM_MOUSELAST))  
    19.         return FALSE;  
    20.   
    21.     return IsDialogMessage(lpMsg);  
    22. }  
    23.   
    24. BOOL CWnd::IsDialogMessage(LPMSG lpMsg)  
    25. {  
    26.     ASSERT(::IsWindow(m_hWnd));  
    27.   
    28.     if (m_nFlags & WF_OLECTLCONTAINER)  
    29.         return afxOccManager->IsDialogMessage(this, lpMsg);  
    30.     else  
    31.         return ::IsDialogMessage(m_hWnd, lpMsg);  
    32. }  

    根据IsDialogMessage(),对话框无法处理WM_KEYUP等消息,我们可以重载PreTranslateMessage()。PreTranslateMessage的实现不仅用于模式对话框,而且用于无模式对话框。

     

    VI.模式对话框的消息循环
        从DoModal的实现可以看出,DoModal调用CreateDlgIndirect创建的是无模式对话框,MFC如何来接管和控制应用程序的消息队列,实现一个模式对话框的功能呢?
    CDialog调用了RunModalLoop来实现模式窗口的消息循环。RunModalLoop是CWnd的成员函数,它和相关函数的实现如下:

    1. int CWnd::RunModalLoop(DWORD dwFlags)  
    2. {  
    3.   
    4.     ASSERT(::IsWindow(m_hWnd));   
    5.     BOOL bIdle = TRUE;  
    6.     LONG lIdleCount = 0;  
    7.     BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) &&!(GetStyle() & WS_VISIBLE);  
    8.     HWND hWndParent = ::GetParent(m_hWnd);  
    9.     m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);  
    10.     MSG* pMsg = &AfxGetThread()->m_msgCur;  
    11.   
    12.     //获取和派发消息直到模式状态结束  
    13.     for (;;)  
    14.     {  
    15.         ASSERT(ContinueModal());/  
    16.         //第一阶段,判断是否可以进行Idle处理  
    17.         while (bIdle &&!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))  
    18.         {  
    19.             ASSERT(ContinueModal());  
    20.   
    21.             //必要的话,当Idle时显示对话框窗口  
    22.             if (bShowIdle)  
    23.             {  
    24.                 ShowWindow(SW_SHOWNORMAL);  
    25.                 UpdateWindow();  
    26.                 bShowIdle = FALSE;  
    27.             }  
    28.   
    29.             //进行Idle处理,必要的话发送WM_ENTERIDLE消息给父窗口  
    30.             if (!(dwFlags & MLF_NOIDLEMSG) &&hWndParent != NULL && lIdleCount == 0)  
    31.             {  
    32.                 ::SendMessage(hWndParent, WM_ENTERIDLE,  
    33.                 MSGF_DIALOGBOX, (LPARAM)m_hWnd);  
    34.             }  
    35.   
    36.             //必要的话发送WM_KICKIDLE消息给父窗口  
    37.             if ((dwFlags & MLF_NOKICKIDLE) ||!SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))  
    38.             {  
    39.                 //终止Idle处理  
    40.                 bIdle = FALSE;  
    41.             }  
    42.         }  
    43.   
    44.         //第二阶段,发送消息  
    45.         do  
    46.         {  
    47.             ASSERT(ContinueModal());  
    48.             // 若是WM_QUIT消息,则发送该消息到消息队列,返回;否则发送消息。  
    49.             if (!AfxGetThread()->PumpMessage())  
    50.             {  
    51.                 AfxPostQuitMessage(0);  
    52.                 return -1;  
    53.             }  
    54.   
    55.             //必要的话,显示对话框窗口  
    56.             if (bShowIdle &&(pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))  
    57.             {  
    58.                 ShowWindow(SW_SHOWNORMAL);  
    59.                 UpdateWindow();  
    60.                 bShowIdle = FALSE;  
    61.             }  
    62.   
    63.             if (!ContinueModal())  
    64.             goto ExitModal;  
    65.   
    66.             //在派发了“正常 ”消息后,重新开始Idle处理  
    67.             if (AfxGetThread()->IsIdleMessage(pMsg))  
    68.             {  
    69.                 bIdle = TRUE;  
    70.                 lIdleCount = 0;  
    71.             }  
    72.         } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));  
    73.     }  
    74.   
    75.     ExitModal:  
    76.     m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);  
    77.     return m_nModalResult;  
    78. }  

         和CWinThread::Run的处理过程比较,RunModalLoop也分两个阶段进行处理。这里不同于Run的Idle处理,RunModalLoop是给父窗口发送WM_ENTERIDLE消息(如果需要的话);另外,当前对

    话框的父窗口被Disabled,是不接收用户消息的。RunModalLoop是一个实现自己的消息循环的示例,消息循环的条件是模式化状态没有结束。实现线程自己的消息循环见8.5.6节。当用户按下

    按钮“取消”、“确定”时,将导致RunModalLoop退出消息循环,结束对话框模式状态,并调用::EndDialog关闭窗口。
    OnOk、OnCancle、EndDialog都可以用来关闭对话框窗口。其中:
    OnOk首先进行数据交换,获取对话框中各个控制子窗口的数据,然后调用EndDialog结束对话框。
    OnCancle直接EndDialog结束对话框。

     

    三.数据交换
       对话框数据交换指以下两种动作,或者是把内存数据写入对应的控制窗口,或者是从控制窗口读取数据并保存到内存变量中。MFC为了简化这些操作,以CDataExchange类和一些数据交换函

    数为基础,提供了一套数据交换和校验的机制。
    相应的DoDataExchange的实现如下:

    1. void CExDialog::DoDataExchange(CDataExchange* pDX)  
    2. {  
    3.     CDialog::DoDataExchange(pDX);  
    4.     DDX_Control(pDX, IDC_NAME, m_name);  
    5.     DDX_Text(pDX, IDC_AGE, m_nAge);  
    6.     DDV_MinMaxInt(pDX, m_nAge, 1, 100);  
    7. }  

    DDX_ Control表示把IDC_NAME子窗口的内容传输到窗口m_name,或者相反。
    DDX_ Text表示把IDC_AGE子窗口的内容按整数类型保存到m_nAge,或者相反。
    DDV_MinMaxInt表示m_nAge应该在1和100之间取值

     

    1.CDataExchange类

    1. class CDataExchange  
    2. {  
    3. public:  
    4.   
    5.     BOOL m_bSaveAndValidate; // TRUE 则 保存和验证数据  
    6.     CWnd* m_pDlgWnd; // 指向一个对话框  
    7.   
    8.     HWND PrepareCtrl(int nIDC); //返回指定ID的控制窗口的句柄  
    9.     HWND PrepareEditCtrl(int nIDC); //返回指定ID的编辑控制窗口句柄  
    10.     void Fail(); // 用来扔出例外  
    11.   
    12.     CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate);  
    13.     HWND m_hWndLastControl; // last control used (for validation)  
    14.     BOOL m_bEditLastControl; // last control was an edit item  
    15. };  

    DoDataExchange类似于Serialize函数,CDataExchange类似于 CArchive。CDataExchange使用成员变量m_pDlgWnd保存要进行数据交换的对话框,使用成员变量 m_bSaveAndValidate指示数

    据传输的方向,如果该变量真,则从控制窗口读取数据到成员变量,如果假,则从成员变量写数据到控制窗口。
    在构造一个CDataExchange对象时,将保存有关信息在对象的成员变量中。构造函数如下:

    1. CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate)  
    2. {  
    3.     ASSERT_VALID(pDlgWnd);  
    4.     m_bSaveAndValidate = bSaveAndValidate;  
    5.     m_pDlgWnd = pDlgWnd;  
    6.     m_hWndLastControl = NULL;  
    7. }  

     

    2.数据交换和验证函数
    在进行数据交换或者验证时,首先使用PrePareCtrl或者PrePareEditCtrl得到控制窗口的句柄,然后使用::GetWindowsText从控制窗口读取数据,或者使用::SetWindowsText写入数据到控制

    窗口。下面讨论几个例子:

    1. static void AFX_CDECL DDX_TextWithFormat(CDataExchange* pDX,int nIDC,LPCTSTR lpszFormat, UINT nIDPrompt, ...)  
    2. {  
    3.     va_list pData; //用来处理个数可以变化的参数  
    4.     va_start(pData, nIDPrompt);//得到参数  
    5.   
    6.     //得到编辑框的句柄  
    7.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);  
    8.     TCHAR szT[32];  
    9.     if (pDX->m_bSaveAndValidate) //TRUE,从编辑框读出数据  
    10.     {  
    11.         //从编辑框得到内容  
    12.         ::GetWindowText(hWndCtrl, szT, _countof(szT));  
    13.         //转换编辑框内容为指定的格式,支持“ %d, %u, %ld, %lu”  
    14.         if (!AfxSimpleScanf(szT, lpszFormat, pData))  
    15.         {  
    16.             AfxMessageBox(nIDPrompt);  
    17.             pDX->Fail(); //数据交换失败  
    18.         }  
    19.     }  
    20.     else //FALSE,写入数据到编辑框  
    21.     {  
    22.         //把要写的内容转换成指定格式,不支持浮点运算  
    23.         wvsprintf(szT, lpszFormat, pData);  
    24.         //设置编辑框的内容  
    25.         AfxSetWindowText(hWndCtrl, szT);  
    26.     }  
    27.   
    28.     va_end(pData);//结束参数分析  
    29. }  
    30.   
    31. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)  
    32. {  
    33.     if (pDX->m_bSaveAndValidate)  
    34.         DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, &value);  
    35.     else  
    36.         DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, value);  
    37. }  

     

    3.UpdateData函数

    1. BOOL CWnd::UpdateData(BOOL bSaveAndValidate)  
    2. {  
    3.     ASSERT(::IsWindow(m_hWnd));   
    4.   
    5.     //创建CDataChange对象  
    6.     CDataExchange dx(this, bSaveAndValidate);  
    7.     DoDataExchange(&dx);  
    8.     //.................  
    9.     return bOK;  
    10. }  

     

    四,模式对话框的使用
    CFormView是MFC使用无模式对话框的一个典型例子。CFormView是基于对话框模板创建的视,它的直接基类是CSrcollView,CSrcollView的直接基类才是CView。
    这样不做详细介绍了

     

    展开全文
  • MFC对话框

    2013-06-30 10:19:32
    MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框。 对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。 模式对话框对象被定义后,...

    1、对话框基本要点和生命周期
    MFC对话框支持“所见即所得”编程模式。其类型分为模式对话框和非模式对话框。
    对话框由一个rc资源文件描述外观,通过ID与一个CPP类相连接,对话框内的控件使用基于ID的变量映射通讯。
    模式对话框对象被定义后,通过调用DoModal()函数来显示对话框并进行相关操作,此函数当对话框被关闭时返回。其返回值标明了对话框是点“确定”退 出,还是“取消”。非模式对话框需要与某个View相关联,以便对话框退出时发送消息给对应的Vew进行必要的处理。
    在对话框显示前,系统会调用OnInitDialog() 函数,在这个函数中你可以设置一些控件属性,进行一些初始化工作。比如,设置滚动条的最大最小值,设置List列表的初始值等。其方法是将控件ID作为参 数,调用GetDlgItem函数获得控件的对象的指针(指针类型是CWnd*),然后使用对象提供的函数进行操作。
    ID对于一个组件来说非常重要,通过向导,我们可以将一个变量和一个组件进行关联(映射)来实现数据交换,而这种绑定的关键就是将一个组件的ID与成员变量关联。

    2、数据交换机制
    控件是对话框的重要组成部分,控件的访问可以通过关联变量实现,包括关联数据变量和控制变量。MFC编程中,通过建立类向导中 的操作可以将窗口控件和对应变量绑定,但是代码操作的是变量,用户操作窗口控件如何让他们同步?UpdateData(Bool true|false)函数正是实现这个功能。

    DoDataExchange由框架类调用,用于交换和检验对话框的数据,该函数不直接调用,而是被UpdateData调用。通过update(TRUE)取得控件上的值,处理修改后通过update(FALSE)传回控件。

    UpdateData(TRUE) -- 刷新控件的值到对应的变量
    UpdateData(FALSE) -- 拷贝变量值到控件显示

    UpdataData()---用来刷新对话框

    3、特殊的Radio Button
    Radio Button控件是分组的,同一组的Radio Button只能有一个被选中。这个机制的实现依赖于TAB顺序,在资源视图下按Ctrl-D键将显示对话框的TAB焦点顺序。举一个例子来说明:
    Radio1、Radio2、Radio3是三个不同的Radio Button控件,其焦点顺序为1、2、3。为了实现分组Radio1的Group属性应该为TRUE,其余两个为FALSE。如果又有两个 Radio4、Radio5焦点顺序为6、7。则Radio4的Group属性应为TRUE,Radio4,Radio5被分为一组。
    需要注意的是,Radio以Group属性来分组,为了结束前一个组,你应该将焦点顺序为4、8的控件的Group属性设为TRUE,否则编译器会产生一个警告。

    4、一些技巧
    通过向导,我们可以将一个类成员变量和控件关联以进行数据交换,例如将一个CString类型的变量和Edit控件关联。将一个int变量和一组Radio Button关联。但是,人总有错的时候,当我们修改或需要删除这种关联时,麻烦就来了。
    在我的使用VS2005过程中没有发现提供了删除“已被关联的控件成员变量”的向导,所以我使用的是比较麻烦的手动删除。
    1)在对话框头文件中删除成员变量的定义
    2)在对话框cpp文件中删除构造函数初始化列表中的对应变量的初始化
    3)在对话框cpp文件中,根据变量名删除DoDataExchange函数中的对应语句
    此时,以class view中的向导中,已经可以重新设定控件所关联的成员变量了。

    登录框的制作:
    在显示主窗口之前显示一个模式对话框来提示用户登录一个常用的功能。只需要在PreCreateWindow函数中加入显示对话框的代码就可以完成这个功能。

          有些时候,我们可能需要从一个控件对象来得到它的ID。比如,你的对话框中好几个滚动条,那么这些滚动条的事件都在OnHScroll,OnVScroll中被响应。如何区分是哪个滚动条就需要确定ID。
    在这两个函数中有一个CScrollBar *pScrollBar指针,我们可以通过调用pScrollBar->GetDlgCtrllD()来获得ID,ID是一个整数。

    在对话框编程中往往需要改变某个控件的文字,比如EDIT控件和Static text控件。此时使用SetDlgItemText(int nID,LPCTSTR lpzString)函数比较方便。
    展开全文
  • Qt中调用MFC对话框

    2019-03-23 16:57:17
    QT是一个跨平台应用程序和UI开发框架。windows开Qt开发方法:一、Qt Creator+MinGW 或者 Qt Creator+VC++ Compiler二、QT4.8.5 for Windows(VS2015+VS...本实例为VS2015+Qt+Qt插件中开发Qt应用程序同时调用MFC对话框
  • MFC对话框中子窗口控件的使用

    热门讨论 2010-06-04 22:17:22
    MFC对话框中子窗口控件的使用MFC对话框中子窗口控件的使用MFC对话框中子窗口控件的使用MFC对话框中子窗口控件的使用
  • BCGControlBar MFC对话框换肤

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    系统环境:Windows 7
    软件环境:Visual Studio 2008 SP1
    本次目的:实现MFC对话框换肤

          在较新版本的BCGControlBar之中增加了对对话框和文档中窗体的form进行换肤。更改了一些控件的显示样式,其中有类似office 2007、office 2010 等等之类的皮肤。下面开始进行换肤,在此先要做好准备,必须安装有BCGControlBar(可以看这里的文章http://blog.csdn.net/akof1314/archive/2010/03/20/5399928.aspx

    首先:利用BCG向导 建立一个基于对话框的程序,然后删去不需要的文件,如下图所示:

    然后,因为我们这次要进行对话框换肤的界面为office 2007蓝色效果 ,所以我们在资源里面导入C:/Program Files/BCGSoft/BCGControlBarPro/BCGCBPro/Styles/BCGPStyle2007Aqua.rc,其他界面再对应加入即可。
    接着,在对话框的初始化函数OnInitDialog() 中,加入以下代码

    BOOL  CThe_Bcg_DialogDlg:: OnInitDialog()
    {
        CBCGPDialog:: OnInitDialog();
        //```````````
        CBCGPVisualManager2007:: SetStyle ( CBCGPVisualManager2007:: VS2007_LunaBlue);
        CBCGPVisualManager:: SetDefaultManager ( RUNTIME_CLASS ( CBCGPVisualManager2007));
        EnableVisualManagerStyle();
        return  TRUE ;   // return TRUE  unless you set the focus to a control
    }

    接着,只要在对话框上放置自己想要的控件即可。下面分别是不同系统上的效果。
    Windows 7系统下:

    Windows XP系统下:

    我们可以看到界面上的一些控件都变掉了,相对好看了些。但是在Windows XP下,标题栏显得特别不协调。如果想要在XP系统下也使标题栏达到换肤的话,请看下一篇《BCGControlBar MFC对话框换肤(续)

     

     

    2010年6月11日 补充:在后来的了解中,其实只在对话框的初始化函数OnInitDialog() 中,把其中的语句换成下面的

    EnableVisualManagerStyle(TRUE,TRUE);

    就可以实现对话框换肤,Windows XP效果如下:

               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • dll封装MFC对话框资源

    2015-12-11 16:41:44
    dll封装MFC对话框资源,博客中的源码工程。可以参看dll的资源封装以及使用。
  • 基于MFC对话框制作的调查问卷,包含单选、多选以及文本填写结果显示,并显示对话框背景。可用于Windows编程学习,挺好用的。
  • MFC对话框在Picture Control显示OpenGL
  • mfc对话框及控件缩放

    2014-10-08 17:30:25
    mfc对话框及控件缩放(控件随着对话框的大小成比例缩放)
  • 对话框的(上下/左右)滚动事件,比如,把一个比较大的对话框放入tab...下面我记录一下自己在使用MFC对话框自己带的垂直滚动条的过程。 (1)设置对话框的vertical scrollbar属性为true; (2)初始化滚动条,如下: v...

    对话框的(上下/左右)滚动事件,比如,把一个比较大的对话框放入tab控件的某一页时,就需要添加滚动条。在使用了java和qt等图形界面化的集成开发环境之后,再使用MFC,就会发现,想要让一个对话框滚动是多么麻烦的一件事情。下面我记录一下自己在使用MFC对话框自己带的垂直滚动条的过程。

      (1)设置对话框的vertical scrollbar属性为true;

      (2)初始化滚动条,如下:    

    复制代码
     
    void Dlg_WholeTest_Test::initScrollbar()
    {
    SCROLLINFO scrollinfo;
    GetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    scrollinfo.nPage=10; //设置滑块大小
    scrollinfo.nMax=75; //设置滚动条的最大位置0--75
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    }
     
    复制代码

      在OnInitDialog函数中调用该初始化函数即可。

      (3)重新实现OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)消息处理函数。

    复制代码
     
    void MyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)   //对话框的滚动效果
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    SCROLLINFO scrollinfo;
    GetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    int unit=3;
    switch (nSBCode)
    {
    case SB_LINEUP: //Scroll one line up
    scrollinfo.nPos -= 1;
    if (scrollinfo.nPos<scrollinfo.nMin)
    {
    scrollinfo.nPos = scrollinfo.nMin;
    break;
    }
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    ScrollWindow(0,unit);
    break;
    case SB_LINEDOWN: //Scroll one line down
    scrollinfo.nPos += 1;
    if (scrollinfo.nPos+scrollinfo.nPage>scrollinfo.nMax) //此处一定要注意加上滑块的长度,再作判断
    {
    scrollinfo.nPos = scrollinfo.nMax;
    break;
    }
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    ScrollWindow(0,-unit);
    break;
    case SB_PAGEUP: //Scroll one page up.
    scrollinfo.nPos -= 5;
    if (scrollinfo.nPos<=scrollinfo.nMin)
    {
    scrollinfo.nPos = scrollinfo.nMin;
    break;
    }
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    ScrollWindow(0,unit*5);
    break;
    case SB_PAGEDOWN: //Scroll one page down
    scrollinfo.nPos += 5;
    if (scrollinfo.nPos+scrollinfo.nPage>=scrollinfo.nMax) //此处一定要注意加上滑块的长度,再作判断
    {
    scrollinfo.nPos = scrollinfo.nMax;
    break;
    }
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    ScrollWindow(0,-unit*5);
    break;
    case SB_ENDSCROLL: //End scroll
    break;
    case SB_THUMBPOSITION: //Scroll to the absolute position. The current position is provided in nPos
    break;
    case SB_THUMBTRACK: //Drag scroll box to specified position. The current position is provided in nPos
    ScrollWindow(0,(scrollinfo.nPos-nPos)*unit);
    scrollinfo.nPos = nPos;
    SetScrollInfo(SB_VERT,&scrollinfo,SIF_ALL);
    break;
    }

    CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
    }
    展开全文
  • MFC对话框工具条添加组合框编辑框例子,可以自己按照这个操作,mfc对话框
  • 该示例演示MFC对话框的标题栏隐藏、显示和高度、边框的调整。
  • mfc对话框程序退出弹出确认对话框

    热门讨论 2013-08-11 00:15:18
    mfc对话框程序退出弹出确认对话框,最简单的范例,没有多余代码,就是相应了ON_WM_CLOSE()消息
  • MFC对话框里显示GIF动画图片
  • MFC 对话框封装Dll简单实现

    热门讨论 2014-08-22 09:18:35
    MFC 对话框封装Dll,简单实现,导出接口,导出Class类
  • mfc对话框添加IE浏览器
  • 更改MFC对话框的图标

    2012-12-28 17:36:39
    更改MFC对话框的图标 简单明了 一看就会
  • 有两个基于MFC的程序(相互独立),A程序和B程序,B程序只是一个mfc对话框程序,其中有调用mpi的函数,进行并行运算。 我是想通过在A程序中用createProcess(NULL,命令行,。。。)去创建B程序。在开启MPI之前,...
  • MFC对话框程序

    2016-04-04 15:19:01
    今天学习了MFC对话框程序设计,这里将一个小程序的步骤记录下来,是关于模态对话框和非模态对话框的。 1.创建一个基于对话框的项目,命名为Dialog; 2.添加两个对话框资源IDD_MODALDLG和IDD_NOMODALDLG 模态框...
  • MFC对话框背景图片改变代码

    热门讨论 2012-07-24 09:53:26
    主要把自己添加资源设置为MFC对话框显示背景
  • 这是一个集合了多个mfc对话框控件的程序,例如combo box,progress,list box等等,可以作为mfc对话框初学者较好的学习手册,上手快
  • 通过MFC对话框模拟实现的汉诺塔动画演示程序,代码简单、易懂,并有编写代码时留下的注释。方便初学者学习如何使用MFC对话框的部分常用控件。代码使用汉诺塔最常用的递归调用算法。
  • MFC 对话框程序返回值

    千次阅读 2016-10-24 14:47:52
    控制台程序可以很方便的获取到程序的返回值,MFC对话框程序返回值的获取就没有控制台程序获取那么方便了。 在MFC对话框程序中有两个大类,一个是应用程序类,一个是用于显示见面的对话框类。由于人和程序的交互是...
  • MFC对话框与静态HTML交互,关于MFC如何与WebBrowser加载的HTML进行交互的问题。。
  • 如何建立基于mfc对话框的窗口分割,实现对话框的窗口的分割 新手,求指导 谢谢
  • 简介:本人学习Qt一个星期了,利用Qt做的仿360界面,然后打包成动态库。 用C++的MFC对话框按钮调用Qt界面动态库,经测试成功。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,251
精华内容 6,900
关键字:

mfc对话框