将Doc/View 应用程序打造成 ActiveX 控件

众所周知,MFC提供了一个非常强大的,但是又不太容易理解的编程模型叫做文档/视图结构。它的基本思想就是将数据的表现和数据的存在分开管理。MFC提供了很多现在的类来达成这个目标,通常,利用CFrameWnd,CView和CDocument这三个类,再利用CSingleDocTemplate的魔法将它们串联起来,就可以很容易的构建出一个单文档的Doc/View应用程序。
         下面是一个典型的Doc/View应用程序界面:
         
      但是,随着互联网的发展,一切东西都跃然于网络上。我们能不能将文档/视图结构的应用程序做成一个酷酷的,可以用于网络发布的应用程序呢。比如说我们可以通过浏览器操作该应用程序。上面的框架界面嵌入到浏览器当中。嗯,一个很好的选择就是将该应用程序打造成ActiveX控件。下面开始介绍该魔法产生的过程:
      首先,我们要考虑到该ActiveX控件可以会显示在Html页上做为浏览器的一个子窗口。而如果使用传统的CDocTemplate类将文档,视图相联系,则每当产生一个新的框架时,都会弹出一个具有框架窗口,达不到内嵌的目的,所以,改造CDocTemplate类是必须的。其中关键的几点是:
      1。考虑到要嵌在OleControl当中,所以新产生的框架窗口将要以该OleControl作为父窗口。
      2。考虑到作为内嵌的子窗口,而子窗口是不能具有菜单的,因此该控件不具有菜单。但是可以用下拉菜单来加以代替,当然,工具条也是可以的。
      3。除了菜单外,大部分Doc/View的功能都将要保留。

第一步:我们从CSingleDocTemplate派生一个类,叫做CActiveDocTemplate.其中关键是要修改二个函数:CreateNewFrame和OpenDocumentFile函数。使得每当创那一个新的框架时,都会以外围的OleControl窗口作为父窗口。这两个函数改变如下:
        CDocument* CActiveXDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bVerifyExists) {
       SaveDocumentFile();  //保存当前文档
       m_docFile=lpszPathName;
      if(bVerifyExists) {
  DWORD dwAttrib=GetFileAttributes(lpszPathName);  //得到文档类型
  if(dwAttrib==0xFFFFFFFF||
   dwAttrib==FILE_ATTRIBUTE_DIRECTORY)    //如果是目录的话
  {
   lpszPathName=NULL;
  }

  
 }

 return CSingleDocTemplate::OpenDocumentFile(lpszPathName,TRUE);
}

CFrameWnd* CActiveXDocTemplate::CreateNewFrame(CDocument* pDoc,CFrameWnd* pOther) {
 ASSERT(pOther==NULL);
 ASSERT(m_pFrameClass!=NULL);

 if(pDoc!=NULL)
  ASSERT_VALID(pDoc);  //检查对象的有效性
  
 //创建一个框架和相应的文档连接起来
 CCreateContext context;
 context.m_pCurrentFrame=pOther;
 context.m_pCurrentDoc=pDoc;
 context.m_pNewViewClass=m_pViewClass;
 context.m_pNewDocTemplate=this;

 m_pFrameWnd=(CFrameWnd*)m_pFrameClass->CreateObject();
 if(m_pFrameWnd==NULL) {
  TRACE1("Warning: Dynamic create of frame %hs failed./n",
            m_pFrameClass->m_lpszClassName);
        return NULL;
 }

 ASSERT_KINDOF(CFrameWnd,m_pFrameWnd);
 if(context.m_pNewViewClass==NULL)
  TRACE0("Warning: creating frame with no default view./n");

 ASSERT_KINDOF(CActiveXDocControl,m_pParentWnd);
 if(!m_pFrameWnd->Create(NULL,"",WS_CHILD|WS_VISIBLE,CFrameWnd::rectDefault,m_pParentWnd,NULL,0,&context)) {
  TRACE0("Warning: CDocTemplate couldn't create a frame./n");
        return NULL;
 }
 return m_pFrameWnd;

}

第二步:从COleControl类派生一个CActiveXDocControl类,以改造COleControl类,使之在包含一个指向CActiveDocTemplate的指针。利用该指针,我们可以创建框架窗口,保存文档内容,以完成常规Doc/View应用程序应完成的功能。下面是改造后的CActiveXDocControl头文件的内容:
   class CActiveXDocControl:public COleControl {
 enum {WM_IDLEUPDATECMDUI=0x0363};

 static BOOL m_bDocInitialized;
 CActiveXDocTemplate* m_pDocTemplate;
 CFrameWnd* m_pFrameWnd;
// SetInitialSize(100,100);
 DECLARE_DYNAMIC(CActiveXDocControl)
 
protected:
 void AddDocTemplate(CActiveXDocTemplate* pDocTemplate);
 CDocTemplate* GetDocTemplate() {return m_pDocTemplate;}
 
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 afx_msg void OnSize(UINT nType,int cx,int cy);
 afx_msg void OnTimer(UINT nIDEvent);
 afx_msg void OnDestroy();

 DECLARE_MESSAGE_MAP();
 DECLARE_DISPATCH_MAP();
 DECLARE_EVENT_MAP();

public:
 CActiveXDocControl();
 virtual ~CActiveXDocControl();

 enum {
  
 };
};

并且在控件类的构造函数中加入DocTemplate:
 AddDocTemplate(new CActiveXDocTemplate(
  RUNTIME_CLASS(CMy55Doc),
  RUNTIME_CLASS(CMy55Frame),
  RUNTIME_CLASS(CMy55View)
  ));

至此,基本框架都已经建立完成,剩下的步骤就是打造Doc/View应用程序了。
通过这几部,我们可以很容易的把现有的文档/视图结构的应用程序改造成可以在浏览器上显示的ActiveX控件。使得现有程序的功能大部分都保留下来,并且可以在网上进行发布,使得用户可以通过浏览器访问并且使用它。

下面是将上面的文档/视图应用程序改造后在浏览器中运行并操作的界面:


                                                                                             本文参考了msdn 上的Designing ActiveX Components with the MFC Document/View Model。