Windows程序设计学习笔记(四)自绘控件与贴图的实现
Windows系统提供大量的控件供我们使用,但是系统提供的控件样式都是统一的,不管什么东西看久了自然会厌烦,为了使界面更加美观,添加一些新的东西我们需要自己绘制控件。
控件在默认情况下并不进行自绘,如果是在窗口中利用CreateWindow创建的话要在风格中加入一个对应的自绘风格,这个一般在MSDN中都可以查到比如按钮的自绘风格是BS_OWNERDRAW、列表框是 LBS_OWNERDRAWFIXED (列表项的高度一致)、LBS_OWNERDRAWVARIABLE(列表项的高度可以不一致),如果我们是在对话框下通过资源的方式创建的可以在其属性上将自绘风格选上。控件被改为自绘时,每当需要自画时控件都会向其父窗口发送一条WM_DRAWITEM消息,该消息中两个参数的如下:
WM_DRAWITEM idCtl = (UINT) wParam; // 控件ID lpdis = (LPDRAWITEMSTRUCT) lParam; // 一个指向DRAWITEMSTRUCT的结构体结构体DRAWITEMSTRUCT的定义如下:
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; //控件类型 UINT CtlID; //控件ID UINT itemID; //控件子项的ID只用于菜单项、组合框、列表框 UINT itemAction; //控件行为,一般在一个动态的行为发生时产生 UINT itemState; //控件状态,在处于某个静态时产生 HWND hwndItem; //控件句柄 HDC hDC; //绘制控件的设备上下文句柄 RECT rcItem; //控件项的矩形范围 DWORD itemData; //程序为菜单项、列表项、组合框中的列表项指定的32值 } DRAWITEMSTRUCT;
对于列表框和组合框,在重绘时会发送一条消息:WM_MEASUREITEM,该消息用于设置列表项的大小信息。
可以在该消息中利用下面的代码设置行高:
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam; lpmis->itemHeight = 32;
下面来说说贴图。贴图的一般步骤为:
1)使用LoadBitmap加载一幅图片,该函数的原型为:
HBITMAP LoadBitmap(//函数返回一个对应位图的对象句柄 HINSTANCE hInstance, //实例句柄,系统通过这个值找到对应的位图 LPCTSTR lpBitmapName //位图名称,这个值可以通过MAKEINTRESOURCE宏获得 );2)用CreateCompatiableDC函数创建一个与指定DC兼容的内存设备描述符。
3)利用SelectObject函数将对应位图选入到对应的HDC中,该函数返回一个原来未被替代的对象句柄,一般我们需要保存这个变量以便以后用于恢复。
4)使用BitBlt贴图
函数BitBlt,该函数的原型如下:
BOOL BitBlt( HDC hdcDest, // 目的控件的设备上下文句柄 int nXDest, // int nYDest, // 这两个参数表示需要贴在目的设备对应矩形中的哪个位置,分别是客户坐标的横坐标和纵坐标 int nWidth, int nHeight, //图片的大小和宽度 HDC hdcSrc, // 源图片所在的DC的句柄 int nXSrc, int nYSrc, //表示从原图片的哪个像素点开始,这两个值表示开始位置的横纵坐标 DWORD dwRop // 贴图的方式,它规定了原图片颜色如何与目标控件颜色组合已形成最终的颜色 );
对于第二步的操作并不是必要的,在贴图时我们可以使用同一个句柄作为原和目的句柄,但是当我们需要贴的图片过多,使用同一个句柄会造成客户区的闪烁,所以可以另外定义一个句柄,保存我们所需要的所有图片,然后一次性通过源DC贴到目的DC,这样可以一次完成,避免了客户区的闪烁。
下面的例子采用的是ListBox控件:
HWND hList = CreateWindow("LISTBOX", "", WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS |WS_VISIBLE | LBS_HASSTRINGS | LBS_NOTIFY | LBS_OWNERDRAWFIXED , 0,0,200,800,hWnd, (HMENU)123, g_hInst, NULL);//在创建ListBox时定义为自画风格,同时WS_CLIPSIBLINGS风格指明在重绘子窗口时不重绘整个客户区
LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam; RECT rtListItem = lpDis->rcItem; if (ODT_LISTBOX == lpDis->CtlType) { if (ODS_SELECTED & lpDis->itemState)//当某项被选中时设置虚线框并使背景为蓝色 { rtListItem.right -= 2; rtListItem.bottom -= 2; HBRUSH hBlue = CreateSolidBrush(RGB(0,0,255)); HGDIOBJ hOld = SelectObject(lpDis->hDC, hBlue); FillRect(lpDis->hDC, &rtListItem, hBlue); DrawFocusRect(lpDis->hDC, &rtListItem); }
//贴图,并将图片背景色设置为所在矩形框的背景色
<span style="white-space:pre"> </span>HBITMAP hBitMap = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1)); HDC hMerDc= CreateCompatibleDC(lpDis->hDC); SelectObject(hMerDc, hBitMap); BitBlt(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.top, lpDis->rcItem.right - lpDis->rcItem.left, lpDis->rcItem.bottom - lpDis->rcItem.top, hMerDc, 0, 0, SRCAND); SelectObject(lpDis->hDC,hBitMap); DeleteObject(hMerDc);
<span style="white-space:pre"> </span>//将文字设置为透明、并显示文字 SetBkMode(lpDis->hDC, TRANSPARENT); DrawText(lpDis->hDC, g_Person[lpDis->itemID].pszName,strlen(g_Person[lpDis->itemID].pszName), &(lpDis->rcItem),DT_CENTER | DT_VCENTER | DT_SINGLELINE); }