如何实现单文档多视图?

博客围绕文档保存数据对象的多视图显示功能展开。设计思路是通过定义不同视图表示三维示例图、二维展开图和计算文档,最终通过创建文档模板添加视图类信息实现该功能,并给出了关键代码,包括文档模板类、app类实例函数等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计思路:

文档负责保存数据对象,没有个数据对象(特征对象,如三通管)包含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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值