如何实现单文档多视图?

设计思路:

文档负责保存数据对象,没有个数据对象(特征对象,如三通管)包含3种显示:三维示例图,二维展开图和计算文档,初步设计通过定义不同的视图来表示不同的显示,现在的问题是如何实现?

该功能已经实现!

通过创建文档模板的方式,将所要添加的视图类信息添加进来,

关键代码如下:

<code begin>


class CIntraGlDocTemplate : public CSingleDocTemplate
{
 DECLARE_DYNAMIC(CIntraGlDocTemplate)
  // Constructors
public:
 CIntraGlDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
  CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass,
  int nSplitterID = -1, int nRow = 0, int nCol = 0);
 
 // Attributes
public:
 int m_nSplitterID; // -1, no splitter else splitter ID (0, 1,...)
 int m_nRow;   // if splitter, row number (0, 1,...)
 int m_nCol;   // if splitter, col number (0, 1,...)
 
 CView* m_pView;  // view pointer
 
 // these overrides to retreive the view class and resource ID
 CRuntimeClass* GetViewClass()
    { return m_pViewClass; }
 UINT GetResourceID()
 { return m_nIDResource; }
};

//


IMPLEMENT_DYNAMIC(CIntraGlDocTemplate, CSingleDocTemplate)

CIntraGlDocTemplate::CIntraGlDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
         CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass,
         int nSplitterID, int nRow, int nCol) :
CSingleDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)
{
 m_nSplitterID = nSplitterID;
 m_nRow = nRow;
 m_nCol = nCol;
 m_pView = NULL;
}

///app类的instance函数如下


BOOL CIntraGlApp::InitInstance()
{
 AfxEnableControlContainer();

 // Standard initialization
 // If you are not using these features and wish to reduce the size
 //  of your final executable, you should remove from the following
 //  the specific initialization routines you do not need.

#ifdef _AFXDLL
 Enable3dControls();   // Call this when using MFC in a shared DLL
#else
 Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

 // Change the registry key under which our settings are stored.
 // TODO: You should modify this string to be something appropriate
 // such as the name of your company or organization.
 SetRegistryKey(_T("Local AppWizard-Generated Applications"));

 LoadStdProfileSettings();  // Load standard INI file options (including MRU)

 // Register the application's document templates.  Document templates
 //  serve as the connection between documents, frame windows and views.

 CIntraGlDocTemplate* pDocTemplate;
 pDocTemplate = new CIntraGlDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CIntraGlDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CModelView));
 AddDocTemplate(pDocTemplate);

 //CSingleDocTemplate* pDocTemplate;
 pDocTemplate = new CIntraGlDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CIntraGlDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CDrawingView));
 AddDocTemplate(pDocTemplate);

 // 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;

 // The one and only window has been initialized, so show and update it.
 m_pMainWnd->ShowWindow(SW_SHOW);
 m_pMainWnd->UpdateWindow();

 return TRUE;
}

// add the following member function to the CMainFram class


class CSplitInfo
{
public:
 int m_MaxRow, m_MaxCol;
 CSplitterWnd m_wndSplitter;
 CSplitInfo::CSplitInfo();
 CSplitInfo &operator=(const CSplitInfo &SplitInfo);
};

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,CCreateContext* pContext)
{
 CView* pView;
 CIntraGlApp* pApp = (CIntraGlApp*)AfxGetApp();
 ASSERT_KINDOF(CIntraGlApp, pApp);

 // hold informations from DocTemplates to create splitter
 POSITION TmpPos = pApp->GetFirstDocTemplatePosition();
 while (TmpPos)
 {
  CIntraGlDocTemplate* pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
  ASSERT_KINDOF(CIntraGlDocTemplate, pTemplate);
  if (pTemplate->m_nSplitterID != -1)
  {
   // find or create the splitter ID in the splitter map
   int nID = pTemplate->m_nSplitterID;
   m_SplitInfo[nID].m_MaxRow = max(m_SplitInfo[nID].m_MaxRow, pTemplate->m_nRow + 1);
   m_SplitInfo[nID].m_MaxCol = max(m_SplitInfo[nID].m_MaxCol, pTemplate->m_nCol + 1);
  }
 }
 
 // create the splitter and the views in the splitter
 POSITION MapPos = m_SplitInfo.GetStartPosition();
 while (MapPos)
 {
  int nID; CSplitInfo SplitInfo;
  m_SplitInfo.GetNextAssoc(MapPos, nID, SplitInfo);
  if (m_SplitInfo[nID].m_MaxRow && m_SplitInfo[nID].m_MaxCol)
  {
   CRect rect; GetClientRect(&rect);
   int cx = rect.Width(); int cy = rect.Height();
   
   // normaly always ToolBar & StatusBar
   cy = cy - 56; if (cy < 56) cy = 0;

   if (m_SplitInfo[nID].m_MaxCol) cx /= m_SplitInfo[nID].m_MaxCol;
   if (m_SplitInfo[nID].m_MaxRow) cy /= m_SplitInfo[nID].m_MaxRow;
   CSize sizeDefault(cx, cy);

   m_SplitInfo[nID].m_wndSplitter.CreateStatic(this, m_SplitInfo[nID].m_MaxRow, m_SplitInfo[nID].m_MaxCol);
   // disable view ID to use for next splitter
   m_SplitInfo[nID].m_wndSplitter.SetDlgCtrlID(0);
 
   TmpPos = pApp->GetFirstDocTemplatePosition();
   while (TmpPos)
   {
    CIntraGlDocTemplate* pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
    if (pTemplate->m_nSplitterID == nID)
    {
     // create view
     int nRow = pTemplate->m_nRow, nCol = pTemplate->m_nCol;
     pContext->m_pNewDocTemplate = pTemplate;
     pContext->m_pNewViewClass = pTemplate->GetViewClass();
     if (!m_SplitInfo[nID].m_wndSplitter.CreateView(nRow, nCol,
      pContext->m_pNewViewClass, sizeDefault, pContext)) return FALSE;
     pTemplate->m_pView = (CView*)m_SplitInfo[nID].m_wndSplitter.GetPane(nRow, nCol);
    }
   }
   // disable view ID to use for next splitter view
   TmpPos = pApp->GetFirstDocTemplatePosition();
   while (TmpPos)
   {
    CIntraGlDocTemplate* pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
    if (pTemplate->m_nSplitterID == nID)
     pTemplate->m_pView->SetDlgCtrlID(0);
   }
  }
 }

 // create the other views (no splitter)
 TmpPos = pApp->GetFirstDocTemplatePosition();
 while (TmpPos)
 {
  CIntraGlDocTemplate* pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
  if (pTemplate->m_nSplitterID == -1)
  {
   pContext->m_pNewViewClass = pTemplate->GetViewClass();
   pView = (CView*)CreateView(pContext, AFX_IDW_PANE_FIRST);
   ASSERT(pView != NULL);
   pTemplate->m_pView = pView;
   // disable view ID to use for next view
   pView->SetDlgCtrlID(0);
  }
 }
 SetActiveView(NULL);

 CIntraGlDocTemplate *pTemplate;

 // litle problem ! if a CRichEditView is in the view list
 // we must activate it first to update the layout correctly
 pTemplate = pApp->GetTemplate(RUNTIME_CLASS(CModelView));
 if (pTemplate) ActivateView(pTemplate->GetViewClass());

 // activate the first doctemplate view
 pTemplate = pApp->GetFirstTemplate();
 ActivateView(pTemplate->GetViewClass());

 return TRUE;
}

void CMainFrame::ActivateView(CRuntimeClass* pViewClass)
{
 CIntraGlApp* pApp = (CIntraGlApp*)AfxGetApp();
 CView* pActiveView = GetActiveView();
 CIntraGlDocTemplate* pNewTemplate = pApp->GetTemplate(pViewClass);
 CIntraGlDocTemplate* pActiveTemplate = pApp->GetTemplate(pActiveView);

 // the view class doesn't exist
 if (pNewTemplate == NULL) return;
 
 // the new template is the active template, do nothing
 if (pNewTemplate == pActiveTemplate) return;

 // the active and new template are in the same splitter, change the active view
 if (pActiveTemplate != NULL)
 {
  if (pActiveTemplate->m_nSplitterID != -1 &&
  pActiveTemplate->m_nSplitterID == pNewTemplate->m_nSplitterID)
  {
   UpdateResource(pNewTemplate);
   SetActiveView(pNewTemplate->m_pView);
   return;
  }

  // the active view is in a splitter, hide all views in the splitter and the splitter
  if (pActiveTemplate->m_nSplitterID != -1)
  {
   POSITION TmpPos;
   CIntraGlDocTemplate* pTemplate;
   int nID = pActiveTemplate->m_nSplitterID;
   TmpPos = pApp->GetFirstDocTemplatePosition();
   while (TmpPos)
   {
    pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
    if (pTemplate->m_nSplitterID == nID)
    {
     pTemplate->m_pView->ShowWindow(SW_HIDE);
     pTemplate->m_pView->SetDlgCtrlID(0);
    }
   }
   // hide the splitter
   m_SplitInfo[nID].m_wndSplitter.SetDlgCtrlID(0);
   m_SplitInfo[nID].m_wndSplitter.ShowWindow(SW_HIDE);
  }
  // the active view is not in a splitter, hide this view
  else
  {
   pActiveTemplate->m_pView->SetDlgCtrlID(0);
   pActiveTemplate->m_pView->ShowWindow(SW_HIDE);
  }
 }
 
 // the new view is in a splitter, show all splitter views and the splitter
 if (pNewTemplate->m_nSplitterID != -1)
 {
  POSITION TmpPos;
  CIntraGlDocTemplate* pTemplate;
  int nID = pNewTemplate->m_nSplitterID;
  TmpPos = pApp->GetFirstDocTemplatePosition();
  while (TmpPos)
  {
   pTemplate = (CIntraGlDocTemplate*)pApp->GetNextDocTemplate(TmpPos);
   if (pTemplate->m_nSplitterID == nID)
   {
    int nRow = pTemplate->m_nRow;
    int nCol = pTemplate->m_nCol;
    int nDlgID = m_SplitInfo[nID].m_wndSplitter.IdFromRowCol(nRow, nCol);
    pTemplate->m_pView->SetDlgCtrlID(nDlgID);
    pTemplate->m_pView->ShowWindow(SW_SHOW);
   }
  }
  // show the splitter
  m_SplitInfo[nID].m_wndSplitter.SetDlgCtrlID(AFX_IDW_PANE_FIRST);
  m_SplitInfo[nID].m_wndSplitter.ShowWindow(SW_SHOW);
 }
 // the new view is not in a splitter, active the new view
 else
 {
  pNewTemplate->m_pView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
  pNewTemplate->m_pView->ShowWindow(SW_SHOW);
 }

 UpdateResource(pNewTemplate);

 // display and update the new view
 SetActiveView(pNewTemplate->m_pView);
 RecalcLayout();
}

BOOL CMainFrame::IsViewActive(CRuntimeClass* pViewClass)
{
 CView* pActiveView = GetActiveView();
 if (pActiveView == NULL) return FALSE;
 return (pActiveView->GetRuntimeClass() == pViewClass);
}

void CMainFrame::UpdateResource(CIntraGlDocTemplate *pNewTemplate)
{
 CIntraGlApp* pApp = (CIntraGlApp*)AfxGetApp();

 // support for view context menu, accelerator and help ID like MDI
 HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(pNewTemplate->GetResourceID()), RT_MENU);

 // if menu is not the default menu, destroy it
 HMENU hMenu = ::GetMenu(m_hWnd);
 if (hMenu != m_hMenuDefault) ::DestroyMenu(hMenu);
 // load menu from doctemplate
 hMenu = ::LoadMenu(hInst, MAKEINTRESOURCE(pNewTemplate->GetResourceID()));
 // if no menu, get default
 if (hMenu == NULL) hMenu = m_hMenuDefault;
 // load menu
 ::SetMenu(m_hWnd, hMenu);

 // load new accelerator table
 HACCEL hAccel = ::LoadAccelerators(hInst, MAKEINTRESOURCE(pNewTemplate->GetResourceID()));
 // if no table for this template, load default
 if (hAccel == NULL) ::LoadAccelerators(hInst, MAKEINTRESOURCE(pApp->GetFirstTemplate()->GetResourceID()));
 
 // chage help ID for this view
 m_nIDHelp = pNewTemplate->GetResourceID();

 // change the title of the document
 pNewTemplate->GetDocString(m_strTitle, CDocTemplate::windowTitle);
 OnUpdateFrameTitle(TRUE);
}

// the command notification function is


void CMainFrame::OnViewModel()
{
 // TODO: Add your command handler code here
 ActivateView(RUNTIME_CLASS(CModelView));
 
}

void CMainFrame::OnUpdateViewModel(CCmdUI* pCmdUI)
{
 // TODO: Add your command update UI handler code here
 pCmdUI->SetCheck(IsViewActive(RUNTIME_CLASS(CModelView)));
 
}

<code end>

if you need the source code please email to me. hongxian.qin@autodesk.com

created by HongxianQin

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
摘要:本文通过一个程序实例描述了在VC++6.0下如何在文档程序中通过菜动态控制多 个窗体的切换。    一、 引言    我们在编制程序中根据需求的不同会在程序风格上选择多文档文档或是对话框模式 ,对于文档模式可能是我们使用比较多的,但有时我们想采用文档的形式显示多个不同 的窗体,如作为数据库前台应用程序就会遇到此类问题,数据库由大量的表组成,而同常 一个窗体内只用来显示一个表,所以要显示其他的表时就要用到切换窗体的技术了,下 面就通过一个程序说明该技术的实现方法。    二、 实现技术    新建一个基于CFormView的文档应用程序,再添加一个窗体和与之对应的基于 CFormView的新视类,然后通过在主框架类里添加控制代码和菜控制实现这两个窗体的动态 切换,下面就是具体的实现过程:    (一) 用"MFC AppWizard(exe)"建立一个新项目"SwitchForm",并在第二步的创建类型上选 择为"Single documnet"文档模式,第三、四、五、六步均取确省状态,最后一步选择 "CFormView"作为视类的基类。点按"完成"按钮,生成了初始工程"SwitchForm"。    (二) 点选菜"Insert"、"Resource…",在弹出的"Insert Resource"对话框中"Dialog"树 里的"IDD_FORMVIEW",点击"New"按钮,生成了一个新的窗体,将其ID号改为"IDD_NEXTFORM"。 在原有的窗体上加一个静态框"这是第一个窗体";在新建的窗体上也添加一个静态框"这是第二 个窗体"。    (三) 在菜资源的"IDR_MAINFRAME"上添加一级菜"窗体切换",及其二级菜"第一个窗 体"、"第二个窗体",其标识号分别为"ID_FIRSTFORM"和"ID_SECONDFORM"。修该"第一个窗体" 的属性为"Checked",表明程序初始时显示的是第一个窗体。    (四) 在"ClassView"属性页里的"SwitchForm classes"上右键,在弹出菜上选择 "New Class…",弹出"New Class"对话框,选择"Dialog ID:"为我们刚添加的新窗体 "IDD_NEXTFORM",选择"Base class:"为"CFormView",类名取为"CNextFormView",这样就把第 二个窗体对应的视图类添加到了工程。 (五) 在框架类里添加函数SwitchToForm(): void CMainFrame::SwitchToForm(int nForm) { file://获取原来的活动窗体的视图句柄 CView* pOldActiveView = GetActiveView(); file://获取由"nForm"标识的窗体所对应的视图句柄 CView* pNewActiveView = (CView*) GetDlgItem(nForm); file://若视图句柄为空,则创建一新的。 if (pNewActiveView == NULL) { if (nForm == IDD_SW99vCHFORM_FORM) pNewActiveView = (CView*)new CSwitchFormView; if (nForm == IDD_NEXTFORM) pNewActiveView = (CView*)new CNextFormView; CCreateContext context; context.m_pCurrentDoc = pOldActiveView->GetDocument(); pNewActiveView->Create(NULL,NULL,0L, CFrameWnd::rectDefault, this,nForm,&context); pNewActiveView->OnInitialUpdate(); } file://选择pNewActiveView为活动窗体 SetActiveView(pNewActiveView); file://显示活动窗体,隐藏非活动窗体 pNewActiveView->ShowWindow(SW_SHOW); pOldActiveView->ShowWindow(SW_HIDE); int ID; if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CSwitchFormView)) ID=IDD_SW99vCHFORM_FORM; if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CNextFormView)) ID=IDD_NEXTFORM; file://设置窗体的ID号 pOldActiveView->SetDlgCtrlID(ID); pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); RecalcLayout(); }    (六)添加两个菜相对应的命令响应函数和更新函数如下: void CMainFrame::OnFirstform() { file://通过IsKindOf函数确定当前活动窗口是否是第一个窗口,如是,则无须切换, file://否则将通过SwitchToForm函数将当前活动窗口切换到"IDD_SW99vCHFORM_FORM" file://标识的第二个窗体。 if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView))) return; SwitchToForm(IDD_SW99vCHFORM_FORM); } void CMainFrame::OnUpdateFirstform(CCmdUI* pCmdUI) { file://通过IsKindOf函数判断当前活动窗口是否是第一个窗体,如是则将其选中。 pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView))); } void CMainFrame::OnSecondform() { if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView))) return; SwitchToForm(IDD_NEXTFORM); } void CMainFrame::OnUpdateSecondform(CCmdUI* pCmdUI) { pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView))); }    然后再在该文件开始处添加对两个视图类的引用: #include "SwitchFormDoc.h" #include "SwitchFormView.h" #include "NextFormView.h"    在此须注意:应在两个视类的引用之前添加对文档类的引用,否则会引起编译错误。另外,由于视 类的构造函数在声明时都确省的声明为保护型的,在框架类中无法引用,所以还要将两个视类的类 声明改动如下: class CNextFormView : public CFormView { public: file://将protected 改为public. CNextFormView(); …… }; class CSwitchFormView : public CFormView { public: file://将protected 改为public. CSwitchFormView(); …… };    三、 编译运行    编译运行程序,开始时的窗体上有"这是第一个窗体的字样",菜也只有"第一个窗体"是被选中的, 当前的活动窗体是第一个窗体;点击菜"第二个窗体",视图中的窗体上的字样变成了"这是第二 个 窗体",同时选中的菜也由"第一个窗体"变成了"第二个窗体",实现了通过菜将窗体进行动态切换。    总结:此程序中关键的是SwitchToView函数,在此函数中,程序搜索所有当前文档的显示窗口来查找与CruntimeClass变量匹配的视图类。如果找到,该窗口被激活。通过与之类似的方法,还可以实现在多文档模式下的档(文档)多视(视图),通过不同的视图以不同的方式显示来自同一份文档的数据,以更好的满足程序的需要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值