友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第67部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
#0168 // drag to the current point。
#0169 CPen* pOldPen = dc。SelectObject(GetDocument()…》GetCurrentPen());
#0170 dc。MoveTo(m_ptPrev);
#0171 dc。LineTo(point);
#0172 dc。SelectObject(pOldPen);
492
…………………………………………………………Page 555……………………………………………………………
第8章 Document…View 深入探討
#0173 m_ptPrev = point;
#0174 return;
#0175 }
View 的重绘动作:GetDocument 和OnDraw
以下是CScribbleView 中与重绘动作有关的成员变量和成员函数。
CScribbleView 的成员变量
m_pStrokeCur :一个指针,指向目前正在工作的线条。
m_ptPrev :线条中的前一个工作点。我们将在这个点与目前鼠标按下的点之间
画一条直线。虽说理想情况下鼠标轨迹的每一个点都应该被记录下来,但如果
鼠标移动太快来不及记录,只好在两点之间拉直线。
CScribbleView 的成员函数
OnDraw :这是一个虚拟函数,负责将Document 的数据显示出来。改写它是程
式员最大的责任之一。
GetDocument:AppWizard 为我们做出这样的码,以inline 方式定义于头文件:
inline CScribbleDoc* CScribbleView::GetDocument()
{ return (CScribbleDoc*)m_pDocument; }
其中m_pDocument 是CView 的成员变量。我们可以推测,当程序设定好Document
Template 之后,每次Framework 动态产生View 对象,其内的m_pDocument 已经被
Framework 设定指向对应之Document 了。
View 对象何时被动态产生?答案是当使用者选按【File/Open 】或【File/New 】。每
当产生一个Document ,就会产生一组Document/View/Frame 「三口组」。
OnPreparePrinting,OnBeginPrinting,OnEndPrinting:这三个CView 虚拟函数将
用来改善打印行为。AppWizard 只是先帮我们做出空函数。第12 章才会用到
它们。
493
…………………………………………………………Page 556……………………………………………………………
第篇 深入 MFC 程式設計
我们来看看CView 之中居最重要地位的OnDraw,面对Scribble Document 的数据结构,
将如何进行绘图动作。为了获得资料,OnDraw 一开始先以GetDocument 取得Document
对象指针;然后以while 循环一一取得各线条,再调用CStroke::DrawStroke 绘图。想象
中绘图函数应该放在View 类别之内(绘图不正是View 的责任吗),但是DrawStroke 却
否!原因是把线条的资料和绘图动作一并放在CStroke 中是最好的包装方式。
void CScribbleView::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// The view delegates the drawing of individual strokes to
// CStroke::DrawStroke()。
CTypedPtrList& strokeList = pDoc…》m_strokeList;
POSITION pos = strokeList。GetHeadPosition ();
while (pos != NULL)
{
CStroke* pStroke = strokeList。GetNext (pos);
pStroke…》DrawStroke(pDC);
}
}
其中用到两个CObList 成员函数:
GetNext:取得下一个元素。
GetHeadPosition:传回串行之第一个元素的「位置」。传回来的「位置」是一
个类型为POSITION 的数值,这个数值可以被使用于CObList 的其它成员函数
中,例如GetAt 或SetAt 。你可以把「位置」想象是串行中用以标示某个节点
node )的指针。当然,它并不真正是指针。
(
494
…………………………………………………………Page 557……………………………………………………………
第8章 Document…View 深入探討
View 与使用者的交谈 (鼠标消息处理实例)
为了实现「以鼠代笔」的功能,CScribbleView 必须接受并处理三个消息:
BEGIN_MESSAGE_MAP(CScribbleView; CView)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
。。。
END_MESSAGE_MAP()
三个消息处理例程的的内容总括来说就是追踪鼠标轨迹、在窗口上绘图、以及调用CStroke
成员函数以修正线条内容……包括产生一个新的线条空间以及不断把坐标点加上去。三
个函数的重要动作摘记于下。这些函数的骨干及其在Messape Map 中的映射项目,不劳
我们动手,有ClassWizard 代劳。下一个小节我会介绍其操作方法。
void CScribbleView::OnLButtonDown(UINT nFlags; CPoint point)
{
// 当鼠标左键按下,
// 利用CScribbleDoc::NewStroke 产生一个新的线条空间;
// 利用CArray::Add 把这个点加到线条上去;
// 调用SetCapture 取得鼠标捕捉权(mouse capture);
// 把这个点记录为「上一点」(m_ptPrev);
}
void CScribbleView::OnMouseMove(UINT; CPoint point)
{
// 当鼠标左键按住并开始移动,
// 利用CArray::Add 把新坐标点加到线条上;
// 在上一点(m_ptPrev)和这一点之间画直线;
// 把这个点记录为「上一点」(m_ptPrev);
}
void CScribbleView::OnLButtonUp(UINT; CPoint point)
{
// 当鼠标左键放开,
// 在上一点(m_ptPrev)和这一点之间画直线;
// 利用CArray::Add 把新的点加到线条上;
495
// 调用ReleaseCapture() 释放鼠标捕捉权(mouse capture)。
}
…………………………………………………………Page 558……………………………………………………………
第篇 深入 MFC 程式設計
ClassWizard 的辅佐
前述三个CScribbleView 成员函数(OnLButtonDown,OnLButtonUp,OnMouseMove)是
Message Map 的一部份,ClassWizard 可以很方便地帮助我们完成相关的Message Map 设
定工作。
首先,选按【View/ClassWizard 】激活ClassWizard,选择其【Message Map 】附页:
在图右上侧的【Class Name 】清单中选择CScribbleView,然后在图左侧的【Object IDs 】
清单中选择CScribbleView,再在图右侧的【Messages 】清单中选择WM_LBUTTONDOWN ,
然后选按图右的【Add Function 】钮,于是图下侧的【Member functions 】清单中出现一
笔新项目。
然后,选按【Edit Code 】钮,文字编辑器会跳出来,你获得了一个OnLButtonDown 函数
空壳,请在这里键入你的程序代码:
496
…………………………………………………………Page 559……………………………………………………………
第8章 Document…View 深入探討
另两个消息处理例程的实作作法雷同。
Message Map 因此有什么变化呢?ClassWizard 为我们自动加上了三笔映射项目:
BEGIN_MESSAGE_MAP(CScribbleView; CView)
//{{AFX_MSG_MAP(CScribbleView)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
// Standard printing mands
ON_MAND(ID_FILE_PRINT; CView::OnFilePrint)
ON_MAND(ID_FILE_PRINT_DIRECT; CView::OnFilePrint)
ON_MAND(ID_FILE_PRINT_PREVIEW; CView::OnFilePrintPreview)
END_MESSAGE_MAP()
此外ScribbleView 的类别声明中也自动有了三个成员函数的声明:
class CScribbleView : public CView
{
。。。
// Generated message map functions
protected:
//{{AFX_MSG(CScribbleView)
afx_msg void OnLButtonDown(UINT nFlags; CPoint point);
afx_msg void OnLButtonUp(UINT nFlags; CPoint point);
afx_msg void OnMouseMove(UINT nFlags; CPoint point);
//}}AFX_MSG
。。。
};
497
…………………………………………………………Page 560……………………………………………………………
第篇 深入 MFC 程式設計
WizardBar 的辅佐
WizardBar 是Visual C++ 4。0 之后的新增工具,也就是文字编辑器上方那个有着【Object
IDs 】和【Messages 】清单的横杆。关于修改Message Map 这件事,WizardBar 可以取代
ClassWizard 这个大家伙。
首先,进入ScribbleViwe。cpp (因为我们确定要在这里加入三个鼠标消息处理例程),选
择WizardBar 上的【Object IDs 】为CScribbleView , 再选择【Messages 】为
_
WM LBUTTONDOWN ,出现以下画面:
回答Yes ,于是你获得一个OnLButtonDown 函数空壳,一如在ClassWizard 中所得。请
在函数空壳中输入你的程序代码。
Serialize :对象的文件读写
你可能对Serialization 这个名词感觉陌生,事实上它就是对象导向世界里的Persistence (永
续生存),只是后者比较抽象一些。对象必须能够永续生存,也就是它们必须能够在程
式结束时储存到文件中,并且在程序重新激活时再恢复回来。储存和恢复对象的过程在
MFC 之中就称为serialization。负责这件重要任务的,是MFC CObject 类别中一个名为
Serialize 的虚拟函数,文件的「读」「写」动作均透过它。
498
…………………………………………………………Page 561……………………………………………………………
第8章 Document…View 深入探討
如果文件内容是借着层层类别向下管理(一如本例),那么只要每一层把自己份内的工
作做好,层层交待下来就可以完成整份资料的文件动作。
Serialization 以外的文件读写动作
其实有时候我们希望在重重包装之中返璞归真一下,感受一些质朴的动作。在介绍
Serialization 的重重包装之前,这里给你一览文件实际读写动作的机会。
文件I/O 服务是任何操作系统的主要服务。Win32 提供了许多文件相关APIs :开档、
关文件、读文件、写文件、搜寻资料。。。。MFC 把这些操作都包装在CFile 之中。可想而知,它
必然有Open、Close、Read 、Write、Seek 。。。 等等成员函数。下面这段程序代码示范CFile 如
何读档:
char* pBuffer = new char'0x8000';
CFile file(〃mydoc。doc〃; CFile::modeRead); //
打开mydoc。doc 文件,使用只读模式。
UINT nBytesRead = file。Read(pBuffer; 0x8000); // 读取8000h 个字节到pBuffer 中。
上述程序片段中,对象file 的构造式将打开mydoc。doc 档。并且由于此对象产生于函数
的堆栈之中,当函数结束,file 的析构式将自动关闭mydoc。doc 档。
开文件模式有许多种,都定义在CFile (AFX。H )之中:
enum OpenFlags {
modeRead = 0x0000; //只读
modeWrite = 0x0001; // 唯写
modeReadWrite =0x0002; // 可读可写
sharepat = 0x0000;
shareExclusive = 0x0010; // 唯我使用
shareDenyWrite = 0x0020;
shareDenyRead =0x0030;
shareDenyNone =0x0040;
modeNoInherit =0x0080;
modeCreate = 0x1000; //产生新档(甚至即使已有相同名称之文件存在)
modeNoTruncate = 0x2000;
typeText = 0x4000; // typeText and typeBinary are used in
typeBinary = (int)0x8000 // derived classes only
};
typeText = 0x4000; // typeText and typeBinary are used in
typeBinary = (int)0x8000 // derived classes only
};
499
…………………………………………………………Page 562……………………………………………………………
第篇 深入 MFC 程式設計
再举一例,下面这段程序代码可将文件mydoc。doc 的所有文字转换为小写:
char* pBuffer = new char'0x1000';
CFile file(〃mydoc。doc〃; CFile::modeReadWrite);
DWORD dwBytesRemaining = file。GetLength();
UINT nBytesRead;
DWORD dwPosition;
while (dwBytesRemaining) {
dwPosition = file。GetPosition();
nBytesRead = file。Read(pBuffer; 0x1000);
::CharLowerBuff(pBuffer; nBytesRead);
file。Seek((LONG)dwPosition; CFile::begin);
file。Write(pBuffer; nBytesRead);
dwBytesRemaining …= nBytesRead;
}
delete'' pBuffer;
文件的操作常需配合对异常情况(exception )的处理,因为文件的异常情况特别多:档
案找不到啦、文件handles 不足啦、读写失败啦。。。。上一例加入异样情况处理后如下:
char* pBuffer = new char'0x1000';
try {
CFile file(〃mydoc。doc〃; CFile::modeReadWrite);
DWORD dwBytesRemaining = file。GetLength();
UINT nBytesRead;
DWORD dwPosition;
while (dwBytesRemaining) {
dwPosition = file。GetPosition();
nBytesRead = file。Read(pBuffer; 0x1000);
::CharLowerBuff(pBuffer; nBytesRead);
file。Seek((LONG)dwPosition; CFile::begin);
file。Write(pBuffer; nBytesRead);
dwBytesRemaining …= nBytesRead;
}
}
catch (CFileException* e) {
if (e…》cause == CFileException::fileNoteFound)
MessageBox(〃File not found〃);
else if (e…》cause == CFileException::tooManyOpeFiles)
MessageBox(〃File handles not enough〃);
else if (e…》cause == CFileException::hardIO)
500
…………………………………………………………Page 563……………………………………………………………
第8章 Document…View 深入探討
MessageBox(〃Hardware error〃);
else if (e…》cause == CFileException::diskFull)
MessageBox(〃Disk full〃);
else if (e…》cause == CFileException::badPath)
MessageBox(〃All or part of the path is invalid〃);
else
MessageBox(〃Unknown file error〃);
e…》Delete();
}
delete'' pByffer;
台面上的Serialize 动作
让我以Scribble 为例,向你解释台面上的(应用程序代码中可见的)serialization 动作。根
据图8…3 的数据结构,Scribble 程序的文件读写动作是这么分工的:
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!