友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第86部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
利用 hint 增加重绘效率
预设情况下,OnUpdate 所收到的无效区(也就是重绘区),是Document Frame 窗口的
整个内部。但谁都知道,原已存在而且没有变化的图形再重绘也只是浪费时间而已。上
一节你已看到Scribble 每加上一整个线条, 就在OnLButtonUp 函数中调用
UpdateAllViews 函数,并且把整个线条(内含其四方边界)传过去,因此我们可以想办法
在OnUpdate 中重绘这个四方形小区域就好。
话说回来,如何能够只重绘一个小区域就好呢?我们可以一一取出Document 中每一线
条的四方边界,与新线条的四方边界比较,若有交点就重绘该线条。CRect 有一个
IntersectRect 函数正适合用来计算四方形交集。
但是有一点必须注意,绘图动作不是集中在OnDraw 吗?因此OnUpdate 和OnDraw 之
间的分工有必要厘清。前面数个版本中这两个函数的动作是:
637
…………………………………………………………Page 700……………………………………………………………
第篇 深入 MFC 程式設計
OnUpdate 啥也没做。事实上CScribbleView 原本根本没有改写此一函数。
OnDraw 迭代取得Document 中的每一线条,并调用CStroke::DrawStroke 将线条
绘出。
Scribble Step4 之中,这两个函数的动作如下:
OnUpdate 判断Framework 传来的hint 是否为CStroke 对象。如果是,设定
无效区域(重绘区域)为该线条的外围四方形;如果不是,设定无效区域为整
个窗口区域。「设定无效区域」( 也就是调用CWnd::InvalidateRect ) 会引发
WM_PAINT ,于是引发OnDraw 。
OnDraw 迭代取得Document 中的每一线条,并调用CStroke::GetBoundingRect
取线条之外围四方形,如果与「无效区域」有交集,就调用CStroke::DrawStroke
绘出整个线条。如果没有交集,就跳过不画。
以下是新增的OnUpdate 函数:
// in SCRIBVW。CPP
void CScribbleView::OnUpdate(CView* /* pSender */; LPARAM /* lHint */;
CObject* pHint)
{
// Document 通知View 说,某些资料已经改变了
if (pHint != NULL)
{
if (pHint…》IsKindOf(RUNTIME_CLASS(CStroke)))
{
// hint提示我们哪一线条被加入(或被修改),所以我们要把该线条的
// 外围四方形设为无效区。
CStroke* pStroke = (CStroke*)pHint;
CClientDC dc(this);
OnPrepareDC(&dc);
CRect rectInvalid = pStroke…》GetBoundingRect();
dc。LPtoDP(&rectInvalid);
InvalidateRect(&rectInvalid);
return;
}
}
//如果我们不能解释hint 内容(也就是说它不是我们所预期的
638
…………………………………………………………Page 701……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
对象),那就让整个窗口重绘吧(把整个窗口设为无效区)。
// CStroke
Invalidate(TRUE);
return;
}
为什么OnUpdate 之中要调用OnPrepareDC?这关系到滚动条,我将在介绍分裂窗口时再
说明。另,GetBoundingRect 动作如下:
CRect& GetBoundingRect() { return m_rectBounding; }
OnDraw 函数也为了高效能重绘动作之故,做了以下修改。阴影部份是与Scribble Step3
不同之处:
// SCRIBVW。CPP
void CScribbleView::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// 取得窗口的无效区。如果是在打印状态情况下,则取
// printer DC 的截割区(clipping region)。
CRect rectClip;
CRect rectStroke;
pDC…》GetClipBox(&rectClip);
//
// 注意:CScrollView::OnPrepare 已经在OnDraw 被调用之前先一步
// 调整了DC 原点,用以反应出目前的卷动位置。关于CScrollView,
// 下一节就会提到。
//调用CStroke::DrawStroke 完成无效区中各线条的绘图动作
CTypedPtrList& strokeList = pDoc…》m_strokeList;
POSITION pos = strokeList。GetHeadPosition();
while (pos != NULL)
{
CStroke* pStroke = strokeList。GetNext(pos);
rectStroke = pStroke…》GetBoundingRect();
if (!rectStroke。IntersectRect(&rectStroke; &rectClip))
continue;
pStroke…》DrawStroke(pDC);
}
}
639
…………………………………………………………Page 702……………………………………………………………
第篇 深入 MFC 程式設計
可卷动的窗口:CScrollView
到目前为止我们还没有办法观察一张比窗口还大的图,因为我们没有滚动条。
一个View 窗口没有滚动条,是很糟糕的事,因为通常Document 范围大而观景窗范围小。
我们不能老让Document 与View 窗口一样大。一个具备滚动条的View 窗口更具有「观
景窗」的意义。
Document
電腦資訊 ,是個洠в袀惱淼念I域 。「洠в袀惱怼沟囊馑迹褪谴蠹译S時可能歸零 ,任何永
IQ EQ
遠有機會爬到任何的頭。而切的成功,不是靠 ,是靠 。
View1
EQ 這個字眼最近引起大颍齽印N沂窃诖蠹s兩個月前與位讀友見面,從他口第次聽到的。
這位朋友是 ENT 執業醫師(ENT 者,ear/nose/throat 是也),他在專業以外的第素養,以及
追求新知的熱情,著實讓我感動。對於醫界沉痾的剴切直言,更讓執筆無責任書評的我,有吾
道不孤之感,呵呵。會拿起封了八個月的筆,與這位讀友的席談實有許多關係。
◆根魚刺 View2
葉醫師,我的這位讀友,對我提到最近的本奇特小書:資訊游俠列傳、無責任書評2、我的
電腦探索,並以種不勝幽然的口吻說:「看到這些年紀輕輕的小輩們頭角崢嶸,自己學個
Visual Basic 。。。
卻心力支絀,實在有莫大的打擊 」。侯捷是這本書的唯交集,本應遵循
「瓜田不迹模畈徽凇怪庞枺欢乙嘤懈~刺鯁在我的喉結裡。
這本書裡頭,資訊游俠列傳是個男對九個男的探索(別铡畷耍皇乔樯膶W);無
責任書評2是個「讀書懷獨行君子之節」的的技術評論與文關懷,非常私,十分另
類。我的電腦探索則是十個男對電腦資訊的深情告白。如果以結婚為分水嶺,劃分男與男
孩,那麼第本書其實是「個男與九個男孩」對電腦的深情告白。
。。。
图11…5a 一个具备滚动条的View 窗口更具 「观景窗」的意义
如果你有SDK 程序设计经验,你就会知道设计一个可卷动的窗口是多么烦琐的事(文
字的卷动还算好,图形的卷动更惨)。MFC 当然不可能对此一般性功能坐视不管,事实
上它已设计好一个CScrollView,其中的滚动条有实时卷动(边拉卷动杆边跑)的效果。
基本上要使View 窗口具备滚动条,你必须做到下列事情:
640
…………………………………………………………Page 703……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
Document
200
450
270
垂
目前垂直位置= 200
直
300 滚
目前水平位置 =270 动
条
水平頁大小= 450 范
900 围
900
水平滚动条范围 = 800
垂
直
頁
大
小
300
800
图11…5b 滚动条View 窗口与Document 之间的关系
定义Document 大小。如果没有大小,Framework 就没有办法计算滚动条尺寸,
以及卷动比例。这个大小可以是常数,也可以是个储存在每一Document 中的
变量,随着执行时期变动。
以CScrollView 取代CView。
只要Document 的大小改变,就将尺寸传给CScrollView 的SetScrollSizes 函
式。如果程序设定Document 为固定大小(本例就是如此),那么当然只要一
开始做一次滚动条设定动作即可。
注意装置坐标(窗口坐标)与逻辑坐标(Document 坐标)的转换。关于此点
稍后另有说明。
641
…………………………………………………………Page 704……………………………………………………………
第篇 深入 MFC 程式設計
Application Framework 对滚动条的贡献是:
■处理WM_HSCROLL 和WM_ VSCROLL 消息,并相对地卷动Document (其实
是移动View 落在Document 上的位置) 以及移动「滚动条拉杆」( 所谓的
thumb )。拉杆位置可以表示出目前窗口中显示的区域在整个Document 的位
置。如果你按下滚动条两端箭头,卷动的幅度是一行(line ),至于一行代表多
少,由程序员自行决定。如果你按下的是拉杆两旁的杆子,卷动的幅度是一页
(page ),一页到底代表多少,也由程序员自行决定。
向上卷动一行
向上卷动一页
垂直拉杆
向下卷动一页
向下卷动一行
水平拉杆 向右卷动一行
向左卷动一页
向右卷动一页
向左卷动一行
窗口一旦被放大缩小,立刻计算窗口的宽度高度与滚动条长度的比例,以重新设
定卷动比例,也就是一行或一页的大小。
以下分四个步骤修改Scribble 源代码:
1 定义Document 的大小。我们的作法是设定一个变量,代表大小,并在Document 初
始化时设定其值,此后全程不再改变(以简化问题)。MFC 中有一个CSize 很适合当
作此一变量类型。这个成员变量在文件进行文件读写(Serialization)时也应该并入文件
内容中。回忆一下,上一章讲到笔宽时,由于每一线条有自己的一个宽度,所以笔宽资
料应该在CStroke::Serialize 中读写,现在我们所讨论的文件大小却是属于整份文件的资
料,所以应该在CScribbleDoc::Serialize 中读写:
642
…………………………………………………………Page 705……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
// in SCRIBBLEDOC。H
class CScribbleDoc : public CDocument
{
protected:
CSize m_sizeDoc;
public:
CSize GetDocSize() { return m_sizeDoc; }
。。。
};
// in SCRIBBLEDOC。CPP
void CScribbleDoc::InitDocument()
{
m_bThickPen = FALSE;
m_nThinWidth = 2; // default thin pen is 2 pixels wide
m_nThickWidth = 5; // default thick pen is 5 pixels wide
ReplacePen(); // initialize pen according to current width
//预设Document 大小为800 x 900 个屏幕图素
m_sizeDoc = CSize(800;900);
}
void CScribbleDoc::Serialize(CArchive& ar)
{
if (ar。IsStoring())
{
ar 》 m_sizeDoc;
}
m_strokeList。Serialize(ar);
}
2 将CScribbleView 的父类别由CView 改变为CScrollView。同时准备改写其虚拟函数
OnInitialUpdate,为的是稍后我们要在其中,根据Document 的大小,设定卷动范围。
// in SCRIBBLEVIEW。H
class CScribbleView : public CScrollView
{
public:
virtual void OnInitialUpdate();
。。。
643
…………………………………………………………Page 706……………………………………………………………
第篇 深入 MFC 程式設計
};
// in SCRIBBLEVIEW。CPP
IMPLEMENT_DYNCREATE(CScribbleView; CScrollView)
BEGIN_MESSAGE_MAP(CScribbleView; CScrollView)
。。。
END_MESSAGE_MAP()
3 改写OnInitialUpdate,在其中设定滚动条范围。这个函数的被调用时机是在View 第
一次附着到Document 但尚未显现时,由Framework 调用之。它会调用OnUpdate,不
带任何Hint 参数(于是lHint 是0 而pHint 是NULL )。如果你需要做任何「只做一
次」的初始化动作,而且初始化时需要Document 的资料,那么在这里做就最合适了:
// in SCRIBVW。CPP
void CScribbleView::OnInitialUpdate()
{
SetScrollSizes(MM_TEXT; GetDocument()…》GetDocSize());
//这是CScrollView 的成员函数。
}
SetScrollSizes 总共有四个参数:
int nMapMode :代表映射模式(Mapping Mode)
SIZE sizeTotal:代表文件大小
const SIZE& sizePage :代表一页大小(预设是文件大小的1/10)
const SIZE& sizeLine :代表一行大小(预设是文件大小的1/100)
本例的文件大小是固定的。另一种比较复杂的情况是可变大小,那么你就必须在文件大
小改变之后立刻调用SetScrollSizes 。
窗口上增加滚动条并不会使View 的OnDraw 负担加重。我们并不因为滚动条把观察镜头移
动到Document 的中段或尾段,而就必须在OnDraw 中重新计算绘图原点与平移向量,
原因是绘图坐标与我们所使用的DC 有关。当滚动条移动了DC 原点,CScrollView 自动
644
…………………………………………………………Page 707……………………………………………………………
第 11 章 View 功能之加強與重繪效率之提昇
会做调整,让资料的某一部份显示而某一部份隐藏。
让我做更详细的说明。「GDI 原点」是DC (注)的重要特征,许许多多CDC 成员函
式的绘图结果都会受它的影响。如果我们想在绘图之前(也就是进入OnDraw 之前)调
整DC
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!