Windows平台提供了丰富的控件,但是在使用中我们不会使用它提供的默认风格,有时候需要对控件进行改写,让它展现出更友好的一面,这次主要是说明三态按钮的实现。

  三态按钮指的是按钮在鼠标移到按钮上时显示一种状态,鼠标在按下时展现一种状态,在鼠标移开时又展现出另外一种状态,总共三种。当然鼠标按下和移出按钮展示的状态系统自己提供的有,这个时候在处理这两种状态只需要贴相应的图片就行了,三态按钮的实现关键在于如何判断鼠标已经移动到按钮上以及鼠标移出按钮,然后根据鼠标的位置将按钮做相应的调整。

  判断鼠标在按钮的相应位置,系统提供了一个函数_TrackMouseEvent用户处理鼠标移出、移入按钮。函数原型如下: 

  1. BOOL _TrackMouseEvent(
  2. LPTRACKMOUSEEVENT lpEventTrack
  3. );

函数需要传入一个TRACKMOUSEEVENT类型的指针,该结构的原型如下:

  1. typedef struct tagTRACKMOUSEEVENT {
  2. DWORD cbSize;//该结构体所占空间大小
  3. DWORD dwFlags;//指定服务的请求(指定它需要侦听的事件),这次主要用到的是TME_HOVER和TME_LEAVE(侦听鼠标移开和移入事件)
  4. HWND hwndTrack;//指定我们需要侦听的控件的句柄
  5. DWORD dwHoverTime;//HOVER消耗的时间,可以用系统提供的一个常量HOVER_DEFAULT由系统默认给出,也可以自己填写,单位是毫秒
  6. } TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;

  在使用该函数时需要包含头文件commctrl.h和lib文件comctl32.lib

  解决了鼠标行为的检测之后,就是针对不同的鼠标行为重绘相应的按钮。重绘按钮需要在消息WM_DRAWITEM中,这个消息的处理是在相应控件的父窗口中实现的,而在一般情况下父窗口不会收到该消息,需要我们手工指定控件资源的属性为的OWNERDRAW为真,或者在创建相应的按钮窗口时将样式设置为BS_OWNERDRAW 。

设置完成后就可以在对应的父窗口处理函数中接收并处理WM_DRAWITEM,在该消息中重绘按钮

该消息中主要使用的参数是lpParam它里面包含的是一个指向DRAWITEMSTRUCT的结构体:

  1. typedef struct tagDRAWITEMSTRUCT {
  2. UINT CtlType; //控件类型
  3. UINT CtlID; //控件ID
  4. UINT itemID; //子菜单项的ID主要用于菜单
  5. UINT itemAction; //控件发出的动作,如ODA_SELECT表示控件被选中
  6. UINT itemState; //控件状态,这次需要用到的状态为ODS_SELECTED表示按钮被按下
  7. HWND hwndItem; //控件句柄
  8. HDC hDC;
  9. RECT rcItem;//控件的矩形区域
  10. ULONG_PTR itemData;
  11. } DRAWITEMSTRUCT;
    //该结构体中的一些成员需要根据控件类型赋值,同时结构体中的itemAction、itemState是可以由多个值通过位或组成在判断是否具有某种状态时需要使用位与运算

而绘制控件时我们可以使用函数DrawFrameControl,该函数可以根据指定的控件类型、控件所处的状态来绘制控件的样式,绘制出来的任然是系统的之前的标准样式,处理WM_DRAWITEN消息的具体代码如下:

  1. LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
  2.  
  3. char szBuf[50];
  4. GetWindowText(lpdis->hwndItem,szBuf,50);
  5. if (ODT_BUTTON ==lpdis->CtlType)
    {
  6.   UINT uState = DFCS_BUTTONPUSH;
  7.   if (lpdis->itemState & ODS_SELECTED)
  8.   {
  9.     uState |= DFCS_PUSHED;
  10.   }
  11.  
  12.   DrawFrameControl(lpdis->hDC,&(lpdis->rcItem),DFC_BUTTON,uState);
  13.   SetTextColor(lpdis->hDC,RGB(255,0,0));
  14.   DrawText(lpdis->hDC,szBuf,strlen(szBuf) + 1,&(lpdis->rcItem),DT_CENTER |
  15.   DT_VCENTER | DT_SINGLELINE);
  16. }

函数_TrackMouseEvent根据其检测的鼠标状态不同可以返回不同的消息,这次主要用的是WM_MOUSEHOVER(表示鼠标移动到按钮上)、WM_MOUSELEAVE(鼠标移出按钮),还需要注意的是这个函数每次检测完成返回后不会再次检测,需要我们自己主动调用函数检测鼠标状态,由于要多次调用,而每次调用都需要初始化所需要的结构体指针,所以我们封装一个函数专门用于调用_TrackMouseEvent:

  1. void Track(HWND hWnd)
  2. {
  3. TRACKMOUSEEVENT tme;
  4. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  5. tme.dwFlags = TME_HOVER | TME_LEAVE;
  6. tme.dwHoverTime = 10;
  7. tme.hwndTrack = hWnd;
  8. _TrackMouseEvent(&tme);
  9. }

消息WM_MOUSEHOVER和消息WM_MOUSELEAVE的处理是在对应的窗口过程中处理的,而按钮的窗口过程由系统提供我们并不知道,所以只有使用子类化的方法在我们的窗口过程中处理这两个消息。在按钮创建后立马要检测鼠标所以可以按钮对应的父窗口完成创建后子类化,对于窗口可以在它的WM_CREATE消息中处理,对于对话框可以在WM_INITDIALOG消息中处理,子类化调用函数SetWindowLong:

  1. g_OldProc = (LRESULT*)SetWindowLong(GetDlgItem(hDlg,IDC_BUTTON1),GWL_WNDPROC,(LONG)BtnProc);
  2. return 0;

在新的窗口过程中处理消息,完成三态按钮:

  1. switch (uMsg)
  2. {
  3.   case WM_MOUSEMOVE:
  4.     Track(hBtn);//当鼠标移动时检测
  5.     break;
  6.   case WM_MOUSEHOVER:
  7.   {
  8.     char szBuf[50];
  9.     RECT rtBtn;
        GetClientRect(hBtn,&rtBtn);
  10.     HDC hDc = GetDC(hBtn);
  11.     DrawFrameControl(hDc,&(rtBtn),DFC_BUTTON,DFCS_BUTTONPUSH);
  12.     HBRUSH hBr = CreateSolidBrush(RGB(255,255,255));
  13.     FillRect(hDc,&rtBtn,hBr);
  14.     GetWindowText(hBtn,szBuf,50);
  15.     SetBkMode(hDc,TRANSPARENT);
  16.     DrawText(hDc,szBuf,strlen(szBuf),&rtBtn,DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  17.     ReleaseDC(hBtn,hDc);
  18.   }
  19.    break;
  20.   case WM_MOUSELEAVE:
  21.   {
  22.     char szBuf[50];
  23.     RECT rtBtn;
  24.     GetClientRect(hBtn,&rtBtn);
  25.     HDC hDc = GetDC(hBtn);
  26.     DrawFrameControl(hDc,&(rtBtn),DFC_BUTTON,DFCS_BUTTONPUSH);
  27.     GetWindowText(hBtn,szBuf,50);
  28.     SetBkMode(hDc,TRANSPARENT);//设置字体背景为透明
  29.     DrawText(hDc,szBuf,strlen(szBuf),&rtBtn,DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  30.     ReleaseDC(hBtn,hDc);
  31.   }
  32.     break;
  33.   default:
  34.     return CallWindowProc((WNDPROC)g_OldProc,hBtn,uMsg,wParam, lParam);//在处理完我们感兴趣的消息后一定要记得将按钮的窗口过程还原
  35.   }
  36. return 0;

到这个地方为止,已经实现了三态按钮的基本样式,通过检测鼠标的位置设置按钮样式,上述代码只是改变了按钮的背景颜色和文字颜色,可能效果不好看。

SDK平台三态按钮的实现的更多相关文章

  1. 开发前奏曲之添加Android SDK平台工具

    原文:http://android.eoe.cn/topic/android_sdk Android SDK分离不同部位的SDK成单独的下载包.您已经安装只包含SDK工具的SDK入门包.要开发一个An ...

  2. 【WPF】使用 XAML 的 Trigger 系统实现三态按钮

    利用 WPF 的 Trigger 系统,也可以很简单的只使用xmal实现三态按钮.在Window或UserControl的资源中声明按钮的style并加入触发功能.使用的时候直接在button里复写s ...

  3. kinect学习笔记(二)—— Sdk平台的搭建~、

    一.资源下载        由于我们使用的kinect v1.0,所以我们只需要使用1.8版本的sdk就好了,然后资源包,在QQ群的共享里面已经有啦,所以大家可以直接下载. 二.软件安装        ...

  4. WPF 三态按钮(PNG贴图)

    原文 http://blog.csdn.net/power_YQ/article/details/7177183 <Window.Resources> Style x:Key=" ...

  5. Android 开发工具介绍-SDK工具和平台工具

    原文链接:http://android.eoe.cn/topic/android_sdk Android的SDK提供各种工具可以帮你为Android平台开发移动应用程序.这些工具被分类成两组:SDK工 ...

  6. android sdk 更新那些文件

    上篇经验,完成了android开发环境的搭建,相信大家也下载了那1.52G,已经下载好了的Adt_bundle. 那么,我们来点击SDK Manager.exe,看看有些什么吧 2 如图所示,为整个目 ...

  7. Zedboard(二)使用Vivado+SDK开发嵌入式应用程序——实例一

    本次介绍用Vivado构建Zedboard开发板的硬件平台+SDK开发应用程序(Zedboard裸机开发) 过程如下: 一.运行Vivado,建立新工程 指定好工程路径,下一步,选择RTL Proje ...

  8. Android SDK下载安装及配置教程

    2017年12月07日 13:33:32 4942 转载自:http://blog.csdn.net/dr_neo/article/details/49870587 Android开发环境搭建可以分为 ...

  9. 最新鲜最详细的Android SDK下载安装及配置教程

    //来源: http://www.cnblogs.com/summary-2017/p/8073225.html 最近Neo突发神经,想要将学过的一些计算机视觉.机器学习中的算法都放到移动设备上去跑跑 ...

随机推荐

  1. 当你在试衣间试衣服,请你务必想起wait()与notify()

    在学习多线程的时候,你无法逃避sleep.wait.notify.notifyAll的关键字,我们肯定是对sleep用的最多,即使你写个Demo也要经常用到Thread.sleep(xxx)模拟等待的 ...

  2. HashMap完全解读

    一.什么是HashMap 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Has ...

  3. Hibernate缓存配置

    一级缓存 Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,当程序调用save(),update(),saveorupdate()等方法 及调用查询接口l ...

  4. jQuery事件命名空间

    先看一些代码: 也可以用bind进行事件绑定.我们看到上面的代码,我们可以在事件后面,以点号,加我们的名字,就是事件命名空间.所谓事件命名空间,就是事件类型后面以点语法附加一个别名,以便引用事件,如& ...

  5. 终于了解了User-Agent的历史了

    你是否好奇标识浏览器身份的User-Agent,为什么每个浏览器都有Mozilla字样? 1 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.3 ...

  6. [MFC] 对话框菜单项Menu选中打勾(单选,多选)

    近期需要实现一个功能:MFC对话框中,一项菜单下有五个菜单项,改变菜单项选中状态,每次只能选择其中一个打勾.(单选) 然后在网上搜了下资料,稍微总结下,以防后面用到. 1.单选实现: CMenu* m ...

  7. NOIP2014-提高组初赛C语言解析(选择填空题)

    第二十届全国青少年信息学奥林匹克联赛初赛 一.单项选择题(共 20 题,每题 1.5 分,共计 30 分.每题有且仅有一个正确选项) 1. 以下哪个是面向对象的高级语言( B ) A.汇编语言   B ...

  8. ngDialog 设置其宽度大小

    [ngdialog弹窗大小设置(angularjs)] 方法一:添加css样式属性 css: .ngdialog.ngdialog-theme-plain.custom-width-70 .ngdia ...

  9. Html5浏览器端less应用

    之前的一个布局是用rem来做的 我上一段代码 div { margin: 0.833333333rem 0; } /* 去处a标签的下划线*/ a { text-decoration: none; } ...

  10. LINQ 之Union All/Union/Intersect操作

    闪存 首页 新随笔 管理 订阅     Union All/Union/Intersect操作 适用场景:对两个集合的处理,例如追加.合并.取相同项.相交项等等. Concat(连接) 说明:连接不同 ...