基于MFCColorButton的MyColorButton

以下内容全部为转载:

BCG 是个很不错的界面库,MFC 传统界面的不二选择。他的绝大部分控件都相当不错,不过在一些细节地方,似乎 XtremeToolkit 还略胜一筹,比如颜色选择按钮、目录选择按钮...

他的颜色按钮,微软吸收以后命名为 CMFCColorButton,保持以前下拉列表的风格,个人不太喜欢,还是觉得 XtremeToolkit 的做的类似于按钮的做的更合我意一点。但是 XtremeToolkit 是要掏钱的,既然俺有 CMFCColorButton,那就来扩展他吧,让他来满足我们的要求。

先看看效果图:

 

这是正常时候的效果:

预览图1

 

当点击时

预览图2

 

上面左边就是标准的 CMFCColorButton,右边是俺自己扩展的新类。

 

说说实现的方法,跟各位探讨一下:

1、默认情况下,CMFCColorButton 重载了基类 CMFCButton 的 OnDraw 和 OnDrawBorder:

  1. virtual void OnDraw(CDC* pDC, const CRect& rect, UINT uiState);  
  2.   
  3. virtual void OnDrawBorder(CDC* pDC, CRect& rectClient, UINT uiState);  

他的这个弹出的颜色面板是 CMFCColorPopupMenu,每当触发的时候动态建立。

主要的绘制代码都是在 OnDraw 中实现的,OnDrawBorder 只是负责绘制焦点区,就是获得焦点时候的那个虚线框。

关键的东西就是绘制那个箭头和竖线条:

 

  1. CRect rectArrow = rect;  
  2. rectArrow.left = rectColor.right + nImageHorzMargin / 2;  
  3.   
  4. //------------  
  5. // Draw Arrow:  
  6. //------------  
  7. CMenuImages::Draw(pDC, m_bLargeArrow ? CMenuImages::IdArrowDownLarge : CMenuImages::IdArrowDown, rectArrow, (uiState & ODS_DISABLED) ? CMenuImages::ImageGray : CMenuImages::ImageBlack);  
  8.   
  9. //----------------  
  10. // Draw separator:  
  11. //----------------  
  12. CRect rectSeparator = rectArrow;  
  13. rectSeparator.right = rectSeparator.left + 2;  
  14. rectSeparator.DeflateRect(0, 2);  
  15.   
  16. if (!m_bWinXPTheme || m_bDontUseWinXPTheme)  
  17. {  
  18.     rectSeparator.left += m_sizePushOffset.cx;  
  19.     rectSeparator.top += m_sizePushOffset.cy;  
  20. }  
  21.   
  22. pDC->Draw3dRect(rectSeparator, afxGlobalData.clrBtnDkShadow, afxGlobalData.clrBtnHilite);  

 

2、这样改了之后,没有点击的请情况下看起来就好了,但是,等等,当点击按钮的时候,并没有出现默认按钮按下下压的那种效果,而 CButton 和 CMFCButton 都是有这种效果的,于是响应 OnLButtonDown 在调用基类 CMFCColorButton 响应之前先调用 CMFCButton 的点击,这样也就有了按钮下压的效果了。

 

3、下压效果是有了,可是问题来了,当点击按钮的时候,那个颜色面板也顺利的弹出来了,这时候按钮一直保持按下状态,当我们选择在其他其他点击鼠标,不选择任何颜色,仅仅关闭颜色面板,这个按下的状态并没有恢复成正常的弹起状态。怎么办?前面提到这个 CMFCColorPopupMenu 是动态建立的,关闭的时候销毁这个对象的指针。在弹出的时候,会调用 OnShowColorPopup 这个虚函数,,可惜的是他关闭的时候并不向我们的父窗口,也就是这里的基类 CMFCColorButton 传递任何消息,于是我们要做的,就是重载他

virtual void OnShowColorPopup();

重载了做什么呢?hook 他!截获他的 WM_CLOSE 消息,向我们派生出来的按钮发送一条 WM_LBUTTONUP 的消息,就这样,按钮也弹起来了,问题解决了。

 

4、最后一个问题,用按钮点击也没问题,按下、恢复的状态也都恢复得挺好,可是快捷键呢?基类 CMFCColorButton 只支持空格键和向下的方向键来打开颜色选择面板,我们可以增加一个向上的方向键来关闭颜色面板,如果此时面板有显示的话。这里的代码倒是很简单:

  1. void CXColorButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)  
  2. {  
  3.     switch (nChar)  
  4.     {  
  5.         case VK_SPACE:  
  6.         case VK_DOWN:  
  7.             PostMessage(WM_LBUTTONDOWN);  
  8.             return;  
  9.         case VK_UP:  
  10.             if (m_pPopup != NULL && m_pPopup->GetSafeHwnd() != NULL)  
  11.             {  
  12.                 m_pPopup->SendMessage(WM_CLOSE);  
  13.                 m_pPopup = NULL;  
  14.             }  
  15.             return;  
  16.         default:  
  17.             break;  
  18.     }  
  19.   
  20.     CMFCButton::OnKeyDown(nChar, nRepCnt, nFlags);  
  21. }  

这里的 m_pPopup 就是那个颜色选择面板 CMFCColorPopupMenu 的指针。

 

至此,所有问题都已经解决,效果几乎可以说是完美 :)

 

后记:提到 hook 消息,不得不感谢 PJ Naughter,他写的 CHookWnd 让 programer 从繁琐的 GetWindowLong 和 SetWindowLong 中解放了出来,只需要子类化 HookWnd 即可。当然,这个东西也不是说拿来就很好套用的,我自己把它扩展了一下,核心思想是通过一个 CObject 的指针来访问不同的类。如果声明为友元类,比如我们这个例子,把 CXXXHooker 声明为我们子类化的 CXColorButton 的 friend class,那就更是得心应手随心所欲了,嘿嘿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值