VC++讲义第16单元多文档视图结构Word下载.docx
《VC++讲义第16单元多文档视图结构Word下载.docx》由会员分享,可在线阅读,更多相关《VC++讲义第16单元多文档视图结构Word下载.docx(18页珍藏版)》请在冰豆网上搜索。
CreateNewFrame()为当前活动文档创建一个子窗口。
CreateNewFrame()不仅创建子窗口,还创建与文档相对应的视图。
与开发基于对话框的应用程序和SDI应用程序一样,使用AppWizard可生成一个MDI应用程序框架,在此基础上,程序员可使用ClassWizard和各种资源编辑器来充实自己的应用程序。
AppWizard为MDI程序框架创建了以下类:
类说明
CAboutDlg“关于”对话框
CChildFrame子框架窗口,用于容纳视图
CMyApp应用程序类
CmyDoc绘图程序视图类
CMyView绘图视图类
CMainFrame框架窗口(用来容纳子窗口),是MDI应用程序的主窗口
可以看出,MDI比SDI多了一个CchildFrame(子框架窗口)类,而且CMainFrame的职责也不同了。
另外,MDI和SDI的初始化应用程序实例方法上也有所不同。
MDI应用程序的InitInstance()函数代码为:
BOOLCDrawApp:
InitInstance()
{
……//初始化工作
CMultiDocTemplate*pDocTemplate;
//MDI文档模板
pDocTemplate=newCMultiDocTemplate(
IDR_DRAWTYPE,
RUNTIME_CLASS(CDrawDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CDrawView));
AddDocTemplate(pDocTemplate);
CMainFrame*pMainFrame=newCMainFrame;
//建立MDI主框架窗口
if(!
pMainFrame->
LoadFrame(IDR_MAINFRAME))
returnFALSE;
m_pMainWnd=pMainFrame;
m_pMainWnd->
DragAcceptFiles();
//设置框架窗口特性
EnableShellOpen();
RegisterShellFileTypes(TRUE);
CCommandLineInfocmdInfo;
//处理命令行
ParseCommandLine(cmdInfo);
ProcessShellCommand(cmdInfo))
ShowWindow(m_nCmdShow);
//显示主框架窗口
UpdateWindow();
returnTRUE;
}
注册文档模板时,首先创建一个CMultiDocTemplate类(对SDI是CSingleDocTemplate)的模板对象,然后用AddDocTemplate()把它加入到文档模板链表中去。
CmultiDocTemplate()构造函数有四个参数,第1个参数是文档使用的资源ID定义,第2个是文档类型,第3个是子窗口类型,第4个是视图类型。
与SDI不同,由于MDI的主框架窗口并不直接与文档相对应,因此无法通过创建文档来创建主框架窗口,而需要自己去创建:
[例16-1]绘图程序。
用户可以鼠标“拖曳”方式在视图中画直线段,线的粗细和颜色可调。
采用MDI结构,可同时打开多个子窗口作图,所作图形可以文件形式保存在磁盘上。
说明:
用AppWizard生成一个MDI程序框架,在第4步打开AdvancedOptions(高级选项)对话框,在DocumentTemplateStrings(文档模板字符串)选项卡中将Fileextension(文件扩展名)设置为“pic”,即该程序的图形文件名后缀为“.pic”。
其他选项均使用缺省设置。
编辑该程序的主菜单,添加一个“颜色”选项和一个“宽度菜单”,其中包括4个选项,其ID和Caption分别设置为:
IDCaption命令消息响应函数
ID_COLOR“颜色”OnColor()
ID_WIDTH1“宽度=1”OnWidth1()
ID_WIDTH3“宽度=3”OnWidth3()
ID_WIDTH5“宽度=5”OnWidth5()
ID_WIDTH7“宽度=7”OnWidth7()
用ClassWizard在视图类中为上述菜单选项建立相应的消息响应函数,以及几个宽度菜单选项相应的更新用户界面消息函数。
程序:
用ClassWizard添加一个用于描述线段的类,并为其添加代码:
classCLine:
publicCObject
public:
CPointm_pointFrom;
//线段起点
CPointm_pointTo;
//线段终点
COLORREFm_colorLine;
//线段颜色
intm_nWidth;
//线段宽度
CLine(){}
CLine(POINTfrom,POINTto,COLORREFcolor,intwidth);
CLine&
operator=(CLine&
line);
voidSerialize(CArchive&
ar);
voidDrawLine(CDC*pDC);
virtual~CLine(){}
DECLARE_SERIAL(CLine);
};
IMPLEMENT_SERIAL(CLine,CObject,1)
CLine:
CLine(POINTfrom,POINTto,COLORREFcolor,intwidth)
m_pointFrom=from;
m_pointTo=to;
m_colorLine=color;
m_nWidth=width;
CLine&
CLine:
operator=(CLine&
line)
m_pointFrom=line.m_pointFrom;
m_pointTo=line.m_pointTo;
m_colorLine=line.m_colorLine;
m_nWidth=line.m_nWidth;
return*this;
voidCLine:
Serialize(CArchive&
ar)
if(ar.IsStoring())
ar<
<
m_pointFrom<
m_pointTo<
m_colorLine<
m_nWidth;
else
ar>
>
m_pointFrom>
m_pointTo>
m_colorLine>
DrawLine(CDC*pDC)
CPenpenNew,*ppenOld;
penNew.CreatePen(PS_SOLID,m_nWidth,m_colorLine);
ppenOld=pDC->
SelectObject(&
penNew);
pDC->
MoveTo(m_pointFrom);
pDC->
LineTo(m_pointTo);
SelectObject(ppenOld);
修改文档类头文件。
在文件首部添加如下代码:
#include"
line.h"
#defineMAX_LINES300
并在文档类定义中添加如下数据成员:
CLinem_lineList[MAX_LINES];
intm_nCount;
然后利用ClassWizard为文档类重载成员函数DeleteContents()。
修改该函数及文档类的Serialize()函数:
voidCMyDoc:
DeleteContents()
m_nCount=0;
CDocument:
DeleteContents();
voidCMyDoc:
Serialize(CArchive&
ar)
if(ar.IsStoring())
ar<
m_nCount;
else
ar>
for(inti=0;
i<
m_nCount;
i++)
m_lineList[i].Serialize(ar);
修改视图类头文件,在视图类定义中添加如下数据成员:
COLORREFm_colorCurr;
//当前颜色
intm_nCurrWidth;
//当前线宽
BOOLm_bCaptured;
//是否按下鼠标左键
//当前线始端
//当前线终端
并在视图类构造函数中加入相应的初始化代码:
CMyView:
CMyView():
m_pointFrom(0,0),m_pointTo(0,0)
m_bCaptured=FALSE;
m_colorCurr=RGB(255,0,0);
m_nCurrWidth=3;
然后修改视图类的OnDraw()函数和消息响应函数:
voidCMyView:
OnDraw(CDC*pDC)
CMyDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
pDoc->
pDoc->
m_lineList[i].DrawLine(pDC);
OnLButtonDown(UINTnFlags,CPointpoint)
CView:
OnLButtonDown(nFlags,point);
m_pointFrom=m_pointTo=point;
SetCapture();
m_bCaptured=TRUE;
OnLButtonUp(UINTnFlags,CPointpoint)
OnLButtonUp(nFlags,point);
if(m_bCaptured)
{
CMyDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
m_pointTo=point;
ReleaseCapture();
pDoc->
m_lineList[pDoc->
m_nCount]=CLine(m_pointFrom,m_pointTo,
m_colorCurr,m_nCurrWidth);
m_nCount++;
SetModifiedFlag();
UpdateAllViews(this);
Invalidate();
}
OnMouseMove(UINTnFlags,CPointpoint)
OnMouseMove(nFlags,point);
CClientDCdc(this);
dc.SetROP2(R2_NOT);
dc.MoveTo(m_pointFrom);
dc.LineTo(m_pointTo);
m_pointTo=point;
dc.MoveTo(m_pointFrom);
dc.LineTo(m_pointTo);
OnColor()
CColorDialogdlg(m_colorCurr);
if(dlg.DoModal()==IDOK)
m_colorCurr=dlg.GetColor();
OnWidth1()
m_nCurrWidth=1;
OnWidth3()
m_nCurrWidth=3;
OnWidth5()
m_nCurrWidth=5;
OnWidth7()
m_nCurrWidth=7;
OnUpdateWidth1(CCmdUI*pCmdUI)
pCmdUI->
SetCheck(m_nCurrWidth==1);
OnUpdateWidth3(CCmdUI*pCmdUI)
SetCheck(m_nCurrWidth==3);
OnUpdateWidth5(CCmdUI*pCmdUI)
SetCheck(m_nCurrWidth==5);
OnUpdateWidth7(CCmdUI*pCmdUI)
SetCheck(m_nCurrWidth==7);
输入输出:
在窗口客户区按下鼠标左键后移动鼠标(“拖曳”)可显示一条始端不变、终端移动的黑色细线段,放松鼠标按键后,该线变为预先确定的颜色和粗细。
使用菜单选项可改变线段的颜色和粗细(图16-1)。
图16-1绘图程序
分析:
为了存储所绘图形,自定义的线段类CLine应可序列化。
为此,CLine类包含了一个没有参数的构造函数、一个重载的赋值运算符、Serialize()成员函数,以及DECLARE_SERIAL()宏和IMPLEMENT_SERIAL宏。
在文档类的Serialize()成员函数中调用了CLine类的Serialize()函数。
所有的鼠标消息和菜单消息响应函数均在视图类中。
其中用到了CDC类的成员函数SetROP2()在移动鼠标期间将绘图模式设置为异或,该模式下有两个特点,一是第2次画同一条线可恢复背景色(擦除),一是无论背景色和画线色的设置如何,均可保证所画线段可见,因此特别适合绘制变化图形。
但是,由于用户的鼠标可以在屏幕上任意移动。
当鼠标移出窗口外时,窗口无法收到鼠标消息。
此时,如果松开了鼠标左键,应用程序由于无法接受到该条消息而不会终止当前笔划,这样就造成了错误。
如何避免这种情况发生呢?
解决的办法是要让窗口在鼠标移出窗口外时仍然能接受到鼠标消息。
幸好,Windows提供了一个API函数SetCapture()解决了这一问题。
OnLButtonDown()中调用SetCapture()用于捕获鼠标,无论鼠标光标位置在何处,都会将鼠标消息送给调用它的那一个窗口。
在用完后,需要用ReleaseCapture()释放窗口对鼠标的控制,否则其他窗口将无法接收到鼠标消息(在OnLButtonUp()函数中)。
这样,即使用户在“拖曳”鼠标时越出当前窗口的客户区,也不会发生错误。
OnLButtonUp()函数中的语句
用于通知所有的视图数据已更新,这是MDI的特别用法。
OnUpdateWidth1()等函数用来在相应菜单选项前加上被选择标记。
自学内容
16.2滚动视图
到目前为止,我们接触到的程序的窗口客户区均受限于计算机屏幕的大小,而这对某些应用来说是不方便的。
例如,若要开发“所见即所得”的文本编辑或图形编辑程序,就希望屏幕上出现的文字或图形与打印机的输出大小接近。
在Windows程序中,这可以通过“滚动视图”技术实现,即在窗口客户区的右方和下方分别添加一个垂直滚动条和一个水平滚动条,使客户区变为一个更大的虚拟客户区的观察窗口。
MFC提供了CScrollView类来实现滚动视图。
CScrollView类自动处理滚动条消息并“滚动”客户区画面。
因此利用CScrollView类显示文档时,可以不必理会客户区的实际大小,只要将其当作一张很大的输出平面即可。
CScrollView类的OnPrepareDC()成员函数会根据水平和垂直滚动条的位置自动设定DC原点。
因此也可以说,客户区的左上角坐标其实是水平、垂直滚动条的位置,而两个滚动条的活动范围(scrollsize)就是虚拟客户区的大小。
CScrollView类有以下重要成员函数实现上述功能:
1.设置虚拟客户区的大小:
voidCScrollView:
SetScrollSizes(intnMapMode,SIZEsizeTotal,
constSIZE&
sizePage=sizeDefault,constSIZE&
sizeLine=sizeDefault);
其中参数nMapMode为映射模式,可参看10.3:
“GDI坐标系”。
参数sizeTotal是整个虚拟客户区的大小;
sizePage是每次“翻页”时的滚动量,也就是用户按下滚动条把柄时的滚动量。
sizeLine是每次跳一小格的滚动量,以上参数的单位均为逻辑坐标的单位。
2.取滚动条坐标(客户区左上角坐标)和虚拟客户区大小:
CPointCScrollView:
GetScrollPosition()const;
CSizeCScrollView:
GetTotalSize()const;
3.将客户区左上角滚动到指定坐标
ScrollToPosition(POINTpt);
其中参数pt为指定的客户区坐标。
4.取滚动位置和虚拟客户区大小的物理坐标
GetDeviceScrollPosition()const;
GetDeviceScrollSizes(int&
nMapMode,SIZE&
sizeTotal,
SIZE&
sizePage,SIZE&
sizeLine)const;
要使应用程序支持卷滚,可在用AppWizard生成框架程序时就指定视图的基类为CSrollView。
做法是在AppWizard的MFCAppWizard-Step6of6对话框中,在应用程序所包含的类中选择视图类,然后在BaseClass下拉列表框中选择应用程序视图类的基类为CScrollView。
如果要手工修改视图类的基类为CScrollView,可按以下步骤操作:
1.修改视图类所对应的头文件,将所有用到CView的地方改为CScrollView。
可以使用文本替换对话框(在编辑菜单中)中的替换功能,进行全局替换。
2.确定虚拟客户区的大小。
这项工作通常在视图派生类的OnInitialUpdate()成员函数或OnCreate()成员函数中通过调用SetScrollSizes()成员函数来完成。
3.如果在视图类的消息响应函数(如鼠标消息函数)中使用了CClientDC设备,则要注意该设备仍以实际客户区的左上角为原点,在存储有关数据时,可能要将其转换为虚拟客户区坐标。
转换方法很简单,只要将坐标值加上客户区左上角在虚拟客户区的坐标值即可。
然而,OnDraw()函数无需任何修改,因其使用的设备由CScrollView自动维护。
16.3对话视图
对于人事档案管理、名片管理、图书管理这类应用程序,视图的主要作用是显示各项文档资料,同时又要提供方便的修改手段。
如例15-1,采用对话框编辑档案材料比较方便。
然而,基于对话框的应用程序不提供文档类,数据存取不方便。
MFC提供了CFormView类,该类成员兼有对话框和视图的特点,最适合作文档管理类应用程序的用户界面。
要使应用程序支持CFormView类,可在用AppWizard生成框架程序时就指定视图的基类为