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

停靠工具栏中的属性表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.06/5 (7投票s)

2004年11月27日

CPOL

3分钟阅读

viewsIcon

70266

downloadIcon

2144

解释了如何将 CPropertySheet 放入 CControlBar。

Sample Image - PropSheetBar.jpg

引言

我想在可停靠工具栏中显示一个属性表,我希望该表在停靠和未停靠状态下都有一个标题,以便用户知道它是关于什么的,并且我希望它停靠到 MDI 视图而不是主框架。我怀疑这些要求对于专业的 MFC 程序员来说不会有太大的挑战,但由于我不是,我花了一些时间才弄清楚,我想如果我分享代码,可能会为其他人节省一些时间。

主要问题如下。首先,让属性表显示在原始的CControlBar中。其次,标准属性表以一定的(相当大)的最小宽度和高度显示,但一般来说,人们希望工具栏相当紧凑。幸运的是,这个问题已经由 Antonio Lacaci 在一篇出色的文章中解决了,我只是稍作修改地使用了他的代码。第三,控制栏在未停靠的浮动状态下具有标题栏和标题,但在停靠状态下仅显示抓手,因此在栏停靠时必须手动编码标题。第四,也是微不足道的,要使工具栏停靠到CView派生类,你必须记住它实际上停靠到视图的框架父级,而不是视图本身。

使用属性表栏

该功能在类CPropSheetBar中实现,该类作为成员变量在 View 中实例化。要使用该类,你首先必须构造并添加从CPropertyPage派生的页面,就像使用普通属性表一样。这些页面必须与属性表栏一样长,最好在视图的OnCreate函数中在堆上构造,然后再创建栏本身。

因此,在View.h中,我们有

CPropSheetBar m_PropSheetBar;
CPage1 *page1;
CPage2 *page2;
CPage3 *page3;

并且在View::OnCreate中,我们有

page1=new CPage1;
page2=new CPage2;
page3=new CPage3;
// for data exchange, I give the page a view pointer
page1->m_pView=this; 
page2->m_pView=this;
page3->m_pView=this;

m_PropSheetBar.AddPage(page1);
m_PropSheetBar.AddPage(page2);
m_PropSheetBar.AddPage(page3); 

CFrameWnd *pFrameWnd=(CFrameWnd *)GetParent();
if (!m_PropSheetBar.Create("test bar", pFrameWnd, 
                    AFX_IDW_CONTROLBAR_FIRST+40))
  return -1; // fail to create 
m_PropSheetBar.EnableDocking(CBRS_ALIGN_ANY);
pFrameWnd->EnableDocking(CBRS_ALIGN_ANY);
pFrameWnd->DockControlBar(&m_PropSheetBar);

你必须在调用CPropSheetBar::Create之前添加属性页面,因为Create需要知道它将显示的对话框资源的大小。你在 View 析构函数中删除这些页面。你在Create函数中传递属性表栏的标题。

实现 CPropSheetBar

CPropSheetBar派生自CControlBar,并有一个成员变量m_cPropSheet,类型为CShrinkingPropSheet。后者派生自标准CPropertySheet,并实现了 Antonio 针对大小问题的修复。CPropSheetBar有一个AddPage实用函数,它只是将页面添加到嵌入的属性表中

void AddPage(CPropertyPage * pPage) {m_cPropSheet.AddPage(pPage);}

CPropSheetBar具有以下重写

virtual void DrawGripper(CDC *pDC,const CRect &rect);
virtual CSize CalcDynamicLayout( int nLength, DWORD dwMode );
virtual BOOL Create(LPCTSTR lpszWindowName, CWnd* pParentWnd, UINT nID);

并且其中使用的一些概念是从 Alger Pike 的文章“A DevStudio-like CControlBar”中借鉴的。

CPropSheetBar::Create首先创建控制栏本身,然后在其内部创建属性表。它测量属性表的大小(这是一个收缩表,感谢微软)并存储它。

BOOL CPropSheetBar::Create(LPCTSTR lpszWindowName, CWnd* pParentWnd, UINT nID)
{
  // create the base window
  CString lpszClassName = AfxRegisterWndClass(CS_DBLCLKS, 
    LoadCursor(NULL, IDC_ARROW),m_brushBkgd, 0);
  DWORD style=WS_CHILD | WS_VISIBLE | CBRS_LEFT | CBRS_GRIPPER | 
              CBRS_TOOLTIPS | CBRS_FLYBY;
  m_dwStyle = style & CBRS_ALL;
  if (!CControlBar::Create(lpszClassName, lpszWindowName, 
                 style, CRect(0,0,0,0), pParentWnd,NULL))
    return FALSE;

  m_strTitle=lpszWindowName; 

  // create the property sheet
  m_cPropSheet.Create(this,WS_CHILD|WS_VISIBLE);
  m_cPropSheet.SetTitle(lpszWindowName);
  CClientDC dc(this);
  m_sizeTitle=dc.GetTextExtent(m_strTitle);
  CRect rc;
  m_cPropSheet.GetWindowRect(rc); // screen coordinates
  m_sizePropSheet=rc.Size();

  return TRUE;
}

CPropSheetBar::CalcDynamicLayout是 Windows 在显示控制栏时调用的关键函数。重写告诉 Windows 栏的大小,因为它现在包含属性表,允许在表周围设置合适的边距。它还会移动属性表以适当定位它在栏中的位置,这取决于我们是否需要手动绘制标题。

CSize CPropSheetBar::CalcDynamicLayout(int nLength, DWORD dwMode)
{
  if (IsFloating())
  {
    CRect rc(CPoint(6,6),m_sizePropSheet);
    m_cPropSheet.MoveWindow(rc);
    return m_sizePropSheet+CSize(16,16);
  }

  CRect rc(CPoint(4,m_sizeTitle.cy+sizeTitleOffset.cy), m_sizePropSheet);
  m_cPropSheet.MoveWindow(rc);
  return m_sizePropSheet+CSize(16, m_sizeTitle.cy+sizeTitleOffset.cy+10);
}

CPropSheetBar::DrawGripper如果栏是浮动的,则调用默认值,因为 Windows 提供了标题栏,否则它会绘制抓手并写入标题。我没有让抓手的位置依赖于停靠位置,但这可以通过测试来完成。

if( m_dwStyle & CBRS_ORIENT_HORZ )

并进行相应的编码。

void CPropSheetBar::DrawGripper(CDC *pDC,const CRect &rect)
{
  if (IsFloating())
  {
    CControlBar::DrawGripper(pDC,rect);
    return;
  }

  CFont font;
  VERIFY(font.CreateFont(
    14, // nHeight
    0, // nWidth
    0, // nEscapement
    0, // nOrientation
    FW_NORMAL, // nWeight
    FALSE, // bItalic
    FALSE, // bUnderline
    0, // cStrikeOut
    ANSI_CHARSET, // nCharSet
    OUT_DEFAULT_PRECIS, // nOutPrecision
    CLIP_DEFAULT_PRECIS, // nClipPrecision
    DEFAULT_QUALITY, // nQuality
    DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
    "Arial")); // lpszFacename

  CFont *oF=pDC->SelectObject(&font);
  pDC->SetBkColor(GetSysColor(COLOR_BTNFACE));
  pDC->TextOut(rect.left+sizeTitleOffset.cx, 
                  rect.top+sizeTitleOffset.cy, m_strTitle);
  CSize sz=pDC->GetTextExtent(m_strTitle);

  // Draw the docking grippers
  CRect rectGrip1(rect.left+sz.cx+2*sizeTitleOffset.cx,7,rect.right-10,7+3);
  pDC->Draw3dRect(rectGrip1, RGB(255,255,255), 
                     RGB(128,128,128));
  CRect rectGrip2(rect.left+sz.cx+2*sizeTitleOffset.cx,11,rect.right-10, 11+3);
  pDC->Draw3dRect(rectGrip2, RGB(255,255,255), RGB(128,128,128));
}

栏有一个小问题:如果你反复双击栏来停靠和取消停靠它,它会逐渐滑到 View 的边距之下!手动将它移回原位很容易,但这很烦人,如果有人知道如何解决这个问题,请告诉我。

就这样! 这不是火箭科学,但可能有用。

© . All rights reserved.