停靠工具栏中的属性表






4.06/5 (7投票s)
解释了如何将 CPropertySheet 放入 CControlBar。
引言
我想在可停靠工具栏中显示一个属性表,我希望该表在停靠和未停靠状态下都有一个标题,以便用户知道它是关于什么的,并且我希望它停靠到 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 的边距之下!手动将它移回原位很容易,但这很烦人,如果有人知道如何解决这个问题,请告诉我。
就这样! 这不是火箭科学,但可能有用。