//头文件
/*******************************************************************************************************
*
* 图标按钮类(SDK)
*
* BY commander
*
* 基于网上代码 geniusdot 的 "VC++ WIN32 sdk实现按钮自绘详解" 的修改
*
* 编写工具:VS2005
*
* 日期:2011/10/02
*
* 说明:用到WINDOWS SDK写程序,苦于网上很少关于图标按钮的文章,
* 为了方便,写了个很不给力的类,给和我一样的菜鸟中的菜鸟
* 一些参考,少走一点弯路吧。
*
* 调用方法:1.在工程中添加一个按钮设置ID为IDB_BUTTON1
* 2.必须设置IDB_BUTTON1按钮的STYLE为owenerdraw属性
* 3.添加3个图标,分别用于正常状态显示,在按钮上空时显示,按下时显示
* 3.在IDB_BUTTON1按钮的直接父窗口的窗口处理函数中(假设是WinMainProc)添加如下
* LONG_PTR __stdcall WinMainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
* {//消息处理
* static IconBtn objIconBtn(hWnd);
* switch(uMsg)
* {
* case WM_INITDIALOG:
* objIconBtn.AddBtn(IDI_ICON1,IDI_ICON2, IDI_ICON3, IDC_BUTTON1);
* objIconBtn.AddBtn(IDI_ICON1,IDI_ICON2, IDI_ICON3, IDC_BUTTON2);
* objIconBtn.AddBtn(IDI_ICON1,IDI_ICON2, IDI_ICON3, IDC_BUTTON3);
* break;
* case WM_DRAWITEM:
* objIconBtn.DrawMsg(hWnd, lParam);
* return true;
* }
* return false;
* }
*******************************************************************************************************/
#ifndef __ICONBTNSDK_H_
#define __ICONBTNSDK_H_
#include <vector>
#include <windows.h>
/*
出现编译错误注意:
在VC2005中调试一个可执行文件时代码生成通过,但是连接时编译器报错:
Generating Code...
正在链接...
LINK : warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
libcpmtd.lib(cerr.obj) : error LNK2001: 无法解析的外部符号 __CrtDbgReportW
libcpmtd.lib(stdthrow.obj) : error LNK2001: 无法解析的外部符号 __CrtDbgReportW
libcpmtd.lib(xmbtowc.obj) : error LNK2001: 无法解析的外部符号 __CrtDbgReportW
libcpmtd.lib(cout.obj) : error LNK2001: 无法解析的外部符号 __CrtDbgReportW
以下是解决方法:
运行时库现在包含可防止混合不同类型的指令。如果试图在同一个程序中使用不同类型的
运行时库或使用调试和非调试版本的运行时库,则将收到此警告。例如,如果编译一个文件
以使用一种运行时库,而编译另一个文件以使用另一种运行时库(例如单线程运行时库对
多线程运行时库),并试图链接它们,则将得到此警告。应将所有源文件编译为使用同一
个运行时库。有关更多信息,请参见使用运行时库(/MD、/MT 和 /LD)编译器选项。
于是打开项目属性,在“配置属性-->C/C++-->代码生成-->运行时库”中将“多线程(/MT)”
修改为“多线程调试(/MTd)”,再重新调试一下,问题解决。
*/
class IconBtn
{
//所有的按钮自身都调用系统同一个处理函数
public:
IconBtn(HWND hParentWnd);
//参数说明:正常图标ID, 按钮上空图标ID, 按钮选中图标ID,按钮ID
void AddBtn(UINT normalIconID, UINT hoverIconID, UINT downIconId, UINT btnID);
//处理按钮WM_DRAWITEM消息,在父窗口处理函数中的WM_DRAWITEM中调用
BOOL DrawMsg(HWND hDlg, LPARAM lParam);
private:
//void RedtBtnProc(HWND hParWnd);
void PrepareImageRect(HWND hBrnWnd, BOOL bHasTitle, RECT* rpItem, RECT* rpTitle,
BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, RECT* rpImage);
//画ICON
void DrawTheIcon(HWND hBtnWnd, HDC* dc, BOOL bIsDisabled, HICON hIcon);
//判断当前触发重画命令的控件是否是一个按钮, true:是按钮,并返回各HICO
bool CurCtrlIsBtn(UINT CtlID, HICON &normalIcon, HICON &hoverIcon, HICON &downIcon);
private:
HWND m_hParWnd;
struct BtnStruct
{
struct BtnStruct(HICON normalIcon, HICON hoverIcon, HICON downIcon, UINT btnID) :
m_normalIcon(normalIcon), m_hoverIcon(hoverIcon),
m_downIcon(downIcon), m_btnID(btnID) {}
struct BtnStruct &operator= (const struct BtnStruct &b)
{
this->m_btnID = b.m_btnID;
this->m_downIcon = b.m_downIcon;
this->m_hoverIcon = b.m_hoverIcon;
this->m_normalIcon = b.m_normalIcon;
return *this;
}
HICON m_normalIcon, m_hoverIcon, m_downIcon;
UINT m_btnID;
};
std::vector<BtnStruct > m_btnDataVec;
};
#endif
//源文件.CPP
#ifndef __ICONBTNSDK_CPP_
#define __ICONBTNSDK_CPP_
#include "IconBtnSDK.h"
#include <commctrl.h>
#pragma comment(lib, "Comctl32.lib")
#define ICON_SIZE 54
static LONG_PTR btnDefProc = -1;
//所有的按钮自身都调用系统同一个处理函数
LONG_PTR BtnProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{//
/*
TME_NONCLIENT是非客户区消息,你在WM_MOUSEMOVE里TrackMouseEvent是没有用的,
因为这样只有鼠标在客户区才会关注WM_NCMOUSEMOVE,而鼠标移到标题栏因为没有触发
WM_MOUSEMOVE,就没有TrackMouseEvent,也就不会有WM_NCMOUSEMOVE消息
如下才能接收WM_MOUSEHOVER
*/
static TRACKMOUSEEVENT tacMouseEv;
//_TrackMouseEvent((LPTRACKMOUSEEVENT)&tacMouseEv);
switch (uMsg)
{
case WM_MOUSELEAVE:
{
UINT CtlID = GetWindowLong(hWnd, GWL_ID);
static DRAWITEMSTRUCT param;
param.CtlID = CtlID;
param.CtlType = ODT_BUTTON;
param.hDC = GetWindowDC(hWnd);
param.hwndItem= hWnd;
param.itemAction = ODA_DRAWENTIRE | ODA_FOCUS | ODA_SELECT;
param.itemData = 0;
param.itemID = 0;
if (GetFocus() == hWnd)
param.itemState = ODS_FOCUS;
else param.itemState = 0;
GetClientRect(hWnd, ¶m.rcItem);
SendMessage(GetParent(hWnd), WM_DRAWITEM, (WPARAM)CtlID, (LPARAM)(LPDRAWITEMSTRUCT)¶m);
}
break;
case WM_MOUSEHOVER:
{
UINT CtlID = GetWindowLong(hWnd, GWL_ID);
static DRAWITEMSTRUCT param;
param.CtlID = CtlID;
param.CtlType = ODT_BUTTON;
param.hDC = GetWindowDC(hWnd);
param.hwndItem= hWnd;
param.itemAction = ODA_DRAWENTIRE | ODA_FOCUS | ODA_SELECT;
param.itemData = 0;
param.itemID = 0;
param.itemState = ODS_HOTLIGHT;
if (GetFocus() == hWnd)
param.itemState |= ODS_FOCUS;
GetClientRect(hWnd, ¶m.rcItem);
SendMessage(GetParent(hWnd), WM_DRAWITEM, (WPARAM)CtlID, (LPARAM)(LPDRAWITEMSTRUCT)¶m);
}
break;
case WM_MOUSEMOVE:
{
tacMouseEv.cbSize = sizeof(TRACKMOUSEEVENT);
tacMouseEv.dwFlags = TME_HOVER | TME_LEAVE;
tacMouseEv.dwHoverTime = 10;
tacMouseEv.hwndTrack = hWnd;
_TrackMouseEvent((LPTRACKMOUSEEVENT)&tacMouseEv);
}
break;
case WM_LBUTTONDBLCLK:
PostMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam);
break;
}
//将不做处理的消息路由给原默认函数
return CallWindowProc((WNDPROC)btnDefProc, hWnd, uMsg, wParam, lParam);
}
void IconBtn::PrepareImageRect(HWND hBtnWnd, BOOL bHasTitle, RECT* rpItem, RECT* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, RECT* rpImage)
{
RECT rBtn;
CopyRect(rpImage, rpItem);
GetClientRect(hBtnWnd, &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;
//假设图标为32*32像素
unsigned short iconWidth = ICON_SIZE;
unsigned short iconHei = ICON_SIZE;
if (rBtn.right > iconWidth) rpImage->left = ((rBtn.right - iconWidth) >> 1);
else rpImage->left = 0;
if (rBtn.bottom > iconHei) rpImage->top = ((rBtn.bottom - iconHei) >> 1);
else rpImage->top = 0;
LONG rpImageHeight = rpImage->bottom - rpImage->top;
//rpImage->top += ((rpImageHeight - (long)dwHeight)/2);
}
if (bIsPressed)//按钮被按下的处理
OffsetRect(rpImage, 1, 1);
}
void IconBtn::DrawTheIcon(HWND hBtnWnd, HDC* dc, BOOL bIsDisabled, HICON hIcon)
{
RECT rImage;
//PrepareImageRect(hBtnWnd, bHasTitle, rpItem, rpTitle, bIsPressed, 38, 38, &rImage);
// 调用API函数按准备好的形式将图片画到按钮上
GetClientRect(hBtnWnd, &rImage);
DrawState( *dc,
NULL,
NULL,
(LPARAM)hIcon,
0,
rImage.left,
rImage.top,
(rImage.right - rImage.left),
(rImage.bottom - rImage.top),
(bIsDisabled ? DSS_DISABLED : DSS_NORMAL) | DST_ICON);
}
bool IconBtn::CurCtrlIsBtn(UINT CtlID, HICON &normalIcon, HICON &hoverIcon, HICON &downIcon)
{//判断当前触发重画命令的控件是否是一个按钮, true:是按钮
for (std::vector<BtnStruct >::iterator iter = m_btnDataVec.begin(); iter != m_btnDataVec.end(); ++iter)
if ((*iter).m_btnID == CtlID)
{
normalIcon = (*iter).m_normalIcon;
hoverIcon = (*iter).m_hoverIcon;
downIcon = (*iter).m_downIcon;
return true;
}
return false;
}
BOOL IconBtn::DrawMsg(HWND hDlg, LPARAM lParam)
{
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) lParam;
//声明一个指向DRAWITEMSTRUCT结构体的指针并将其指向存储着按钮构造信息的lParam
HICON normalIcon, hoverIcon, downIcon;
if(!CurCtrlIsBtn(lpDIS->CtlID, normalIcon, hoverIcon, downIcon))
return FALSE;
HDC dc = lpDIS->hDC; //用于按钮绘制的DC
BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED);
BOOL bIsFocused = (lpDIS->itemState & ODS_FOCUS);
BOOL bIsDisabled = (lpDIS->itemState & ODS_DISABLED);
BOOL bIsHover = (lpDIS->itemState & ODS_HOTLIGHT);
const int ODS_NOFOCUSRECT = 0x020;
BOOL bDrawFocusRect = !(lpDIS->itemState & ODS_NOFOCUSRECT);
//判断按钮各种状态的BOOL值
RECT itemRect = lpDIS->rcItem; //按钮的矩形区域
//SetBkMode(dc, TRANSPARENT); //设置绘制按钮时的背景状态
//if (bIsFocused) //判断按钮是否获得了焦点并对其边框进行处理
//{
// HBRUSH br = CreateSolidBrush(RGB(0,0,0));
// //draw a border
// FrameRect(dc, &itemRect, br);
// //设置边框的厚度,一定要是负数
// InflateRect(&itemRect, -1, -1);
// DeleteObject(br);
//} // if
COLORREF crColor = GetSysColor(COLOR_WINDOW);//得到系统窗口颜色
HBRUSH brBackground = CreateSolidBrush(crColor);//创建画刷
FillRect(dc, &itemRect, brBackground);//绘制按钮
DeleteObject(brBackground);
//TCHAR sTitle[100];
//GetWindowText(GetDlgItem(hDlg, lpDIS->CtlID), sTitle, 100);//得到按钮的文本
//RECT captionRect = lpDIS->rcItem;//把文本的区域设置为按钮区域
//BOOL bHasTitle = (sTitle[0] !='/0');//按钮上是否有文本存在
///
if (bIsHover)
{// 鼠标在按钮上空
//UINT uState = DFCS_BUTTONPUSH |
// ((bIsPressed) ? DFCS_PUSHED : 0);
//draws a frame control of the specified type and style.
//DrawFrameControl(dc, &itemRect, DFC_BUTTON, uState);
DrawTheIcon(GetDlgItem(hDlg, lpDIS->CtlID), &dc, bIsDisabled, hoverIcon);
}
else if (bIsPressed)
{// 这里画被按下去的按钮
//HBRUSH brBtnShadow = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
//FrameRect(dc, &itemRect, brBtnShadow);
//DeleteObject(brBtnShadow);
DrawTheIcon(GetDlgItem(hDlg, lpDIS->CtlID), &dc, bIsDisabled, downIcon);
}
else//如果没有被按下就这样画
{
//UINT uState = DFCS_BUTTONPUSH |
// ((bIsPressed) ? DFCS_PUSHED : 0);
//draws a frame control of the specified type and style.
//DrawFrameControl(dc, &itemRect, DFC_BUTTON, uState);
DrawTheIcon(GetDlgItem(hDlg, lpDIS->CtlID), &dc, bIsDisabled, normalIcon);
}
//if (bHasTitle)//如果按钮有文本标题
//{
// // 按钮被按下的处理
// if (bIsPressed)
// OffsetRect(&captionRect, 1, 1);
// // 将文本居中
// HFONT hFont = (HFONT)SendMessage(GetDlgItem(hDlg, lpDIS->CtlID), WM_GETFONT, 0, 0);
// if (hFont == NULL)
// {//当前使用系统默认字体
// hFont = (HFONT)GetStockObject(SYSTEM_FONT);
// }
// LOGFONT lf;
// GetObject(hFont, sizeof(LOGFONT), &lf);
// size_t textLen = wcslen(sTitle);
// textLen *= lf.lfWidth;
// 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);
}
IconBtn::IconBtn(HWND hParentWnd)
{
m_hParWnd = hParentWnd;
m_btnDataVec.clear();
}
void IconBtn::AddBtn(UINT normalIconID, UINT hoverIconID, UINT downIconId, UINT btnID)
{
RECT btnClient;
GetClientRect(GetDlgItem(m_hParWnd, btnID), &btnClient);
HICON normalIcon = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(normalIconID),
IMAGE_ICON,
btnClient.right,
btnClient.bottom,
0);
HICON hoverIcon = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(hoverIconID),
IMAGE_ICON,
btnClient.right,
btnClient.bottom,
0);
HICON downIcon = (HICON)LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(downIconId),
IMAGE_ICON,
btnClient.right,
btnClient.bottom,
0);
m_btnDataVec.push_back(BtnStruct(normalIcon, hoverIcon, downIcon, btnID));
btnDefProc = (LONG_PTR)SetWindowLongPtr(GetDlgItem(m_hParWnd, btnID), GWLP_WNDPROC, (LONG)(LONG_PTR)BtnProc);
}
#endif