网上找了很多,可只是给出代码,没有详细解释,不便初学者理解.我就抄回冷饭.把这个再拿出来说说.
实例图片:
首先建立一个标准的Win32 Application 工程.选择a simple Win32 Application.
然后建立我们的资源文件首先新建一个对话框资源,资源ID改为IDD_MAIN_DLG
然后在其上新建一个按钮控件资源ID改为IDC_ODBUTTON,此按钮的styles中必须选中owenerdraw属性.
然后将其保存为.rc的资源文件.并将其导入我们的工程.同理新建一个图标文件资源ID改为IDI_OWNERDRAW保存为.ico的图标然后导入.
准备工作做完了下面开始写代码.
首先声明如下全局变量.
#include "stdafx.h"
#include "resource.h"
HINSTANCE odInst = NULL;  //接收程序实例的句柄
HWND hMainWnd = NULL;     //接收主窗口的句柄
HWND hDlgNow = NULL;      //接收对话框的句柄
staticHICON hOwnerDrawIcon = NULL;  //用作自绘按钮的图标
staticLONG prev_proc;                 //储存按钮先前的回调函数
staticHICON hIcon = NULL;           //对话框图标句柄
然后开始写WinMain()函数
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    odInst = hInstance;
    
    WNDCLASS  wc;
    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC)ODWndProc; //定义一个窗口默认函数,这里我们会交由默认窗口函数处理
wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(odInst,MAKEINTRESOURCE(IDI_OWNERDRAW));
    wc.hCursor       = NULL;
    wc.hbrBackground = 0;
    wc.lpszClassName = "OwnerDraw";
    wc.lpszMenuName     = NULL;

RegisterClass(&wc);

MSG msg;
    
    HWND onlywin= FindWindow("OwnerDraw","MyOwnerDraw");

if(onlywin)
        {
        ExitProcess(1);
        }
    
    hMainWnd=CreateWindow("OwnerDraw","MyOwnerDraw",WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hInstance,NULL);

if(!hMainWnd)
        {
            returnFALSE;
        }

hDlgNow = DoMainDlg(hMainWnd);
    ShowWindow(hDlgNow, nCmdShow);

while(GetMessage(&msg, NULL, 0, 0)) 
    {
        if(NULL == hDlgNow || !IsDialogMessage(hDlgNow, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

returnmsg.wParam;
}

首先注册一个标准的窗口类,的WNDCLASS结构体,默认的窗口过程为ODWndProc.其定义如下.
LRESULT CALLBACK ODWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);//返回系统默认的窗口过程
}
然后判断有无相同实例存在如有则结束之
HWND onlywin= FindWindow("OwnerDraw","MyOwnerDraw");

if (onlywin)
        {
        ExitProcess(1);
        }

接下来创建主窗口
hMainWnd=CreateWindow("OwnerDraw","MyOwnerDraw",WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hInstance,NULL);
需要注意的是我们这里并不调用ShowWindow()和UpdateWindow();因为我们不需要显示主窗口
hDlgNow = DoMainDlg(hMainWnd);
    ShowWindow(hDlgNow, nCmdShow);
这里调用DoMainDlg函数创建一个对话框并显示之. DoMainDlg函数实现如下.
HWND DoMainDlg(HWND parent)
{
    DWORD dwErr;
    HWND hRet = CreateDialog(odInst, (LPCTSTR)IDD_MAIN_DLG, parent, (DLGPROC)MainDlgProc);
    if(hRet == NULL)
        dwErr = GetLastError();

return hRet;

}

最后为消息循环
while(GetMessage(&msg, NULL, 0, 0)) 
    {
        if (NULL == hDlgNow || !IsDialogMessage(hDlgNow, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
其中IsDialogMessage(hDlgNow, &msg)主要作用是把指定对话框的消息,路由给其处理.
下面是对话框窗口的默认消息响应回调函数MainDlgProc
我这里主要讲响应WM_DRAWITEM消息与WM_INITDIALOG..
首先是响应WM_INITDIALOG
case WM_INITDIALOG:
            
            if(hIcon == NULL)
                hIcon = LoadIcon(odInst, MAKEINTRESOURCE(IDI_OWNERDRAW));
            
            if(hOwnerDrawIcon == NULL)
                hOwnerDrawIcon = (HICON)LoadImage(odInst, 
                                        MAKEINTRESOURCE(IDI_OWNERDRAW), 
                                        IMAGE_ICON, 
                                        38,
                                        38,
                                        0);
            prev_proc = SetWindowLongPtr(GetDlgItem(hDlg, IDC_ODBUTTON), GWLP_WNDPROC, (LONG)ButtWindProc);
            
            SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);

SetFocus(GetDlgItem(hDlg, IDC_ODBUTTON));
            
            
            break;

首先为对话框加载一个图标,我这里图省事全部都用了一个图标,在实际应用中.可以随需要更换.
然后是为自绘按钮加载图标.接下来改变默认的自绘按钮的窗口过程.将原按钮过程存与prev_proc中.
最后发送WM_SETICON消息设置对话框图标和设置焦点.
接下来是响应WM_DRAWITEM消息,需要说明的是这个消息必须要设置了BS_OWNERDRAW
我们用记事本打开我们的对话框资源文件会看到类似下面的设置
IDD_MAIN_DLG DIALOG DISCARDABLE  0, 0, 250, 142
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 10, "System"
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,193,7,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,193,24,50,14
    CONTROL         "OwnerDraw",IDC_ODBUTTON,"Button",BS_OWNERDRAW | 
                    WS_TABSTOP,49,31,79,26
END
此处资源文件中的BS_OWNERDRAW即对应创建按钮时选中的Ownerdraw属性.之所以这样作是因为只有这样
对话框才能响应WM_DRAWITEM消息.下面为代码.
case WM_DRAWITEM:
            {
                LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam;
                //声明一个指向DRAWITEMSTRUCT结构体的指针并将其指向存储着按钮构造信息的lParam

if(lpDIS->CtlID != IDC_ODBUTTON)
                    return(0);
                
                HDC dc = lpDIS->hDC; //用于按钮绘制的DC
BOOL bIsPressed  = (lpDIS->itemState & ODS_SELECTED);
                BOOL bIsFocused  = (lpDIS->itemState & ODS_FOCUS);
                BOOL bIsDisabled = (lpDIS->itemState & ODS_DISABLED);
                BOOL bDrawFocusRect = !(lpDIS->itemState & ODS_NOFOCUSRECT);
                //判断按钮各种状态的BOOL值
RECT itemRect = lpDIS->rcItem; //按钮的矩形区域

SetBkMode(dc, TRANSPARENT); //设置绘制按钮时的背景状态
if(bIsFocused)  //判断按钮是否获得了焦点并对其边框进行处理
{
                    HBRUSH br = CreateSolidBrush(RGB(0,0,0));  
                    FrameRect(dc, &itemRect, br);
                    InflateRect(&itemRect, -1, -1);
                    DeleteObject(br);
                } // if

COLORREF crColor = GetSysColor(COLOR_BTNFACE);//得到系统按钮颜色

HBRUSH    brBackground = CreateSolidBrush(crColor);//创建画刷

FillRect(dc, &itemRect, brBackground);//绘制按钮

DeleteObject(brBackground);
                
                // 这里画被按下去的按钮
if(bIsPressed)
                {
                    HBRUSH brBtnShadow = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
                    FrameRect(dc, &itemRect, brBtnShadow);
                    DeleteObject(brBtnShadow);
                }
                
                else //如果没有被按下就这样画
{
                    UINT uState = DFCS_BUTTONPUSH |
                        ((bIsPressed) ? DFCS_PUSHED : 0);
                    
                    DrawFrameControl(dc, &itemRect, DFC_BUTTON, uState);
                }
                
                charsTitle[100];
                GetWindowText(GetDlgItem(hDlg, IDC_ODBUTTON), sTitle, 100);//得到按钮的文本

RECT captionRect = lpDIS->rcItem;//把文本的区域设置为按钮区域

BOOL bHasTitle = (sTitle[0] !='/0');//按钮上是否有文本存在
                
//这里画按钮上的图标,具体实现见下面
(GetDlgItem(hDlg, IDC_ODBUTTON), &dc, bHasTitle, 
                    &lpDIS->rcItem, &captionRect, bIsPressed, bIsDisabled);
                
                
                
                if(bHasTitle)//如果按钮有文本标题
{
                    // 按钮被按下的处理
if(bIsPressed)
                        OffsetRect(&captionRect, 1, 1);
                    
                    // 将文本居中
RECT centerRect = captionRect;
                    DrawText(dc, sTitle, -1, &captionRect, DT_WORDBREAK | DT_CALCRECT|DT_CENTER);
                    LONG captionRectWidth = captionRect.right - captionRect.left;
                    LONG captionRectHeight = captionRect.bottom - captionRect.top;
                    LONG centerRectWidth = centerRect.right - centerRect.left;
                    LONG centerRectHeight = centerRect.bottom - centerRect.top;
                    OffsetRect(&captionRect, (centerRectWidth - captionRectWidth)/2, (centerRectHeight - captionRectHeight)/2);
                    
                    
                    
                    SetBkMode(dc, TRANSPARENT);
                    
                    if(bIsDisabled)//如果按钮被禁用
{
                        OffsetRect(&captionRect, 1, 1);
                        SetTextColor(dc, ::GetSysColor(COLOR_3DHILIGHT));
                        DrawText(dc, sTitle, -1, &captionRect, DT_WORDBREAK | DT_CENTER);
                        OffsetRect(&captionRect, -1, -1);
                        SetTextColor(dc, ::GetSysColor(COLOR_3DSHADOW));
                        DrawText(dc, sTitle, -1, &captionRect, DT_WORDBREAK | DT_CENTER);
                    } 
                    else //如果没被禁用正常画
{
                        SetTextColor(dc, ::GetSysColor(COLOR_BTNTEXT));
                        SetBkColor(dc, ::GetSysColor(COLOR_BTNFACE));
                        DrawText(dc, sTitle, -1, &captionRect, DT_WORDBREAK | DT_CENTER);
                    } 
                    
                }
                
                // 画按钮得到焦点时的虚线方框
if(bIsFocused && bDrawFocusRect)
                {
                    RECT focusRect = itemRect;
                    InflateRect(&focusRect, -3, -3);
                    DrawFocusRect(dc, &focusRect);
                } // if
return(TRUE);
            }
            break;

到此WM_DRAWITEM消息响应完毕.下面我们看看DrawTheIcon这个函数.
static void DrawTheIcon(HWND hButtonWnd, HDC* dc, BOOL bHasTitle, RECT* rpItem, RECT* rpTitle, BOOL bIsPressed, BOOL bIsDisabled)
{
    RECT    rImage;
    PrepareImageRect(hButtonWnd, bHasTitle, rpItem, rpTitle, bIsPressed, 38, 38, &rImage);
    
    // 调用API函数按准备好的形式将图片画到按钮上
DrawState(    *dc,
        NULL,
        NULL,
        (LPARAM)hOwnerDrawIcon,
        0,
        rImage.left,
        rImage.top,
        (rImage.right - rImage.left),
        (rImage.bottom - rImage.top), 
        (bIsDisabled ? DSS_DISABLED : DSS_NORMAL) | DST_ICON);
}
还有其中的PrepareImageRect函数
static void PrepareImageRect(HWND hButtonWnd, BOOL bHasTitle, RECT* rpItem, RECT* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, RECT* rpImage)
{
    RECT rBtn;
    
    CopyRect(rpImage, rpItem);
    
    
    GetClientRect(hButtonWnd, &rBtn);
    if (bHasTitle == FALSE)//如果按钮上有文本内容
{
        // 使图片水平居中
LONG rpImageWidth = rpImage->right - rpImage->left;
        rpImage->left += ((rpImageWidth - (long)dwWidth)/2);
    }
    else
    {   //控制图片与焦点方框内部
LONG rpTitleWidth = rpTitle->right - rpTitle->left;
        rpTitle->right = rpTitleWidth - dwWidth - 30;
        rpTitle->left = 30;
        rpImage->left = rBtn.right - dwWidth - 22;
        
        LONG rpImageHeight = rpImage->bottom - rpImage->top;
        rpImage->top += ((rpImageHeight - (long)dwHeight)/2);
    }
    if(bIsPressed)//按钮被按下的处理
OffsetRect(rpImage, 1, 1);
    
}
行了到这里主要的工作都作完了还要说明的就是ButtWindProc这个按钮窗口的回调函数.写它的主要目的是为了
实现按钮的连续单击,在此函数中我们处理了WM_LBUTTONDBLCLK鼠标双击事件,并将其转化为一个单击事件.像这样:
LRESULT CALLBACK ButtWindProc(
                              HWND hWnd,                            //window handle                   
                              UINT message,                         // type of message                 
                              WPARAM wParam,                        // additional information          
                              LPARAM lParam)                        //additional information          
{
    switch (message)
    {
    case WM_LBUTTONDBLCLK:
        PostMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam);
        break;
    
    }
    //将不做处理的消息路由给原默认函数
returnCallWindowProc((WNDPROC)prev_proc, hWnd, message, wParam, lParam);
    
}
下面只需再响应WM_SETICON, WM_SYSCOMMAND, WM_COMMAND.这三个没什么好说的.前两个交由系统默认过程处理,最后一个对应对话框上的确定和取消,都是销毁窗口的行为.
case WM_SETICON:
                DefWindowProc(hDlg, message, wParam, lParam);
            break;
    
    case WM_SYSCOMMAND:
                {
                    return DefWindowProc(hDlg, message, wParam, lParam);
                }
    case WM_COMMAND:
                switch (LOWORD(wParam))
                {
                case IDCANCEL:
                case IDOK:
                    
                    DestroyIcon(hOwnerDrawIcon);
                    
                    PostQuitMessage(0);
                    return TRUE;
                }//switch
                break;
本文部分内容参考自http://www.codeproject.com/buttonctrl/nativewin32xpthemes.asp
本实例源代码http://geniusdot.googlepages.com/new.rar

VC++ WIN32 sdk实现按钮自绘详解.相关推荐

  1. php 工商银行公众号支付代码_微信支付PHP SDK —— 公众号支付代码详解

    在微信支付 开发者文档页面 下载最新的 php SDK 这里假设你已经申请完微信支付 1. 微信后台配置  如图 我们先进行测试,所以先把测试授权目录和 测试白名单添加上.测试授权目录是你要发起微信请 ...

  2. php 微信支付开发测试,微信支付PHP SDK —— 公众号支付代码详解

    在微信支付 开发者文档页面 下载最新的 php sdk 这里假设你已经申请完微信支付 1. 微信后台配置  如图 我们先进行测试,所以先把测试授权目录和 测试白名单添加上.测试授权目录是你要发起微信请 ...

  3. Android之高德地图SDK配置及简单使用详解

    需要用到的东西请去高德地图API官网下载 本次教程是对比着高德官网的demo一步步添加东西,所以需要有一份demo就够了. 1.打开高德地图的demo(AMap3DDemo),同时新建一个项目 2.将 ...

  4. android饿了么购物车,Android仿饿了么加入购物车旋转控件自带闪转腾挪动画的按钮效果(实例详解)...

    概述 在上文,酷炫Path动画已经预告了,今天给大家带来的是利用 纯自定义View,实现的仿饿了么加入购物车控件,自带闪转腾挪动画的按钮. 效果图如下: 图1 项目中使用的效果,考虑到了View的回收 ...

  5. js点击取消按钮关闭当前弹框_UI设计中“取消按钮”的分析详解

    按钮,无论是在 Web 还是 App 上都被广泛地使用,而很少有设计师会注意到按钮当中的细节,导致在设计过程中出现一些低级的错误,使得用户在完成任务的过程中产生阻碍,无法顺利达成目的. 在许多优秀的产 ...

  6. RT5350原厂SDK及AP移植步骤详解

    最近想搞一下rt5350,所以找了个原厂的SDK包进行了编译,很快路由器就可以用了,把我的编译操作步骤写了下分享给更多的爱好者,供大家参靠,下一步准备移植摄像头玩玩.有兴趣的可以一起交流. RT535 ...

  7. 微信小程序按钮Button使用详解

    最近在出微信小程序系列教程 顺手写了点博客 欢迎大家关注 button 用来实现按钮点击效果,本文章效果如下: 1 wxml 文件中 在这里我就放了三个普通的按钮 <view class=&qu ...

  8. python tkinter button_Python连载60-Tkinter布局、按钮以及属性详解

    一.Tkinter​ 1.组件的大致使用步骤 (1)创建总面板 (2)创建面板上的各种组件: i.指定组件的父组件,即依附关系:ii.利用相应的属性对组件进行设置:iii.给组件安排布局. (3)同步 ...

  9. win7 更新android sdk,大神为你详解win7系统android sdk manager无法更新的处理对策

    随着电脑的使用率越来越高,我们有时候可能会遇到win7系统android sdk manager无法更新问题,如果我们遇到了win7系统android sdk manager无法更新的问题,要怎么处理 ...

最新文章

  1. AMNO.6 给出一个不多于5位的整数,要求 1、求出它是几位数 2、分别输出每一位数字 3、按逆序输出各位数字,例如原数为321,应输出123 输入 一个不大于5位的数字
  2. Maven让资源文件处理插件能够解析资源文件中的Maven属性
  3. Spring第二讲--利用组件注解符精简Spring配置文件
  4. NYOJ 904 search 二分查找
  5. VTK:PolyData之ExtractCellsUsingPoints
  6. 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
  7. win7磁盘设置背景方法
  8. eclipse 安装和使用AmaterasUML
  9. pip 安装 opencv
  10. SpringSecurity应用(二)
  11. 节选转载:你敢向代码库中添加Boost你就等着被开除吧
  12. Maven的Snapshot版本与Release版本
  13. UVA 10247 Complete Tree Labeling
  14. 为了苦苦寻觅视频素材的你,我们整理了 9 个版权视频网站
  15. R7 7735HS参数 锐龙77735HS怎么样相当于什么水平级别
  16. Unable to preventDefault inside passive event listener due to target being treated as passive 怎么办?
  17. 360权重 360网站权重查询在哪里
  18. Metal 框架之从可绘制纹理中读取像素数据
  19. Vue中的@blur事件
  20. regsvr32 命令使用和regsvr32 dll失败原因介绍

热门文章

  1. 在阿里云服务器centOs7系统中部署.NET Core项目
  2. python3 判断大小端的一种方法
  3. Docker容器的管理
  4. [LeetCode]15. 3Sum
  5. 通过Small Basic把儿子/女儿带入编程的世界
  6. 【多线程学习第一弹】支持value重复的双向Map
  7. DataTemplate——数据模板的一个典型例子
  8. linux chmod命令参数及用法详解--文件文件夹权限设定命令
  9. aswing学习笔记4-通过调用面板中的按钮实现主界面动态切换皮肤的问题!
  10. 过去的2018年,400000粉丝用指尖投票,选出了这10本技术书