65.9K
CodeProject 正在变化。 阅读更多。
Home

拆分窗口中的无限数量的可切换视图

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (36投票s)

2004年2月20日

4分钟阅读

viewsIcon

98453

downloadIcon

2789

允许用户拥有与拆分窗格关联的无限数量的视图

引言

本文详细介绍如何使用我的 CMultiSplitterView 类,该类允许在一个拆分器窗格内实现多个可切换视图。在上图中,灰色区域是第一个视图。通过转到菜单栏并单击“视图 > 显示第二个视图”,程序将动态地将第一个视图更改为第二个视图。

此类允许您通过一个函数调用切换到您创建的任何视图,无需任何额外操作!添加视图也像调用一个函数一样简单。现在,让我们进入细节...

代码详情

一切的核心都位于 MultiSplitterView.cppMultiSplitterView.h 文件中。

在此处将视图添加到拆分器窗格

 bool CMultiSplitterView::AddSwitchableView(UINT id, 
                 CRuntimeClass * pView,
                 CCreateContext* pContext,
                 CRect & size, bool isFirstView, UINT altId)
{
   CWnd* pWin;
   DWORD style;

   pWin  = (CWnd*) pView->CreateObject();
   style = WS_CHILD ;
   
   if (isFirstView) 
   {
     style |=  WS_VISIBLE ;
   }

   pWin->Create(NULL, NULL, style, size , this, id, pContext);

   if (isFirstView) // id provided is usually diff. so use alternate
   {
     views[pWin] =altId ;
   }
   else 
   {
     views[pWin] = id;
   }

   return true;
}

第一个参数是您与视图关联的视图 ID,以便可以轻松查找。第二个参数是通过调用 RUNTIME_CLASS(SomeViewClass) 创建的,该函数返回 CRuntimeClass 类的指针。第三个参数是在 CMainFrame 类中由 OnCreateClient 函数提供的 CCreateContext。第四个参数是窗口的尺寸。现在,最后两个参数是可选的,并且仅在首次调用 AddSwitchableView() 时使用。因为第一个参数的 ID 是第一个视图将设置为的窗格的 ID,所以我需要传递用户将视图关联的实际 ID,因此最后一个参数称为 altId

好的,我们传入所有这些参数,然后创建一个类型为传入的运行时类的对象,并将其转换为其基类 CWnd 以便创建和存储。请注意,在对 CWnd 对象调用 Create 时,我始终使用传入的 ID 和此指针,该指针将视图与拆分器关联起来。然后,我将 CWnd 的指针存储在 map<> 中作为“键”,并将 ID 或备用 ID 作为值用于后续查找。

接下来,我们需要动态切换视图,以便一个显示而另一个隐藏。

以下代码处理任意数量视图的切换

bool CMultiSplitterView::SwitchView(UINT id, int paneRow, int paneCol)
{

    CView* pOldView = (CView*) GetPane(paneRow, paneCol); // get current view

   if (pOldView == NULL) // serious prob
  {
     return false;
  }

   CView* pNewView = (CView*) GetDlgItem(id); // get new view

   if(pNewView == NULL ) // bad view id or this is already the view we requested
   {
      return false;
   }

   CFrameWnd * mainWnd = (CFrameWnd *)AfxGetMainWnd();

   if (mainWnd == NULL) // serious prob
   {
     ASSERT(false);
     return false;
   }
  
   if(mainWnd->GetActiveView() == pOldView)
    mainWnd->SetActiveView(pNewView);
  
   pNewView->ShowWindow(SW_SHOW);
   pOldView->ShowWindow(SW_HIDE);

   pNewView->SetDlgCtrlID(  IdFromRowCol(paneRow, paneCol));

   CWnd * bCwnd =(CWnd *)pOldView; // upcast to CWnd ptr

  if (views.find(bCwnd) == views.end()) // search for CWnd ptr
  {
  return false;
  }

   UINT oldId = views[bCwnd]; // get id of this view for future lookup

   pOldView->SetDlgCtrlID(oldId); // reset view id, so we can look it up
}

好的,现在用户或 GUI 已调用 SwitchView() 并传入了某个 ID 以及视图所属拆分器的行和列。我们首先使用用户提供的行和列获取当前视图的指针。接下来,我们获取新视图(即将显示的与传入 ID 关联的视图)。我们验证它们不是 NULL,然后从主窗口获取 CFrameWnd 指针。然后,我们比较活动视图与旧视图,看它们是否相同(它们应该是相同的),然后将新视图设置为请求的视图。在隐藏和显示旧视图和新视图之后,将新视图的 ID 设置为与视图关联的行和列的 ID 至关重要,因为它是窗格的子窗口。然后,我们在 map<> 中查找旧视图的 ID,因为它的控件 ID 仍然是 IdFromRowCol() 中的 ID。然后,我们将控件 ID 重置为 map<> 中存储的值,以便稍后可以检索视图指针。

如何使用

在您的 CMainFrame 中,您必须首先包含 MultiViewSplitter.h 头文件。接下来,声明一个类型为 CMultiViewSplitter 的成员 var。接下来,在 CMainFrame 中的 OnCreateClient()

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{

CRect r;
GetWindowRect(r);

m_SplitterFirst.CreateStatic(this,1,2); 

m_SplitterFirst.CreateView(0,0, RUNTIME_CLASS( CSomeView) ,
               CSize(r.Width() *0.14, r.Height()), pContext); 

m_SplitterSeconde.CreateStatic(&m_SplitterFirst, 2, 1, WS_CHILD | WS_VISIBLE |
                 WS_BORDER, m_SplitterFirst.IdFromRowCol(0, 1));

/******************* Add switchable views **************************/

   m_SplitterSeconde.AddSwitchableView(m_SplitterSeconde.IdFromRowCol(0, 0),
            RUNTIME_CLASS(CFirstView) ,pContext, CRect(0,0,r.Width(), r.Height()) ,
      true , FIRST_VIEW);

   m_SplitterSeconde.AddSwitchableView(SECOND_VIEW, 
      RUNTIME_CLASS( CSecondView), pContext,
      CRect(0,0,r.Width(), r.Height()*0.60) );

/******************************************************************/

  ...

}

对于此示例,真正重要的是“添加可切换视图”注释之后的部分。我首先将第一个拆分器 m_SplitterFirst 拆分为 2 列。然后,我将第二个拆分器创建为第一个拆分器的子对象,并将其拆分为两行。现在,这一切对您来说都不重要,因为您可以根据自己的需要拆分窗口。

对于首次调用 AddSwitchableView(),请务必使用调用 m_SplitterSeconde.IdFromRowCol(x, x) 提供的拆分器 ID,并将备用视图 ID 作为最后一个参数,将 true 作为倒数第二个参数,以指示这是第一个视图。

RUNTIME_CLASS( x ) 的调用内部,添加封装您的视图的任何类的类名。
哇,就是这样!现在,无论何时您想动态切换视图,只需调用 SwitchView( x ),其中 x 是视图的 ID。请下载示例演示项目以全面了解其工作原理。

如果有人给本文投了低分(低于 4 或 5),您能否告诉我您给的评分原因以及我如何做得更好。欢迎提供建议、好评或指出问题。感谢您的时间。

其他有用函数

  • GetViewPtr(UINT id, int paneRow, int paneCol) - 获取与拆分器关联的基类 CWnd 指针

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.