单文档:SDI
一个应用程序一次只能打开一个文档,如记事本,一个记事本窗口只能打开一个文档,新建时会覆盖之前的。
多文档:MDI
可打开多个,如word.
对话框、单文档、多文档区别
对话框模板适合于做交互界面
文档模板适合于做文件处理
多文档模板适合于做多文件处理
CWinAPP:应用程序对象,负责初始化程序(进程启动终止),保存视图与文档等对象间的关系,处理消息循环、将消息调度到需要的目标窗口,命令行参数。
CFrameWnd:框架窗口对象,提供一个应用程序主窗口。包含菜单,工具条,最大小化按钮,标题栏,系统菜单,视图的显示及位置等。许多默认为MFC应有的功能都是在该类中实现。
CDocument:存视图空白区数据。通常保存在它的成员变量中。
还可由函数GetFirstViewPosition()获得视图指针,对其操作
CView:视图对象。(显示文档类数据)类基类为CWnd,所以它具有CWnd所有功能如创建、移动、显示、隐藏窗口。CView可以接受所有windows消息,而CDocument不行。它实现文档类需要的功能,如可获得指向文档类的指针:GetDocument(),再获取文档类成员变量就获得数据了。
CDocTemplate:将独立的文档、视图、框架对象联系起来,每一种类型文档对应一个文档模板,它管理该类型所有文档。文档、视图、框架对象都由它创建。它实现文档类需要的功能。如建立保存文档,获取文档字符串GetDocString()等。
在应用程序类的InitInstance()中:p284
CSingleDocTemplate *tmp=new CSingleDocTemplate(…)
本质上是一个数组,保存:
一般用类向导定义,手动添加的话是在BEGIN_MASSAGE_MAP与END_MESSAGE_MAP宏之间添加ON_COMMAND(应用控件ID,CView::OnChange),第二个参数为添加消息映射的类的成员方法。
(1)WM_COMMAND消息:按视图、文档、模板、框架窗口、应用程序对象、默认的DefWindowProc传送,一旦一个对象接收了就不会再往后传。
(2)标准windows消息如键盘鼠标消息传给视图,大多其它消息传给框架窗口,文档对象和应用程序对象从不接收非命令消息。
编程实例:
(1)新建基于单文档的MFC项目,命名为SingleDocument。
其中SingleDocument.cpp处理CWinApp类
(2)为SingleDocumentDoc类添加成员变量CString m_str,用于存储显示文本。在类视图中添加。
(3)为实现。。。在SingleDocumentDoc的构造函数中初始化m_str为该字符串,为了使再新建一个文档也能出现该字符,在OnNewDocument中也定义m_str的值:
SingleDocumentDoc.cpp中:
CSingleDocumentDoc::CSingleDocumentDoc() noexcept
{
// TODO: 在此添加一次性构造代码
m_str = _T("您好,单文档界面的例程!");
}
BOOL CSingleDocumentDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: 在此添加重新初始化代码
// (SDI 文档将重用该文档)
m_str = _T("您好,单文档界面的例程!");
return TRUE;
}
(3)实现由对话框与用户交互,改变显示文本:
在资源视图中插入Dialog,为对话框添加类:基类为CDialog,命名为DlgInput,在其中添加编辑框,为编辑框控件添加相关联CString变量m_input,则m_inout的值为输入在编辑框中的值
在主菜单中编辑菜单项中添加一项菜单项改变文本,修改ID为ID_CHANGETEXT;为菜单项添加事件处理程序(右键)。消息类型为command,选择类SingleDocumentDoc
自动添加的消息映射:
BEGIN_MESSAGE_MAP(CSingleDocumentDoc, CDocument)
ON_COMMAND(ID_CHANGETEXT, &CSingleDocumentDoc::OnChangetext)
END_MESSAGE_MAP()
编辑该函数,进入SingleDocumentDoc.cpp,首先加入头文件"DlgInput.h",编辑处理函数OnChangeText():显示对话框,获取编辑框内容,更新视图。
void CSingleDocumentDoc::OnChangeText()
{
// TODO: 在此添加命令处理程序代码
DlgInput inputDlg; //创建一个CInputDlg类的对象inputDlg
if (inputDlg.DoModal() == IDOK) //显示对话框
{
m_str = inputDlg.m_input; //获取输入的字符串
UpdateAllViews(NULL); //更新视图
}
}
UpdateAllViews(NULL); 会调用SingleDocumentView类的OnDraw()函数,所以编辑SingleDocumentView.cpp中OnDraw()函数,主要是调用TextOut()函数,将获取到的对话框文本显示在视图区。
void CSingleDocumentView::OnDraw(CDC* pDC)
{
CSingleDocumentDoc* pDoc = GetDocument();//获得文本类对象
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
CRect rectClient;
GetClientRect(rectClient); // 获取当前客户区的指针
CSize sizeClient = rectClient.Size(); // 获取当前客户区的大小
CString str = pDoc->m_str; // 从文件中读取数据
CSize sizeTextExtent = pDC->GetTextExtent(str); // 用新选定的字体绘制字符串
pDC->TextOut((sizeClient.cx - sizeTextExtent.cx) / 2, (sizeClient.cy - sizeTextExtent.cy) / 2, str);
}
实现文档1显示文本,文档2绘图。
#define IDR_MYMDITYPE2 135
(2)手工加入第二个资源模板字符串
打开MyMdi.rc找到:
IDR_MYMDITYPE "\nMyMdi\nMyMdi\nMyMdi 文件(*.mmd)\n.mmd\nMyMdi.Document\nMyMdi.Document"
在它后面添加:
IDR_MYMDITYPE2 "\nMyMdi2\nMyMdi2\nMyMdi2 文件(*.mm2)\n.mm2\nMyMdi2.Document\nMyMdi2.Document"
(3)为第二个资源文档添加菜单
打开菜单资源,将IDR_MYMDITYPE复制一份为IDR_MYMDITYPE2
4、编辑代码
(1)要在应用程序的InitInstance()函数中定义新的文档模板对象,打开MyMdi.cpp,在InitInstance()函数中添加:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_My112TYPE,
RUNTIME_CLASS(CMy112Doc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(CMy112View));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
(2)在文件中添加MyMdiDoc2.h与MyMdiView2.h头文件
(3)为CMyMdiDoc2类添加CPtrArray类型成员变量m_data,这里用于保存图形信息。为其在应用程序中添加一个类DrawData:
class DrawData{
Point begin,end;
int type;
};
并在CMyMdiView2中添加头文件DrawData.h
CMyMdiDoc2中添加整型变量OnChangeDrawType(UNIT nID)
在CMyMdiView2中加入OnChangeDrawType映射消息,并补上代码
MyMdiDoc2.cpp
BEGIN_MESSAGE_MAP(CMyMdiDoc2, CDocument)
ON_COMMAND_RANGE(ID_LINE,ID_RECTANGLE, &CMyMdiDoc2::OnChangeDrawType)
END_MESSAGE_MAP()
void CMyMdiDoc2::OnChangeDrawType(UINT nID)
{
m_drawType=nID-ID_LINE;
}
(4)画图:
在MyMdiView2.h中添加成员变量
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
DrawData* m_drawData;
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
在MyMdiView2窗口属性中添加WM_LBUTTONDOWN与WM_LBUTTONUP两个消息,编辑事件处理代码:
// CMyMdiView2 消息处理程序
void CMyMdiView2::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CMyMdiDoc2* pDoc = (CMyMdiDoc2 *)GetDocument();
m_drawData=new DrawData;
m_drawData->begin=point;
CView::OnLButtonDown(nFlags, point);
}
void CMyMdiView2::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CMyMdiDoc2* pDoc =(CMyMdiDoc2 *) GetDocument();
m_drawData->end=point;
CClientDC dc(this);
CBrush *brush=CBrush::FromHandle((HBRUSH)
GetStockObject(HOLLOW_BRUSH));
dc.SelectObject(brush);
CRect rect(m_drawData->begin,m_drawData->end);
switch(pDoc->m_drawType)
{
case 0:
dc.MoveTo(m_drawData->begin);
dc.LineTo(m_drawData->end);
break;
case 1:
dc.Ellipse(rect);
break;
case 2:
dc.Rectangle(rect);
break;
}
m_drawData->type=pDoc->m_drawType;
pDoc->m_data.Add(m_drawData);
brush->DeleteObject();
Invalidate(true);
CView::OnLButtonUp(nFlags, point);
}
(5)最后,编辑视图类的OnDraw()函数,该函数将文档类的成员m_data在客户区绘制一遍。
void CMyMdiView2::OnDraw(CDC* pDC)
{
//CDocument* pDoc = GetDocument();
// TODO: 在此添加绘制代码
CMyMdiDoc2 *pDoc =(CMyMdiDoc2 *)GetDocument();
CBrush *brush=CBrush::FromHandle((HBRUSH)
GetStockObject(HOLLOW_BRUSH));
pDC->SelectObject(brush);
for(int i=0;im_data.GetCount();i++)
{
m_drawData=(DrawData *)(pDoc->m_data.GetAt(i));
CRect rect(m_drawData->begin,m_drawData->end);
switch(m_drawData->type)
{
case 0:
pDC->MoveTo(m_drawData->begin);
pDC->LineTo(m_drawData->end);
break;
case 1:
pDC->Ellipse(rect);
break;
case 2:
pDC->Rectangle(rect);
break;
}
}
brush->DeleteObject();
}