如何用VC++ 5.0实现工具栏及其属性的控制

类别:VC语言 点击:0 评论:0 推荐:


如何用VC++ 5.0实现工具栏及其属性的控制
  任志民

  VC++ 5.0风格的工具栏具有平面外观,左边带有一个“把手”,你可以通过鼠标拖动这个“把手”来移动工具栏。工具栏各组间带有分隔线(如图1所示)。当鼠标在工具栏上面移动时,工具栏上的相应按钮会突出显示。本文所讨论的增强型工具栏CEnhanceToolBar类由CToolBar类所派生,是CToolBar类的补充和扩展。
  如果你并不在乎工具栏有没有“把手”的话,要生成平面工具栏是十分简单的。你只需要在CMainFrame的OnCreate()函数中添加一句话就可以(必须加在工具栏生成函数之后,因为MFC在生成工具栏时,要清除其式样):

  //MainFrm.cpp

  int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

  {

   ......

  if(!m_wndToolBar.Create(this)

  //!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

   {

   TRACE0("Failed to create toolbar\n");

   return -1; // fail to create

   }

  m_wndToolBar.ModifyStyle(0,TBSTYLE_FLAT);

  //设置工具栏为平面格式

   ......

  }

  如果你的计算机安装了4.71.1712.3版本的COMCTL32.DLL(该动态库随IE 4.0一同发行),那就更好了,你的工具栏就会自动绘制分隔线。你可以通过鼠标拖动工具栏的非按钮区域来移动这种工具栏。GetRight中的工具栏就是这样的。

  如果你要得到更好看(更“专业”)的工具栏,那么就请跟我一步步地做下去。

  

  1.添加一个新类,本文中叫做CEnhanceToolBar类,由CToolBar类派生。

  2.为CEnhanceToolBar类添加成员变量和函数的声明,并且根据你的需要设置其访问属性。

  //EnhanceToolBar.h

  // Overrides

  // ClassWizard generated virtual function overrides

  //{{AFX_VIRTUAL(CEnhanceToolBar)

  //按钮状态变化时调用此函数

  virtual void OnUpdateCmdUI(CFrameWnd*pTarget,BOOL bDisableIfNoHndler);

  //}}AFX_VIRTUAL

  // Implementation

  public:

   void DrawGrip(CWindowDC*pDC,CRect& rectWindow);//“把手”绘制函数

   void EraseNonClient();//擦除非客户区

   void DrawSpace();//分隔线绘制函数

   void RedrawBackground();//背景重绘函数

  

  private:

   int ButtonNumber;//工具栏的按钮数(包括分隔线)

   COLORREF HiLight, Shadow;//3D控件的加亮色和阴影色

  

  3.添加成员函数的定义:

  //EnhanceToolBar.cpp

  void CenhanceToolBar :: OnUpdateCmdUI(CframeWnd * pTarget, BOOL bDisableIfNoHndler)

  {//按钮状态变化时调用此函数

   static CUIntArray Styles;

   int Index;

   UINT dwStyle;

   for (Index = 0; Index < ButtonNumber; Index++)

   {

   dwStyle = GetButtonStyle(Index);

   Styles.SetAtGrow(Index,dwStyle);//保存按钮的式样

   }

  CToolBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);

  //调用基类的处理函数

   for (Index = 0; Index < ButtonNumber; Index++)

   {//设置选中的按钮(checked button)为按下的状态

   dwStyle = GetButtonStyle(Index);

   if (dwStyle & TBBS_DISABLED)

   return;//如果按钮为禁用状态,则返回(避免闪烁)

   if (dwStyle & TBBS_CHECKBOX)

   {

   if (dwStyle & TBBS_CHECKED)

   dwStyle |= TBBS_PRESSED;

   else

   dwStyle &= ~TBBS_PRESSED;

   SetButtonStyle(Index,dwStyle);//设置按钮的式样

   }

   }

   //检查按钮的式样是否改变(按下或释放)

   for (Index = 0; Index < ButtonNumber; Index++)

   {

   dwStyle = GetButtonStyle(Index);

   if (Styles[Index] != dwStyle)

   {

   RedrawBackground();//重新绘制背景

   Invalidate();//重新绘制整个工具栏

   break;//已更新整个工具栏,因此没必要继续循环

   }

   }

  }

  

  void CEnhanceToolBar::DrawGrip(CWindowDC *pDC, CRect& rectWindow)

  {//绘制把手

   if (IsFloating())//如果工具栏是浮动状态,则不绘制“把手”

   return;

   CRect GripRect = rectWindow;//得到把手的矩形区域

   GripRect.DeflateRect(1,1);//矩形区域的各边向中心靠近一个像素

  if (m_dwStyle & CBRS_ORIENT_HORZ)

  //如果工具栏为水平状态,则“把手”在左边

   {

   GripRect.right = GripRect.left + 3;//绘制第一条隆起的棱

   pDC->Draw3dRect(GripRect, HiLight, Shadow);

   GripRect.OffsetRect(4,0);//绘制第二条隆起的棱

   pDC->Draw3dRect(GripRect, HiLight, Shadow);

   }

   else//如果工具栏为垂直状态,则“把手”在顶部

   {

   GripRect.bottom = GripRect.top + 3;//绘制第一条隆起的棱

   pDC->Draw3dRect(GripRect, HiLight, Shadow);

   GripRect.OffsetRect(0,4);//绘制第二条隆起的棱

   pDC->Draw3dRect(GripRect, HiLight, Shadow);

   }

  }

  

  void CEnhanceToolBar::DrawSpace()

  {//绘制分隔线

   CClientDC dc(this);

   for (int Index = 0; Index < ButtonNumber; Index++)

   {

   UINT dwStyle = GetButtonStyle(Index);//获得按钮的类型

   if (dwStyle & TBBS_SEPARATOR)//如果是分隔线

   {

   CRect rect;

   GetItemRect(Index,rect);//获得矩形区域

   if (m_dwStyle & CBRS_ORIENT_HORZ)

   //工具栏为水平时分隔线为垂直

   {

   int w = rect.Width();

   rect.DeflateRect((w-2)/2,0);//将矩形缩减为2~3个像素宽

   dc.Draw3dRect(rect, Shadow, HiLight);//绘制分隔线

   }

   else//分隔线为水平

   {

   rect.left = rect.left - m_sizeButton.cx;

   rect.right = rect.left + m_sizeButton.cx;

   rect.top = rect.bottom+1;

   rect.bottom = rect.top+3;

   int h = rect.Height();

   rect.DeflateRect(0,(h-2)/2);//将矩形缩减为2~3个像素高

   dc.Draw3dRect(rect, Shadow, HiLight);//绘制分隔线

   }

   }

   }

  }

  

  void CEnhanceToolBar::EraseNonClient()

  {//擦除非用户区

   CWindowDC dc(this);

   CRect rectClient;

   GetClientRect(rectClient);

   CRect rectWindow;

   GetWindowRect(rectWindow);

   ScreenToClient(rectWindow);

   rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);

   dc.ExcludeClipRect(rectClient);

  

   // 绘制非用户区的边界

   rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);

   DrawBorders(&dc, rectWindow);

  

   // 擦除非绘制部分

   dc.IntersectClipRect(rectWindow);

   SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);

   DrawGrip(&dc, rectWindow); //绘制“把手”

  }

  

  由于平面工具栏是透明的,所以当改变尺寸和移动时(例如当拖动工具栏时),就需要重绘背景。同样,当按钮状态改变时(按下或释放)也需要进行这种操作。

  

  void CEnhanceToolBar::RedrawBackground()

  {//重新绘制背景

   CWnd* pParent = GetParent();//获得父窗口指针

   if (pParent)

   {

   CRect drawrect,rect;

   GetWindowRect(&rect);//获得工具栏矩形区域

   drawrect = rect;

   pParent->ScreenToClient(&drawrect);//转换为父窗口坐标

   pParent->InvalidateRect(&drawrect);//重绘矩形区域

   //绘制父窗口的其他工具栏

   for (CWnd* pSibling = pParent->GetWindow(GW_CHILD);pSibling;

   pSibling = pSibling->GetNextWindow(GW_HWNDNEXT))

   {

   if (pSibling == this)

   continue;

   drawrect = rect;

   pSibling->ScreenToClient(&drawrect);//兄弟窗口的坐标

   pSibling->InvalidateRect(&drawrect);//重绘矩形区域

   }

   }

  }

  

  4.利用ClassWizard给CEnhanceToolBar类添加消息映射:

  //EnhanceToolBar.h

  

   //{{AFX_MSG(CEnhanceToolBar)

   afx_msg void OnPaint();

   afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp);

   afx_msg void OnWindowPosChanging(WINDOWPOS FAR* lpwndpos);

   //}}AFX_MSG

  

  //EnhanceToolBar.cpp

  

   BEGIN_MESSAGE_MAP(CEnhanceToolBar, CToolBar)

   //{{AFX_MSG_MAP(CEnhanceToolBar)

   ON_WM_PAINT()

   ON_WM_NCCALCSIZE()

   ON_WM_WINDOWPOSCHANGING()

   //}}AFX_MSG_MAP

   END_MESSAGE_MAP()

  

  5.为消息映射函数添代码:

  void CEnhanceToolBar::OnPaint()

  {

   CToolBar::OnPaint();//绘制标准工具栏

   EraseNonClient();//擦除背景

   DrawSpace();//绘制立体分隔线

  }

  

  void CEnhanceToolBar::OnNcCalcSize(BOOL bCalcValidRects,

   NCCALCSIZE_PARAMS FAR* lpncsp)

  {// 计算非用户区域,用于调整“把手”

   CToolBar::OnNcCalcSize(bCalcValidRects,lpncsp);

   if (IsFloating())//如果工具栏是浮动状态,则不绘制“把手”

   return;

  if (m_dwStyle & CBRS_ORIENT_HORZ)

  //如果工具栏为水平状态,则“把手”在左边

   {

   lpncsp->rgrc[0].left += 2;

   lpncsp->rgrc[0].right += 2;

   }

   else//如果工具栏为垂直状态,则“把手”在顶部

   {

   lpncsp->rgrc[0].top += 4;

   lpncsp->rgrc[0].bottom += 4;

   }

  }

  

  void CEnhanceToolBar::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)

  {//当尺寸、位置或Z方向次序变化时,程序框架调用此成员函数

   CToolBar::OnWindowPosChanging(lpwndpos);

   RedrawBackground();//重新绘制背景

  }

  

  6.编写构造函数,加入下面的代码:

  CEnhanceToolBar::CEnhanceToolBar()

  {

   HiLight = ::GetSysColor(COLOR_3DHILIGHT);//获得3D控件的加亮色

   Shadow = ::GetSysColor(COLOR_3DSHADOW);//获得3D控件的阴影色

  ButtonNumber = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0);

  //获得工具栏的按钮数(包括分隔线)

  }

  把以上程序编译运行后,很酷的工具栏就会出现了。下面是几点说明:

  (1)关于工具栏的其他格式请参考COMMCRTL.H。

  #define TBSTYLE_TOOLTIPS 0x0100

  #define TBSTYLE_WRAPABLE 0x0200

  #define TBSTYLE_ALTDRAG 0x0400

  #define TBSTYLE_FLAT 0x0800......

  

  (2)如果你的计算机安装了4.71.1712.3版本的COMCTL32.DLL(随IE 4.0一同发行),那么你不需要添加下列的函数,这样就会更简单。

  void DrawSpace();//分隔线绘制函数

  void RedrawBackground();//背景重绘函数

  virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler);

  void OnWindowPosChanging(WINDOWPOS FAR* lpwndpos);

  

  (3)如何在工具栏的按钮中间加入一个分隔线呢?很简单,使用资源编辑器打开应用程序的工具栏,拖动一个按钮,使其与上一个按钮离开一定距离,松开鼠标键就可以了。

  

  (4)已知的BUG有:

  *当拖动工具栏使之成为垂直停泊工具栏、再拖动它使之成为浮动工具栏、再拖动它使之成为水平停泊工具栏时,“把手”的一部分会被遮住。

  *如果你的计算机安装了4.71.1712.3版本的COMCTL32.DLL,那么当工具栏为垂直的或长度超过一行需要换行时,会出现工具栏的底边被剪切的情况。

  以上介绍了如何在应用程序中生成VC++ 5.0式样的工具栏。文中的信息适用于Microsoft Visual C++(32-bit Edition)5.0版及后继版本所包含的Microsoft Foundation Classes (MFC)。要实现这种工具栏,你必须有4.7(或以上) 版本的COMCTL32.DLL,该动态库随IE 3.0一同发行,并且将作为Windows 98的标准组件之一。VC++ 5.0中已经附带了该动态库。

  (北京市海淀区海淀路80号中科大厦 100080 任志民)


本文地址:http://com.8s8s.com/it/it3796.htm