[MFC]文档视图体系结构、单文档界面

1. 文档视图结构:

    1) 是MFC的一种模块化设计思想的产物,要求程序的数据和表现数据的视图分开来处理;

    2) 数据以文档的形式表现,文档可以存储在磁盘中永久保存也可以被程序读出并进行处理;

    3) 单文档(SDI,Single Document Interface)和多文档(Multiple Document Interface)的区别:单文档界面程序只支持打开一个文档,而多文档界面允许同事打开多个文档,还支持用多个视图来表现一个文档;

    4) 典型的单文档界面程序体系结构介绍:

         i. 主要包含应用程序对象、主框架对象、视图对象和文档对象;

         ii. 应用程序对象:提供消息循环给主框架和视图,并将消息送给它们两个;

         iii. 主框架对象:主要作为视图、工具栏以及其它用户界面对象的容器,文档视图结构从来都不会为主框架获取设备描述表来绘图,因为其所有的绘图都会输出到视图中,就算让主框架绘图也不会显示,因为视图刚好覆盖了主框架的客户区,是从CFrameWnd派生而来;

         iv. 文档对象:负责和视图交换数据,需要以视觉的形式表现数据时就将数据提供给视图去显示,需要保存视图表现的数据时就从视图获取数据,也可以和磁盘交换数据,提供数据的永久保存,是从CDocument类派生而来的;

         v. 视图对象:主要用于表现数据,同时处理窗口的各种消息,也负责将鼠标和键盘的输入转换成处理数据的命令,从CView或者其它如CScrollView派生而来;


2. SDI的InitInstance函数:

    1) 我们这里只关心现在要用到并急需掌握的内容,其余内容和Windows注册表有关,现在还不用关心;

    2) SDI文档模板的创建和注册:

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
	IDR_MAINFRAME,
	RUNTIME_CLASS(CSquaresDoc),
	RUNTIME_CLASS(CMainFrame),       // main SDI frame window
	RUNTIME_CLASS(CSquaresView));
AddDocTemplate(pDocTemplate);
          i. 首先先创建了一个SDI模板对象,SDI文档模板是SDI应用程序的重要组件,它用来统一管理文档、视图、框架三者,同时也保存了主结构要加载的界面资源(菜单、加速键等);

          ii. 看一下构造函数:

CSingleDocTemplate::CSingleDocTemplate( 
	UINT nIDResource, // 程序的界面资源(菜单、加速键等)
	CRuntimeClass* pDocClass, // 先加载文档
	CRuntimeClass* pFrameClass, // 再加载主框架
	CRuntimeClass* pViewClass // 最后再加载视图
); 
!!CRuntimeClass其实是一个结构体,里面保存了类在Windows中注册的类名、回调函数等基础信息;

!!其次就是这个奇葩的加载顺序,因为一般Windows程序的打开都是先通过打开一个文档再真正开启应用程序,而有些程序如word、notepad等可以直接打开,那是因为直接双击程序图标打开程序之前由于没有指定文档,所以默认创建了一个临时的空白文档再打开,因此要开启应用程序必须要先打开一个文档才行;

!!只有文档正确打开之后才能正常创建主框架以及视图;

          iii. 通常作为程序员你仅仅就编写了一个类而已,但是该如何获取该类的CRuntimeClass结构呢,很简单,MFC提供了RUNTIME_CLASS宏,只需要通过一个类名就能返回相应类的CRuntimeClass结构指针了!!

          iv. 创建完文档模板后就只需要将创建的模板通过CwinApp::AddDocTemplate注册给应用程序即可:void CWinApp::AddDocTemplate(CDocTemplate* pTemplate);

!!这里要注意了,参数是CDocTemplate,而不是CSingleDocTemplate,这就说明该函数还可以注册其它文档模板;

!!很好理解,其实CSingleDocTemplate只是CDocTemplate的派生类,也是DocTemplate的其中一种,可以通过多次调用此方法为每个模板定义应用程序支持的文档类型,但是在SDI结构中只能注册一个文档类型,但是在MDI中可以注册多个文档类型;


!!!为什么文档模板CXXXDocTemplate使用new来“动态”创建,并且里面的三个类(文档、框架、视图)也是“Runtime”,即运行时创建的(并非编译就已经创建的)???

!!其实是为了支持多文档!!想想看,像word这样的程序,可以支持多种类型的文档,诸如.doc、.docx等等,当然word本身也是多文档程序,支持多种文档类型,但是在打开一个文档时只需要创建一种文档类型的模板就行了,由于打开程序会先打开文档,然后根据具体的文档类型再决定使用哪种文档模板,比如在InitInstance中:以伪代码的形式显示

if doc_type == .doc:
	doc_temp = new CDocDocTemplate;
	...
else if doc_type == .docx
	doc_temp = new CDocXDocTemplate;
	...
else if doc_type == .XXX
	doc_temp = new CXXXDocTemplate;
	...
!!这样就可以根据具体的文档类型动态地选择创建什么样的模板,这样不是更省资源吗?这样就不用一下子创建所有的模板了吗?

!!!其实,上面的这种用法是专门给SDI程序使用,而MDI支持一次打开多种不同类型的文档,因此在MDI中可以同时注册多种类型模板,但是在SDI中只能注册一种模板,所以可以使用if - else - new的形式根据选择的文档类型动态选择创建具体类型的模板;

!!!实际上,目前开发者对于开发MDI程序非常消极,因为SDI几乎可以满足一切MDI的需求,只不过仅仅就是限制了不能用一个程序一次性打开多个而且是不同类型的文档;

!!其实,也可以用SDI来实现一次打开多种不同类型的文档,只需要每打开一个文档就开一个该程序的即可,每个线程使用if - else - new来打开不同类型的文档就行了,而只不过MDI更加贪一点,它只有一个程序线程,可以在一个线程中打开多种不同类型的文档而已,如果要用SDI实现的话就需要同时打开多个该程序而已;


    3) 解析并运行命令行命令:

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
	return FALSE;

         i. 有些情况下并不是直接双击图标来打开程序的,而是从命令行启动,但事实上Windows会将所有的双击图标打开程序的操作转换成命令行命令来启动程序;

         ii. 在这里就是通过CCommandLineInfo和ParseCommandLine来接受并解析命令行的命令;

         iii. ParseCommandLine:

               a. 原型:void CWinApp::ParseCommandLine(CCommandLineInfo& rCmdInfo);

               b. 该函数可以将程序缓存的命令行输入解析并存入CCommandLine对象中,然后让ProcessShellCommand函数执行Shell命令;

         vi. ProcessShellCommand:

               a. 原型:BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo);

               b. 该函数将执行rCmdInfo中的命令,如果执行成功则返回TRUE,否则返回FALSE;

    4) 显示并更新窗口就不用说了:

// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
    5) 最后一步就是开启拖动功能:

// Enable drag/drop open
m_pMainWnd->DragAcceptFiles();
         i. 该函数将开启拖动打开功能,就是把一个文档的图标拖动到相应程序的图标上就能使用改程序来打开文档的功能;

         ii. 原型:void CWnd::DragAcceptFiles(BOOL bAccept = TRUE);

         iii. bAccept为TRUE表示可以接受拖动到程序图标上的文档,否则就不能;


3. 文档/视图结构的命令传递机制简介:

    1) 在过去MFC1.0中所有的消息都将发送给主框架,如果使用了视图则主框架再将这些消息再传递给视图;

    2) 文档/视图结果出现后,MFC进入了2.0时代,将程序、框架、视图、文档的消息处理完全划分了开来,使设计更加模块化;

    3) 虽然消息处理完全分开了,但是底层的Windows操作系统还是只允许窗口接收消息,因此为了实现上述的消息处理的模块化,MFC在底层使用了非常非常复杂的命令传递机制;

你可能感兴趣的:(MFC,MDC,SDI,文档视图体系结构)