随笔-19  评论-2  文章-0  trackbacks-0

2009-9-2

===========================
《深入解析MFC》笔记 5.对话框和控件类
===========================

CDialog: 模态MFC对话框 和 非模态MFC对话框
--------------------------------------------------------------------------   
    模态对话框
        从CDialog派生一个本地对象,调用 DoModal() 初始化、创建、显示并销毁对话框
       
    非模态对话框
        从堆上利用new 操作符创建,显示调用Create() 成员函数来显示对话框, 销毁 DestroyWindow()
       
    对话框操作
        NextDlgCtrl() 、PrevDlgCtrl() 或 GotoDlgCtrl() ,遍历对话框中的控件   
--------------------------------------------------------------------------

                        Win32 API
                · CreateDialog() —— 从模板资源创建一个非模态的对话框。
                · CreateDialogIndirect() —— 从模板指针创建一个非模态的对话框。
                · DialogBox() —— 从模板资源创建一个模态的对话框。
                · DialogBoxIndirect() —— 从模板指针创建一个模态的对话框。
        CDialog只抵用CreateDialogIndirect(),自己实现了模态化。
--------------------------------------------------------------------------
       
    CDialog的一些数据成员
        · m_nIDHelp —— 按钮的辅助ID,由模板ID确定
        · m_lpszTemplateName —— 资源模板的名字。若指定了ID,m_lpszTemplateName就是MAKELONG(0,ID).
        · m_hDialogTemplate —— 一旦装载之后,是资源模板句柄。
        · m_lpDialogInit —— 一个指向用来初始化对话框的数据的指针。用来初始化对话框的数据是从资源文件中装载的,通过一个资源ID与对话框相联系。
        · m_pParentWnd —— 一个指向父窗口或主窗口的CWnd指针。
        · m_hWndTop —— 最顶层的父窗口
        · m_pOccDialogInfo —— 存储OLE控件所需要的信息。
       
    CDialog的一些成员函数
        · virtual PreTranslateMessage() —— 为特殊情况过滤消息,如工具提示和SHIFT-F1环境帮助
        · virtual OnCmdMsg() ——  将命令消息路由给所有者和当前的 CWinThread 对象。忽略控件通知
        · virtual CheckAutoCenter() —— 检查用户是否已经指定了对话框自动位于中心
        · virtual SetOccDialogInfo  —— 用于设置 m_pOccDialogInfo。
        · virtual PreInitDialog() —— 在WM_INITDIALOG(OnInitDialog() )之前被调用。
        · PreModal() —— 通过准备将自己粘贴到已创建的对话框窗口上,为DoModal()逻辑准备CDialog
        · PostModal —— 在DoModal()逻辑之后将CDialog分离。
-------------
    CDialog的构造函数
        DLGCORE.CPP
       
    DoModal()       《深入解析MFC》 P155
        处理对华框的创建、显示和销毁
        · 首先载入对话框资源: 从资源文件中查找、载入并锁定对话框模版。无法定位资源时,返回-1.
        · 准备创建对话框: 先调用PreModal() 来执行安全检查并未对话框查找所需的父句柄,调用CWnd::GetSageOwner()来完成这些,并将结果存入m_hWnd中。
                                然后调用EnableWindow(hWndParent,FALSE)来讲对话框的父窗口禁用,强制实现了模态化。
        · 创建并显示对话框:
                        先通过调用AfxHookWindowCreate() 将一个MFC对象粘贴到对话框中,
                        然后调用 CWnd::CreateDlgIndirect(),该函数会做一些错误检查,然后设置对话框字体,再用 WF_CONTINUEMODAL
                对 CWnd::m_nFlags成员做 OR 操作,然后调用 ::CreateDialogIndirect(),参数是对话框模版、父窗口句柄 和 AfxDlgProc()
                (作为对话框过程)。返回TRUE或FALSE。
                        调用CWnd::RunModalLoop() 处理消息直至用户按下 ”OK“或”Cancel“,OnOK() 和 OnCancel() 调用 EndDialog(),EndDialog() 调用
                CWnd::EndModalLoop(),这样就结束了loop处理过程,控制流转给DoModal()。
                        调用 SetWindowPos() 隐藏目前已经死掉的对话框,再调用 EnableWindow(hWndParent,TRUE)来激活父窗口。
        · 收尾:
                调用DestroyWindow()来销毁Windows对话框对象,然后调用PostModal() 将第一步中粘贴到Windows对象的C++对象分离,设置m_hWndTop为NULL
               
    CDialog控件的初始化
        WM_INITDIALOG
            WM_INITDIALOG映射到CDIalog::HandleInitDialog().HandleInitDialog()执行OLE控件的一些初始化,从而调用CDialog::OnInitDialog()。
            OnInitDialog()调用CWnd::ExecuteDlgInit(),由后者完成最终对话框的初始化。
           
----------------------------------------------------------------------------------------------------------------
    DDX/DDV: CDialog数据交换与验证               P160
   
        CDataExchange 成员函数
            · 构造函数 —— 传入一个对话框指针和 bSaveAndValidate的初始化设置。
            · PrepareCtrl() —— 为DDX/DDV准备一个非编辑控件。通常这个函数会调用内部对话框指针 m_pDlgWnd 的 GetDlgItem() 方法
            · PrepareEditCtrl() —— 为DDX/DDV 准备一个编辑控件。首先调用PrepareCtrl(),然后将m_bEditLastControl设置为TRUE
            . Fail() —— 如果验证失败就调用这个函数。将焦点设置到前一个控件,并抛出CUserException异常。
            · PrepareOleCtrl() —— 为DDX/DDV 准备一个OLE控件。
           
            · m_bSaveAndValidate —— 指定了DDX的方向,以及是否应该做 DDV ,FALSE时,数据从成员变量流到控件。
            · m_pDlgWnd —— 一个指向对话框的CWnd指针,它包含了需要交换和验证的控件。这个值通过CDataExchange构造函数传入。
            · m_hWndLastControl —— 记录了前一个控件的句柄。
            · m_bEditLastControl —— 一个Boolean变量,指定了前一个控件是否是编辑器。
           
        DDX_Text()
            ① 先调用 PrepareEditCtrl() 或 PrepareCtrl()。
            ② 然后DDX_Text() 检查 Boolean 变量 m_bSaveAndValidate的值,
            ③ 若标志为TRUE(数据从控件流向成员变量),调用 Win32API(如 ::GetWindowText()),从编辑控件获得字符串,将字符串放在"value"中
                若为FALSE,调用 AfxSetWindowText(),使用CString参数的内容更新编辑控件。
               
        DDV_MaxChars()
                检查 CDataExchange::m_bSaveAndValidate 标志,确保为 TRUE(即现在正在进行验证)。然后根据指定的大小检查字符串的长度。
           
------------------------------------------------------------------------------------------------
    CWnd::UpdateData()
        ①· 首先创建一个CDataExchange实例,将 this 作为CDialog指针传入,并继续传入bSaveAndValidate 标志(OnDialogInit()调用时为FALSE…)。
        ②· UpadteData() 会阻塞当前线程,使线程不能接收控件的消息。
        ③· 本地Boolean变量 bOK用来存储 DDX/DDV 代码的返回值。一旦UpdateData() 将 bOK设置为 FALSE,它会调用你的CDialog派生类的DoDataExchange方法,
            同时将CDataExchange实例作为参数传入。 bOK被设置为TRUE, 这个值最终返回给 OnUpdate(),表示当前交换和验证没有错误。
        ④· 出现CUserException异常时,调用这块代码,bOK被设置为FALSE,最终UpdateData会返回0;
        ⑤· 如果DDX/DDV处理过程中没有任何异常抛出,会报告已经发生的一系列错误,也有可能导致返回FALSE。
        ⑥· 重置通知窗口,从而按常规方法处理控件通知,最终返回FALSE 或 TRUE(依赖于DoDataExchange()的返回值 )。

========================================================================
    MFC公用对话框
    CColorDialog             让用户选择一种颜色
    CFileDialog                用于打开和保存文件
    CFindReplaceDialog   允许用户查找和替代项
    CFontDialog              提供了选择字体的对话框
    CPrintDialog              让用户选择打印机、页数、打印方向
   
----------------------------------------------------------------------------------
    属性页
        CPropertySheet         CPropertyPage
       
        1、创建多个对话框模板,使模板描述你想要的属性页中布置的控件和布局。
        2、为每个对话框模板创建CPropertyPage的一个派生类,在类中增加成员变量来存储属性页的状态。
        3a、对于一个模态的属性页,只要创建它的CPropertySheet实例,然后调用CPropertySheet::AddPage()将前两步创建的单个属性页加入到属性页中。
               再调用CDialog::DoModal()来显示属性页。
        3b、如果创建一个非模态的属性页,则创建的CPropertySheet派生类须增加一些东西一边能够关闭对话框,MFC不会自动增加OK/Apply/Cancel
               然后创建你的CPropertySheet派生类的一个实例,并调用它的Create()来显示非模态属性页。
              
        使用属性页公用控件
            首先,填写PROPSHEETHEADER结构的一些域,然后调用 ::PropertyPage(),以该结构为参数,负责创建属性页。
            创建完成后,属性页发送 WM_NOTIFY 消息:
                · PSN_APPLY —— Apply 按钮被按下
                · PSN_WIXZBACK —— 在向导模式下,用户按下了 Previous 按钮
                · PSN_WIZNEXT —— 在向导模式下,用户按下了 Next 按钮
                · PSN_WIZFINISH —— 在向导模式下,用户按下了 Finish 按钮
            若想响应刚提到的通知,可想属性页控件发送消息。
                · PSM_SETTILTLE —— 设置属性页的标题
                · PSM_ADDPAGE —— 在属性页中增加一个单页。消息的 lParam参数是一个指向已创建的属性单页(由CreatePropertySheetPage()创建)的句柄
                · PSM_REMOVEPAGE —— 在属性页中删除一个属性单页。
   
        CPropertySheet 内幕
            · CommonConstruct() —— 不同的CPropertySheet构造函数接受的参数传递给 CommonConstruct()。 首先设置了m_psh所指向的 PSH_PROPSHEETPAGE
                                    结构的 dwFlags 域,然后初始化了 m_psh 所指向结构的几个其他域。 并将 m_bStacked 设置为TRUE,m_bModeless设为FALSE。
            · EnableStackedTabs() —— 将CPropertySheet设置为“stacked”模式而不是“scrolled”模式。只修改了 m_bStacked 成员变量。
            · BuildPropPageArray() —— 负责将 m_pages 数组转化成 PROPSHEETPAGE 结构。
            · OnInitDialog() ——  1、若不是出于 向导 模式,将整个属性页传递出去,以免控件绑在一起。
                                             2、根据 m_bStacked 的值修改标签的风格。通过调用 EnableStackedTabs() 完成。
                                             3、若属性页为非模态,也不处于 向导  状态,OnInitDialog()就删除标准按钮。
                                             4、调用 CWnd::CenterWindow(),是属性页出于窗口正中央。
            · m_pages —— 一个指针数组,是 CPtrArray 的实例,保存了指向属性页的所有 CPropertyPage 类的指针。
            · m_strCaption —— 属性页的说明
            · m_pParentWnd —— 指向父窗口的指针
            · m_bStacked —— 一个Boolean 变量,记录了用户指定的标签风格是“stacked”还是“scrolled”。
            · m_bModeless —— 一个Boolean变量, 记录用户希望属性页是模态还是非模态。
           
            CPropertySheet::DoModal()             (DLGPROP.CPP)
                首先,调用 AfxDeferRegisterClass(),导致MFC调用 InitCommonControls()函数,该函数初始化了Windows 通用控件 DLL。
                然后调用 BuildPropPageArray(),
                然后禁用主窗口,强制实现模态化,调用 ::PropertySheet() 来创建并显示公用属性页控件。
                再调用 CWnd::RunModalLoop() 在模态化窗口中处理消息泵。完成后,销毁窗口,激活父窗口,然后返回 RunModalLoop() 的返回值。
                   tag: 直到 DoModal()(对于模态属性页)或Create() (对于非模态属性页)被调用,类都不能映射到一个Windows窗口句柄。
                          在::PropertySheet() 被调用后,Windows窗口句柄才被创建,并和 MFC CPropertySheet 对象绑定在一起。
          
            CPropertySheet::AddPage()      
                首先,在内部变量 m_pages(属性单页指针数组)中保持了CPropertyPage指针。
                检查 m_hWnd非空后,调用 ::CreatePropertySheetPage(),并发送 PSM_ADDPAGE 消息给属性页控件,从而让它将创建的属性单页加入。
               
            CPropertySheet::BuildPropPageArray()
                    首先,删除了 m_psh.ppsp 域所指向的数组,并将它设置为NULL,为新数组保留控件,新的数组来自内部 CPtrArray 数组 m_pages。
                    然后,为本地变量 ppsp分配了一个新数组(数组基类型是 PROPSHEETPAGES),并将 m_psh.ppsp 指向同一块内存。
                    遍历 m_pages数组中的所有 CPropertyPage。对于每个夜,它将CPropertyPage 的内部 PROPSHEETPAGE 结构拷贝到 PROPSHEETHEADER 数组
                ppsp中。拷贝结束后,调用_ChangePropPageFont() 来修改所有控件的字体,参数是一个指向对话框模版的指针,这样控件和属性页使用相同的字体。
                    所有页都放入了 m_psh.ppsp,就更新 m_psh.nPages 这个单页计数,以便和 m_pages 的大小相匹配。
           
            CProperSheet 通知
                CPropertySheet 将几个属性页控件通知映射为可覆盖的回调函数


================================================================
MFC控件类
   
    · AFXWIN2.INL —— 所有控件类的内嵌函数
    · WINCTRL1.CPP —— CStatic 、 CButton 、 CListBox 、CComboBox、CEdit和 CScrollBar
    · WINCTRL2.CPP —— CDragListBox。
    · WINCTRL3.CPP —— CCheckListBox
    · WINBTN.CPP —— CBitmapButton

 

 

posted on 2010-03-15 23:09 Euan 阅读(3743) 评论(0)  编辑 收藏 引用 所属分类: windows

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理