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 阅读(3727)
评论(0) 编辑 收藏 引用 所属分类:
windows