MFC多文档机制实例

时间:2022-05-18 06:21:01

源于之前转的OnFileOpen和OnOpenDocument那篇文章,只是写菜单项的消息响应函数(即OnFileOpen,处理默认“打开”菜单项ID:ID_FILE_OPEN的事函数件)不能同时打开多个文档,而重载MFC自带的OnOpenDocument则可以打开多个文档。由于暂时对MFC内部机制不是很熟悉所以可以猜想打开多个文档显示图片或文本的代码已经包含进去,但写消息响应函数还是要自己写那部分代码。

OnOpenDocument的缺点就在于不能定制打开文件对话框,其实也就只用修改后缀过滤器。

方法也在网上找到了:http://blog.csdn.net/kujojyotaro/article/details/27070079 见这篇博客的技巧五

即修改.rc文件的stringtable

这次按照http://hi.baidu.com/ecoiboblpwbijye/item/dd05129bab4375f028164733的程序思路写了个实例程序,对多文档程序有了进一步了解。

新建多文档工程,工程名为:MDIDemo。实现既能够打开txt文本又能够打开bmp图片。

关键还是在于App::InitInstance方法内的这步代码

// 将用作文档、框架窗口和视图之间的连接
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_MDIDemoTYPE,
RUNTIME_CLASS(CMDIDemoDoc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(CMDIDemoView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
CMultiDocTemplate是实现MDI界面的文档模板,可以看出这段代码就是动态创建一个文档模板然后用AddDocTemplate加入到应用程序中。

关键就是四个构造函数,第一个参数是IDR_MDIDemoTYPE,用记事本打开.rc文件找到STRINGTABLE部分

BEGIN
// 非 Mac 应用程序将移除额外的两个子字符串
    IDR_MAINFRAME           "MDIDemo"
    IDR_MDIDemoTYPE         "\nMDIDemo\nMDIDemo\n\n\nMDIDemo.Document\nMDIDemo.Document"
END

可以发现这个宏就代表6行字符串,第三行和第四行是空的,通过改变字符串就可以过滤后缀名。

可以看出来第一个参数就代表对话框的类型,34行之外的暂时也不用修改。

而后面三个参数依次是Doc类,Child类和View类作为参数的RUNTIME_CLASS宏。RUNTIME_CLASS是通过C++类的名字得到一个运行时类结构

#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
得到的结构是CRuntimeClass*指针,并可以得到类名 CRuntimeClass* prt = RUNTIME_CLASS( CAge );

prt->m_lpszClassName即类名。也就是后三个参数代表相应Doc类,Child类和View类的名字。

也就是我现在要打开不同类型的文档。Child类是一样的,那么只需要新建对应的ADoc和AView类,BDoc和BView类,以及IDR_ATYPE和IDR_BTYPE就可以打开A和B两种类型的文档。这里A为TXT,B为BMP

先改变字符串表IDR_XXXTYPE(XXX为文档种类)

MFC多文档机制实例

新建两个MFC类,继承CDocument类的CTEXTDoc类和继承CScrollView类的CTEXTView类,顺便把原来的View类的父类改成CSrcollView类并重载OnInitialUpdate()函数添加

CSize sizeTotal;
// TODO: 计算此视图的合计大小
sizeTotal.cx = sizeTotal.cy = 1000;
SetScrollSizes(MM_TEXT, sizeTotal);

再在App:InitInstance()中添加文档模板的构造和添加代码(记得include相应头文件)

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_BMPTYPE,
RUNTIME_CLASS(CMDIDemoDoc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(CMDIDemoView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
CMultiDocTemplate* pDocTemplate2;
pDocTemplate2 = new CMultiDocTemplate(IDR_TEXTTYPE,
RUNTIME_CLASS(CTEXTDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CTEXTView));
if (!pDocTemplate2)
return FALSE;
AddDocTemplate(pDocTemplate2);

直接编译运行弹出以下选择框

MFC多文档机制实例

因为MDI程序默认打开一个文档,所以弹出此对话框,之后每次新建文档都会弹出此对话框选择新建BMP还是TEXT文档模板。

每次打开文档就会有3种选择。

MFC多文档机制实例

接下来就是完善代码了,在相应的Doc和View类里添加打开图像和文本的代码(新建的CTEXTView类里要重载GetDocument函数)


ps:我不喜欢原地址的序列化读写文件方法,发现VS2012下用CArchive类读的话只能读Unicode编码的txt……否则会乱码……

研究了半天同时满足读ANSI和Unicode的文本文档还是挺麻烦的,现在txt的话还是ANSI比较多吧,还是用std::fstream好了。

绘图就用GDI+了,不过GDI+的双缓冲也得研究下,这都是后话了。