友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第84部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
面那样自己动手。
MFC 的方式就简单多了。它提供的DDX (X 表示eXchange ),允许程序员事先设定控
制组件与变量的对应关系。我们不但可以令控制组件的内容一有改变就自动传送到变量
去,也可以藉MFC 提供的DDV (V 表示Validation )设定字段的合理范围。如果使用
617
…………………………………………………………Page 680……………………………………………………………
第篇 深入 MFC 程式設計
者在字段上键入超出合理范围的数字,就会在按下【OK 】后出现类似以下的画面:
资料的查核(Data Validation )其实是一件琐碎又耗人力的事情,各式各样的资料都应该
要检查其合理范围,程序才算面面俱到。例如日期字段绝不能允许12 以上的月份以及
31 以上的日子(如果程序还能自动检查2月份只有28 天而遇闰年有29 天那就更棒了);
金额字段里绝不能允许文字出现,电话号码字段一定只有9位(至少台湾目前是如此)。
为了解决这些琐碎又累人的工作,市售有一些链接库,专门做资料查核工作。
然而不要对MFC 的DDV 能力期望过高,稍后你就会看到,它只能满足最低层次的要
求而已。就DDV 而言,Borland 的OWL 表现较佳。
现在我打算以两个成员变量映射到对话框上的两个Edit 字段。我希望当使用者按下
【OK 】钮,第一个Edit 字段的内容自动储存到m_nThinWidth 变量中,第二个Edit 栏
位的内容自动储存到m_nThickWidth 变量中:
IDD_PEN_WIDTHS DIALOG 。。。 class CPenWidthsDlg : public CDialog
IDD_PEN_WIDTHS DIALOG 。。。 class CPenWidthsDlg : public CDialog
BEGIN {
BEGIN {
EDITTEXT IDC_THIN_PEN_WIDTH; 。。。 public:
EDITTEXT IDC_THIN_PEN_WIDTH; 。。。 public:
EDITTEXT IDC_THICK_PEN_WIDTH;。。。 int m_nThinWidth;
EDITTEXT IDC_THICK_PEN_WIDTH;。。。 int m_nThinWidth;
。。。 int m_nThickWidth;
。。。 int m_nThickWidth;
END };
END };
618
…………………………………………………………Page 681……………………………………………………………
10 MFC
第 章 與對話盒
下面是ClassWizard 的操作步骤(为对话框类别增加两个成员变量,并设定DDX / DDV):
进入ClassWizard,选择【Member Variables 】附页,再选择CPenWidthsDlg。
对话框中央部份有一大块区域用来显示控制组件与变量间的对映关系(见下一
页图)。
选择IDC_THIN_PEN_ WIDTH,按下【Add Variable。。。 】钮,出现对话框如下。
键入变量名称为m_nThinWidth 。
选择变量型别为int 。
按下【OK 】键, 于是ClassWizard 为CPenWidthsDlg 增加了一个变量
m_nThinWidth 。
在ClassWizard 对话框最下方(见下一页图)填入变量的数值范围,以为DDV
之用。
重复前述步骤,为IDC_THICK_PEN_WIDTH 也设定一个对应变量,范围也是
1~20。
619
…………………………………………………………Page 682……………………………………………………………
第篇 深入 MFC 程式設計
上述动作影响我们的程序代码如下:
class CPenWidthsDlg : public CDialog
{
// Dialog Data
//{{AFX_DATA(CPenWidthsDlg)
enum { IDD = IDD_PEN_WIDTHS };
int m_nThinWidth;
int m_nThickWidth;
//}}AFX_DATA
。。。
CPenWidthsDlg::CPenWidthsDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPenWidthsDlg::IDD; pParent)
{
m_nThickWidth = 0;
m_nThinWidth = 0;
。。。
}
void CPenWidthsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPenWidthsDlg)
620
…………………………………………………………Page 683……………………………………………………………
10 MFC
第 章 與對話盒
DDX_Text(pDX; IDC_THIN_PEN_WIDTH; m_nThinWidth);
DDV_MinMaxInt(pDX; m_nThinWidth; 1; 20);
DDX_Text(pDX; IDC_THICK_PEN_WIDTH; m_nThickWidth);
DDV_MinMaxInt(pDX; m_nThickWidth; 1; 20);
//}}AFX_DATA_MAP
}
只要资料「有必要」在成员变量与控制组件之间搬移,Framework 就会自动调用
DoDataExchange 。我所说的「有必要」是指,对话框初次显示在屏幕上,或是使用者按
下【OK 】离开对话框等等。CPenWidthsDlg::DoDataExchange 由一组一组的DDX/DDV
函数完成之。先做DDX ,然后做DDV,这是游戏规则。如果你纯粹借助ClassWizard,
就不必在意此事,如果你要自己动手完成,就得遵循规则。
该是完成上一节的OnDefaultPenWidths 的时候了。当【Default 】钮被按下,Framework
会调用OnDefaultPenWidths,我们应该在此设定粗笔细笔两种宽度的默认值:
void CPenWidthsDlg::OnDefaultPenWidths()
{
m_nThinWidth = 2;
m_nThickWidth = 5;
UpdateData(FALSE); // causes DoDataExchange()
// bSave=FALSE means don't save from screen;
// rather; write to screen
}
MFC 中各式各样的DDx_ 函数
如果你以为MFC 对于对话框的照顾,只有DDX 和DDV,那你就又错了,另外还有
一个DDP ,使用于OLE Custom Control (也就是OCX )的Property page 中,下面是它
的形式:
//{{AFX_DATA_MAP(CSmilePropPage)
DDP_Text(pDX; IDC_CAPTION; m_caption; _T(〃Caption〃) );
DDX_Text(pDX; IDC_CAPTION; m_caption);
DDP_Check(pDX; IDC_SAD; m_sad; _T(〃sad〃) );
DDX_Check(pDX; IDC_SAD; m_sad);
//}}AFX_DATA_MAP
621
…………………………………………………………Page 684……………………………………………………………
第篇 深入 MFC 程式設計
什么是Property page ?这是最新流行(Microsoft 强力推销?)的接口。这种接口用来解
决过于拥挤的对话框。ClassWizard 就有四个Property page ,我们又称为tag (附页)。
拥有property page 的对话框称为property sheet ,也就是tagged dialog (带有附页的对话
盒)。
如何唤起对话框
【Pen Widths 】对话框是一个所谓的Modal 对话框,意思是除非它关闭(结束),否则
它会紧抓住这个程序的控制权,但不影响其它程序。相对于Modal 对话框,有一种
Modeless 对话框就不会影响程序其它动作的进行;通常你在文字处理软件中看到的文字
搜寻对话框就是Modeless 对话框。
过去,MFC 有两个类别,分别负责Modal 对话框和Modeless 对话框,它们是
CModalDialog 和CDialog。如今已经合并为一,就是CDialog。不过为了回溯兼容,
MFC 有这么一个定义:
#define CModalDialog Cdialog
要做出Modal 对话框,只要调用CDialog::DoMoal 即可。
我们希望Step3 的命令项【Pen/Pen Widths 】被按下时,【Pen Widths 】对话框能够执行
起来。要唤起此一对话框,得做到两件事情:
1。 产生一个CPenWidthsDlg 对象,负责管理对话框。
2。 显示对话框窗口。这很简单,调用DoMoal 即可办到。
为了把命令消息连接上CPenWidthsDlg,我们再次使用ClassWizard,这一次要为
CScribbleDoc 加上一个命令处理例程。为什么选择在CScribbleDoc 而不是其它类别中
处理此一命令呢?因为不论是粗笔或细笔,乃至于目前正使用的笔,其宽度都被记录在
CScribbleDoc 中成为它的一个成员变量:
622
…………………………………………………………Page 685……………………………………………………………
10 MFC
第 章 與對話盒
// in SCRIBDOC。H
class CScribbleDoc : public CDocument
{
protected:
UINT m_nPenWidth; // current user…selected pen width
UINT m_nThinWidth;
UINT m_nThickWidth;
。。。
}
所以由CScribDoc 负责唤起对话框,接受笔宽设定,是很合情合理的事。
如果命令消息处理例程名为OnPenWidths,我们希望在这个函数中先唤起对话框,由对
话盒取得粗笔和细笔的宽度,然后再把这两个值设定给CScribbleDoc 中的两个对应变
数。下面是设计步骤。
执行ClassWizard,选择【Message Map 】附页,并选择CScribbleDoc。
在【Object IDs 】清单中选择ID_PEN_ WIDTHS。
在【Messages 】清单中选择MAND 。
按下【Add Function 】钮并接受ClassWizard 给予的函数名称OnPenWidths。
623
…………………………………………………………Page 686……………………………………………………………
第篇 深入 MFC 程式設計
按下【Edit Code 】钮,光标落在OnPenWidths 函数内,键入以下内容:
// SCRIBDOC。CPP
#include 〃pendlg。h〃
。。。
void CScribbleDoc::OnPenWidths()
{
CPenWidthsDlg dlg;
// Initialize dialog data
dlg。m_nThinWidth = m_nThinWidth;
dlg。m_nThickWidth = m_nThickWidth;
// Invoke the dialog box
if (dlg。DoModal() == IDOK)
{
// retrieve the dialog data
m_nThinWidth = dlg。m_nThinWidth;
m_nThickWidth = dlg。m_nThickWidth;
// Update the pen that is used by views when drawing new strokes;
// to reflect the new pen width definitions for 〃thick〃 and 〃thin〃。
ReplacePen();
}
}
现在,Scribble Step3 全部完成,制作并测试之。
624
…………………………………………………………Page 687……………………………………………………………
10 MFC
第 章 與對話盒
本章回顾
上一章我们为Scribble 加上三个新的菜单命令项。其中一个命令项【Pen/Pen Widths。。。 】
将引发对话框,这个目标在本章实现。
制作对话框,我们需要为此对话框设计模板(Dialog Template),这可藉Visual C++ 整
合环境之对话框编辑器之助完成。我们还需要一个衍生自CDialog 的类别(本例为
CPenWidthsDlg)。ClassWizard 可以帮助我们新增类别,并增加该类别的成员变量,以
及设定对话框之DDX/DDV 。以上都是透过ClassWizard 以鼠标点点选选而完成,过程
中不需要写任何一进程序代码。
所谓DDX 是让我们把对话框类别中的成员变量与对话框中的控制组件产生关联,于是
当对话框结束时,控制组件的内容会自动传输到这些成员变量上。
所谓DDV 是允许我们设定对话框控制组件的内容类型以及资料(数值) 范围。
对话框的写作,在MFC 程序设计中轻松无比。你可以尝试练习一个比较复杂的对话框。
625
…………………………………………………………Page 688……………………………………………………………
第篇 深入 MFC 程式設計
626
…………………………………………………………Page 689……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
11
第 章
View 功能之加強 与
重绘效率之提升
前面数章中,我们已经看到了View 如何扮演Document 与使用者之间的媒介:View 显
示Document 的资料内容,并且接受鼠标在窗口上的行为(左键按下、放开、鼠标移动),
视为对Document 的操作。
Scribble 可以对同一份Document 产生一个以上的Views ,这是MDI 程序的「天赋」
MDI 程序标准的【Window/New Window 】窗体项目就是为达此目标而设计的。但有一个
缺点还待克服,那就是你在窗口A的绘图动作不能实时影响窗口B,也就是说它们之间
并没有所谓的同步更新…即使它们是同一份资料的一体两面!
Scribble Step4 解决上述问题。主要关键在于想办法通知所有相同血源(同一份
Document )的各兄弟(各个Views ),让它们一起行动。但却因此必须考虑这个问题:
如果使用者的一个鼠标动作引发许多许多的程序绘图动作,那么「同步更新」的绘图效
率就变得非常重要。因此在考量如何加强显示能力时,我们就得设计所谓的「必要绘图
区」,也就是所谓的Invalidate Region,或称「不再适用的区域」或「重绘区」。每当使
用者增加新的线条,Scribble Step4 便把「包围该线条之最小四方形」设定为「必要绘图
区」。为了记录这项资料,从Step1 延用至今的Document 数据结构必须有所改变。
Step4 的同步更新,是以一笔画为单位,而非以一个点为单位。换句话说在一笔画未完
627
…………………………………………………………Page 690……………………………………………………………
第篇 深入 MFC 程式設計
成之前,不打算让同源的多个View 窗口同步更新…那毕竟太伤效率了。
Scribble Step4 的另一项改善是为Document Frame 窗口增加垂直和水平滚动条,并且示范
一种所谓的分裂窗口(Splitter window ),如图11…1。这种窗口的主要功能是当使用者
欲对文件做一体两面(或多面)观察时,各个观察子窗口可以集中在一个大的母窗口中。
在这里,子窗口被称为「窗口」(pane )。
图11…1 Scribble Step4 , 同源 ( 同一份Document ) 之各个View 窗口
具备同步更新的能力。Document Frame 窗口具备分裂窗口与卷轴。
628
…………………………………………………………Page 691……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
同時修改多个 : OnUpdate
Views UpdateAllViews 和
在Scribble View 上绘图,然后选按【Window/New Window 】,会蹦出另一个新的View ,
其内的图形与前一个View 相同。这两个Views 就是同一份文件的两个「观景窗」。新
窗口的产生导至WM_PAINT 产生,于是OnDraw 发生效用,把文件内容画出来:
图11…2 一份Document 连结两
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!