//头文件 /******************************************************************************************************* * * 图标按钮类(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