友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第59部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
生成是怎么一回事儿,现在正是复习第3章的时候。我将在第8章带你实际看看
Document Template 的内部动作。
前面曾提到,我们在CMultiDocTemplate 构造式的第一个参数置入IDR_SCRIBTYPE,
代表RC 文件中的菜单(MENU )、图标(ICON )、字符串(STRING )三种资源,其中又
以字符串资源大有学问。这个字符串以'n' 分隔为七个子字符串,用以完整描述文件类型。七
个子字符串可以在AppWizard 的步骤四的【Advanced Options 】对话框中指定:
432
…………………………………………………………Page 495……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
RC 文件中的字符串资源
IDR_MAINFRAME ::
::
〃Scribble Step0〃
(
IDR_SCRIBTYPE 7 个 子 字 串 ):
((
n
Scribn
Scribn
Scribble Files (*。scb)n
。SCBn
Scribble。Documentn
Scrib Document
每一个子字符串都可以在程序进行过程中取得,只要调用CDocTemplate::GetDocString 并
在其第二参数中指定索引值(1~7)即可,但最好是以CDocTemplate 所定义的七个常数
代替没有字面意义的索引值。下面就是CDocTemplate 的7个常数定义:
// in AFXWIN。H
class CDocTemplate : public CCmdTarget
{
。。。
enum DocStringIndex
{
windowTitle; // default window title
docName; // user visible name for default document
fileNewName; // user visible name for FileNew
// for file based documents:
filterName; // user visible name for FileOpen
filterExt; // user visible extension for FileOpen
// for file based documents with Shell open support:
regFileTypeId; // REGEDIT visible registered file type identifier
regFileTypeName // Shell visible registered file type name
};
。。。
};
433
…………………………………………………………Page 496……………………………………………………………
第篇 湷觥 FC 程式設計
所以,你可以这么做:
CString strDefExt; strDocName;
pDocTemplate…》GetDocString(strDefExt; CDocTemplate::filterExt);
pDocTemplate…》GetDocString(strDocName; CDocTemplate::docName);
七个子字符串意义如下:
index 意义
1。 CDocTemplate::windowTitle 主窗口标题栏上的字符串。SDI 程序才需要指定它,MDI 程
式不需要指定,将以IDR_MAINFRAME 字符串为默认值。
2。 CDocTemplate::docName 文件基底名称(本例为〃Scrib〃)。这个名称再加上一个流
水号码,即成为新文件的名称(例如〃Scrib1〃)。如果此
字符串未被指定,文件预设名称为〃Untitled〃。
3。 CDocTemplate::fileNewName 文件类型名称。如果一个程序支持多种文件,此字符串将显
示在【File/New 】对话框中。如果没有指明,就不能够在
【File/New 】对话框中处理此种文件。本例只支持一种文
件类型,所以当你选按【File/New 】,并不会出现对话框。
第13 章将示范「一个程序支持多种文件」的作法(#743
页)。
4。 CDocTemplate::filterName 文件类型以及一个适用于此类型之万用过滤字符串
(wildcard filter string) 。本例为〃Scribble(*。scb)〃。这个字符串
将出现在【File Open 】对话框中的【List Files Of Type】列
示盒中。
5。 CDocTemplate::filterExt 文件档之扩展名,例如〃scb〃。如果没有指明,就不能够
在【File Open 】对话框中处理此种文件档。
6。 CDocTemplate::regFileTypeId 如果你以::RegisterShellFileTypes 对系统的登录数据库
(Registry )注册文件类型,此值会出现在
HKEY_CLASSES_ROOT 之下成为其子机码(subkey )并
仅供Windows 内部使用。如果未指定,此种文件类型就
7。 无法注册,鼠标拖放(drag and drop) 功能就会受影响。
CDocTemplate::regFileTypeName 这也是储存在登录数据库(Registry )中的文件类型名称,
并且是给人(而非只给系统)看的。它也会显示于程序中
用以处理登录数据库之对话框内。
434
…………………………………………………………Page 497……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
以下是Scribble 范例中各个字符串出现的位置:
1 CDocTemplate::windowTitle
3 出现在p。743 的【New】对话框中
2 CDocTemplate::docName
5 CDocTemplate::filterExt
4 CDocTemplate::filterName
7 CDocTemplate::regFileTypeName
6 CDocTemplate::regFileTypeId
我必须再强调一次,AppWizard 早已帮我们产生出这些字符串。把这些来龙去脉弄清楚,
只是为了知其所以然。当然,同时也为了万一你不喜欢AppWizard 准备的字符串内容,你
知道如何去改变它。
435
…………………………………………………………Page 498……………………………………………………………
第篇 湷觥 FC 程式設計
Scribble 的Document/View 设计
用最简单的一句话描述,Document 就是资料的体,View 就是资料的面。我们藉
CDocument 管理资料,藉Collections Classes (MFC 中的一组专门用来处理资料的类别)
处理实际的资料数据;我们藉CView 负责资料的显示,藉CDC 和CGdiObject 实际绘
图。人们常说一体两面一体两面,在MFC 中一体可以多面:同一份资料可以文字描述
之,可以长条图描述之,亦可以曲线图描述之。
Document/View 之间的关系可以图7…3 说明。View 就像一个观景器(我避免使用「窗口」
这个字眼,以免引起不必要的联想),使用者透过View 看到Document ,也透过View 改
变Document 。View 是Document 的外显接口,但它并不能完全独立,它必须依存在一个
所谓的Document Frame 窗口内。
一份Document 可以映射给许多个Views 显示,不同的Views 可以对映到同一份巨大
Document 的不同区域。总之,请把View 想象是一个镜头,可以观看大画布上的任何区
域(我们可以选用CScrollView 使之具备滚动条);在镜头上加特殊的偏光镜、柔光镜、十
字镜,我们就会看到不同的影像…虽然观察的对象完全相同。
资料的管理动作有哪些?读档和写档都是必要的,文件存取动作称为Serialization,由
Serialize 函数负责。我们可以(而且也应该)在CMyDoc 中改写Serialize 函数,使它符
合个人需求。资料格式的建立以及文件读写功能将在Scribble step1 中加入,本例(step0)
的CScribbleDoc 中并没有什么成员变量(也就是说容纳不了什么数据),Serialize 则简
直是个空函数:
436
…………………………………………………………Page 499……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
void CScribbleDoc::Serialize(CArchive& ar)
{
if (ar。IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
}
}
这也是我老说骨干程序啥大事也没做的原因。
除了文件读写,资料的显示也是必要的动作,资料的接受编辑也是必要的动作。两者都
由View 负责。使用者对Document 的任何编辑动作都必须透过Document Frame 窗口,
消息随后传到CView。我们来想想我们的View 应该改写哪些函数?
1。 当Document Frame 窗口收到WM_PAINT ,窗口内的View 的OnPaint 函数会被呼
叫,OnPaint 又调用OnDraw 。所以为了显示资料,我们必须改写OnDraw 。至于
OnPaint,主要是做「只输出到屏幕而不到打印机」的动作。有关打印机,我将
在第12 章提到。
2。 为了接受编辑动作,我们必须在View 类别中接受鼠标或键盘消息并处理之。
如果要接受鼠标左键,我们应该改写View 类别中的三个虚拟函数:
afx_msg void OnLButtonDown(UINT nFlags; CPoint point);
afx_msg void OnLButtonUp(UINT nFlags; CPoint point);
afx_msg void OnMouseMove(UINT nFlags; CPoint point);
上述两个动作在Scribble step0 都看不到,因为它是个啥也没做的程序:
void CScribbleView::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
437
…………………………………………………………Page 500……………………………………………………………
第篇 湷觥 FC 程式設計
主窗口的诞生
上一章那个极为简单的Hello 程序,主窗口采用CFrameWnd 类别。本例是MDI 风格,将
采用CMDIFrameWnd 类别。
构造MDI 主窗口,有两个步骤。第一个步骤是new 一个CMDIFrameWnd 对象,第二个
步骤是调用其LoadFrame 函数。此函数内容如下:
// in WNDFRM。CPP
BOOL CFrameWnd::LoadFrame(UINT nIDResource; DWORD dwDefaultStyle;
CWnd* pParentWnd; CCreateContext* pContext)
{
CString strFullString;
if (strFullString。LoadString (nIDResource))
AfxExtractSubString (m_strTitle; strFullString; 0); // first sub…string
if (!AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG))
return FALSE;
// attempt to create the window
LPCTSTR lpszClass = GetIconWndClass (dwDefaultStyle; nIDResource);
LPCTSTR lpszTitle = m_strTitle;
if (!Create (lpszClass; lpszTitle; dwDefaultStyle; rectDefault;
pParentWnd; MAKEINTRESOURCE(nIDResource); 0L; pContext))
{
return FALSE; // will self destruct on failure normally
}
// save the default menu handle
m_hMenuDefault = ::GetMenu(m_hWnd);
// load accelerator resource
LoadAccelTable (MAKEINTRESOURCE(nIDResource));
if (pContext == NULL) // send initial update
SendMessageToDescendants(WM_INITIALUPDATE; 0; 0; TRUE; TRUE);
return TRUE;
}
438
…………………………………………………………Page 501……………………………………………………………
第7章 簡單而完整:MFC 骨幹程式
BOOL CScribbleApp::InitInstance()
{
。。。。
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame…》LoadFrame (IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
。。。 CFrameWnd::LoadFrame
} CFrameWnd::LoadFrame
CFrameWnd::Create
CFrameWnd::Create
CWnd::CreateEx
::CreateWindowEx
::CreateWindowEx
触发WM_CREATE
BEGIN_MESSAGE_MAP(CMainFrame; CMDIFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame; CMDIFrameWnd)
ON_WM_CREATE()
ON_WM_CREATE()
。。。
。。。
END_MESSAGE_MAP()
END_MESSAGE_MAP()
CMainFrame::OnCreate
CMainFrame::OnCreate
窗口产生之际会发出WM_CREATE 消息,因此CMainFrame::OnCreate 会被执行起来,
那里将进行工具栏和状态列的建立工作(稍后描述)。LoadFrame 函数的参数(本例为
IDR_MAINFRAME ) 用来设定窗口所使用的各种资源, 你可以从前一页的
CFrameWnd::LoadFrame 源代码中清楚看出。这些同名的资源包括:
// defined in SCRIBBLE。RC
IDR_MAINFRAME ICON DISCARDABLE 〃resScribble。ico〃
IDR_MAINFRAME MENU PRELOAD DISCARDABLE { 。。。 }
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE { 。。。 }
STRINGTABLE PRELOAD DISCARDABLE
BEGIN
IDR_MAINFRAME 〃Scribble Step0〃
(这个字符串将成为主窗口的标题)
。。
END
这种作法(使用LoadFrame 函数)与第6章的作法(使用Create 函数)不相同,请注意。
439
…………………………………………………………Page 502……………………………………………………………
第篇 湷觥 FC 程式設計
工具栏和状态列的诞生 (Toolbar & Status bar)
工具栏和状态列分别由CToolBar 和CStatusBar 掌管。两个对象隶属于主窗口,所以我
们在CMainFrame 中以两个变量(事实上是两个对象)表示之:
class CMainFrame : public CMDIFrameWnd
{
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
。。。
};
主窗口产生之际立刻会发出WM_CREATE,我们应该利用这时机把工具栏和状态列建立
起�
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!