工具栏编程小结

    技术2022-05-11  150

    FAQ1: 如何为工具栏添加CHEVRON

    FAQ2: 如何实现工具栏按钮的拖拽

     

     

    最近一直很忙,没空把新的知识沉淀、更好的消化。今天就把有关工具栏编程的知识汇总一下。那些教科书上有的东西就不写了,呵呵!

     

     

    如何为工具栏添加CHEVRON

     

    先说一下如何为工具栏添加CHEVRON,就是当工具栏按钮的长度超过了窗口能显示的长度时,在工具栏的右侧自动添加一小按钮,点击后可以把后面未显示的工具栏按钮以菜单的形式显示出来。点击此下载相关的代码

           实际上,这是个重组栏(Rebar)提供的功能,首先需为Rebar添加RBBS_USECHEVRON风格。请看下面的代码:

     

                  REBARBANDINFO rbi;    

     

                  memset( &rbi , 0 , sizeof(rbi) ) ;

     

                  rbi.cbSize = sizeof( REBARBANDINFO ) ;

     

                  rbi.fMask  = RBBIM_STYLE ;

     

                  m_Rebar.GetBandInfo( index , &rbi ) ;

     

                  rbi.fStyle |= ( RBBS_USECHEVRON | RBBS_BREAK ) ;  // RBBS_BREAK风格不是必需的!

     

                  m_Rebar.SetBandInfo( index , &rbi ) ;            

     

          

     

    或者可以通过另一种方式来添加RBBS_USECHEVRON风格:

     

           m_wndReBar.AddBar(&m_wndToolBar, NULL, NULL, RBBS_USECHEVRON )

     

          

     

           然后为ReBar指定正常显示的宽度,即当ReBar的实际长度超过这个宽度就自动出现CHEVRON

     

    REBARBANDINFO rbi;    

     

                  memset( &rbi , 0 , sizeof(rbi) ) ;

     

                  rbi.cbSize = sizeof( REBARBANDINFO ) ;               

     

                  rbi.fMask  = RBBIM_IDEALSIZE ;                    

     

                  rbi.cxIdeal = GetToolbarWidth() ;

     

                  m_Rebar.SetBandInfo( index , &rbi ) ;

     

     

     

           看看GetToolbarWidth()的代码:

     

                  int nWidth = 0 ;

     

           int n = GetToolBarCtrl().GetButtonCount() ;

     

                  for( int i = 0 ; i < n ; i ++ )

     

           {

     

                         CRect rtItem ;

     

                  GetItemRect( i , &rtItem ) ;

     

                         nWidth += rtItem.Width() ;

     

           }

     

                  return nWidth ;

     

     

     

           显然,这是个CToolbar派生类的成员函数!通过它来得到工具栏所有按钮的宽度的总和,这里包括了隐藏按钮的宽度,如果你用到了隐藏按钮,可能最终的效果和你期望的有一点出入。你还可以用CToolbarCtrl::GetMaxSize这个函数得到工具栏的宽度(此函数不计算隐藏按钮)。我不用GetMaxSize是有原因的,因为我做的工具栏长度是动态改变的,它不断的添加或删除某个按钮。我发现在这种情况下,GetMaxSize并不能得到准确的数据!使得在该出现CHEVRON时并不出现,或者在完全有足够长度显示按钮时也显示CHEVRON

     

           当然,你可以把两个SetBandInfo调用集中在一起,这样效率会更好。但我所做的工具栏的长度是在不断变化的,每次改变时都要重设cxIdeal,所以我把设置cxIdeal提取出来独立成一个函数。

     

           还有一个相关的风格:TBSTYLE_EX_HIDECLIPPEDBUTTONS。此风格是工具栏(不是ReBar)的扩展风格哦,通过CToolBarCtrl::SetExtendedStyle设置。设置了该风格后,工具栏上无法完全显示的按钮就会隐藏,并且加到CHEVRON的菜单中。

     

           最后声明一点,文中的代码与提供的例子并不是同一个代码!这个例子是我从网络上搜索来的。

     

     

     

     

     

    如何实现工具栏按钮的拖拽

     

           再写一个关于工具栏按钮的拖拽。

     

    有两个工具栏通知消息:TBN_BEGINDRAGTBN_ENDDRAG。关于这两个通知消息书上介绍的很少,网上也不多见!下面的代码介绍了如何处理该通知消息来实现工具栏按钮的拖拽:

     

     

     

    TBN_BEGINDRAG通过消息的处理:

     

    void CMyToolbar::OnTbnBeginDrag(NMHDR *pNMHDR, LRESULT *pResult)

     

    {     

     

           LPNMTOOLBAR pNMTB = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);          

     

           m_DragedID = pNMTB->iItem ;            // m_DragedID 保存了被拖的按钮ID

     

           m_bDragButton = TRUE ;                     // m_bDragButton BOOL变量,是个拖拽标志

     

           *pResult = 0;

     

    }

     

     

     

    注意:这里不用调用SetCapture哦!应该是默认的消息处理已经帮我们调用了SetCapture

     

     

     

    TBN_ENDDRAG通知消息的处理:

     

    void CMyToolbar::OnTbnEndDrag(NMHDR *pNMHDR, LRESULT *pResult)

     

    {

     

           LPNMTOOLBAR pNMTB = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);   

     

           if( m_bDragButton )

     

           {

     

                  //

     

                  // 放处理

     

                  //

     

                  CPoint point ;

     

                  ::GetCursorPos( &point ) ;

     

                  ScreenToClient( &point ) ;

     

                 

     

                  // 先检查鼠标是否在工具栏窗体内             

     

                  CRect rc;

     

                  GetClientRect( &rc ) ;

     

                  if( rc.PtInRect( point ) )

     

                  {

     

                         //

     

                         // 检查鼠标在哪个按钮之上,是否在搜索按钮上

     

                         //

     

                         int index = GetToolBarCtrl().HitTest( &point ) ;

     

                         UINT id = GetItemID( index ) ;

     

                         if( id >= 0 )

     

                         {                          

     

                                // 拖放对象不是同一个                         

     

                                if( id != m_DragedID )

     

                                {

     

                                       // 调换位置

     

                                       // ID=m_DragedID的按钮放在ID=id的前面(后面也可以,看你的需求)

     

                                      

     

                                }                          

     

                         }

     

                 

     

                  }

     

     

     

     

     

                  m_DragedID = 0 ;

     

                  m_bDragButton = FALSE ;           

     

                  SetCursor( LoadCursor( NULL,IDC_ARROW ) ) ;

     

                  InvalidateRect(NULL);

     

           }

     

           *pResult = 0;

     

    }

     

     

     

     

     

    最后是WM_MOUSEMOVE消息的处理,将拖的过程反馈给用户:

     

    void CMyToolbar::OnMouseMove(UINT nFlags, CPoint point)

     

    {

     

    if ( m_bDragButton )

     

           {

     

                  //

     

                  // 先检查鼠标是否在工具栏窗体内

     

                  //

     

                  CRect rc;

     

                  GetClientRect( &rc ) ;

     

                  if( rc.PtInRect( point ) )

     

                  {

     

                         //

     

                         // 检查鼠标在哪个按钮之上

     

                         //

     

                         int index = GetToolBarCtrl().HitTest( &point ) ;

     

                         UINT id = GetItemID( index ) ;

     

     

     

                         if( id >= 0 )

     

                         {

     

                                //

     

                                // 拖放对象不是同一个

     

                                //

     

                                if( id != m_DragedID )

     

                                {

     

                                       //

     

                                       // 在放目标按钮的前面画一条2象素的竖线,表示要将某个按钮拖到该按钮的前面!

     

                                       // 这里要怎么画,你可以凭自已的想象力,目的就是要让用户知道被拖的按钮即将被放在目标按钮的旁

     

                                       // 边,可以是前面或后面。可以画竖线,也可以画其它的。

     

                                       //

     

                                       CRect rtItem ;

     

                                       GetItemRect( index , &rtItem ) ;

     

                                       rtItem.right = rtItem.left + 2 ;

     

     

     

                                       CClientDC dc (this);

     

                                       dc.FillSolidRect( &rtItem , RGB(0,0,0) );

     

                                }

     

     

     

                                SetCursor( LoadCursor( ::AfxGetInstanceHandle() , (LPCTSTR)IDC_CURSOR_DROP_BUTTON )  ) ;

     

                         }

     

                        

     

                         else

     

    {

     

                                SetCursor( LoadCursor( NULL,IDC_NO ) ) ;

     

                         }

     

                        

     

                  }

     

                  //

     

                  // 不能放的区域

     

                  //

     

                  else

     

                  {

     

                         SetCursor( LoadCursor( NULL,IDC_NO ) ) ;

     

                  }

     

                 

     

     

     

           }

     

     

     

           CToolBar::OnMouseMove(nFlags, point);

     

    }

     

     

     

    上面对三个消息的处理并不复杂,如果你曾经或常常为TREELIST控件写拖拽的代码,相信你很容易就可以理解。

     


    最新回复(0)