今天教大家如何重绘ListBox,其中涉及到的相关知识点也一一解释了
效果图:
是不是和QQ的列表框非常相似?那么这里就一步一步教大家如何使用Win32SDK来完成这项工作!
创建Win32窗口
#include "stdafx.h"
#include <windows.h>
HWND hWnd; //progman
//消息函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//判断消息ID
switch (uMsg){
case WM_DESTROY: // 窗口销毁消息
PostQuitMessage(0); // 发送退出消息
return 0;
}
// 其他的消息调用缺省的消息处理程序
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// 3、注册窗口类型
BOOL RegisterWindow(LPCSTR lpcWndName, HINSTANCE hInstance)
{
ATOM nAtom = 0;
// 构造创建窗口参数
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc; // 指向窗口过程函数
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = NULL;
wndClass.hCursor = NULL;
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = lpcWndName; // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
nAtom = RegisterClass(&wndClass);
return TRUE;
}
//创建窗口(lpClassName 一定是已经注册过的窗口类型)
HWND CreateMyWindow(LPCTSTR lpClassName, HINSTANCE hInstance)
{
HWND hWnd = NULL;
// 创建窗口
hWnd = CreateWindow(lpClassName, "test", WS_OVERLAPPEDWINDOW^WS_THICKFRAME, 0, 0, 1000, 800, NULL, NULL, hInstance, NULL);
return hWnd;
}
//显示窗口
void DisplayMyWnd(HWND hWnd)
{
//获得屏幕尺寸
int scrWidth = GetSystemMetrics(SM_CXSCREEN);
int scrHeight = GetSystemMetrics(SM_CYSCREEN);
RECT rect;
GetWindowRect(hWnd, &rect);
ShowWindow(hWnd, SW_SHOW);
//重新设置rect里的值
rect.left = (scrWidth - rect.right) / 2;
rect.top = (scrHeight - rect.bottom) / 2;
//移动窗口到指定的位置
SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
UpdateWindow(hWnd);
}
void doMessage() // 消息循环处理函数
{
MSG msg = { 0 };
// 获取消息
while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
{
DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
}
}
// 入口函数
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
HWND hWnd = NULL;
LPCTSTR lpClassName = "MyWnd"; // 注册窗口的名称
RegisterWindow(lpClassName, hInstance);
hWnd = CreateMyWindow(lpClassName, hInstance);
DisplayMyWnd(hWnd);
doMessage();
return 0;
}
这里窗口创建代码基本上就已经编写完成了!
第二步,我们要在WinMain函数里创建ListBox控件,在创建之前需要讲解一下WM_DRAWITEM消息,这个消息是在当具有“自绘属性”的控件需要重绘时绘被触发到消息队列里,开发人员可以在此消息下对控件进行重绘,也可以交给操作系统来完成绘制。
上面说了什么是带有自绘属性的控件?
在MFC下当你添加一个控件时,在它的stsy风格里有一个drawItem属性,选择成true,即此控件带有自绘属性,那么当控件被重绘时,MFC框架绘触发WM_DRAWITEM消息并调用DrawItem函数,开发人员需要重载控件的DrawItem函数,即可完成重绘,但是在Win32 SDK下不是这样的,因为MFC是封装Win32 SDK的,它有一套自己的绘图管理机制,所以这里我们需要自己来完成这套绘图机制!
什么是自绘属性?
在不同的控件下自绘属性是不同的比如ComboBox的是CBS_OWNERDRAWVARIABLE而ListBox的是LBS_OWNERDRAWVARIABLE但是这两个又可以相互兼容,但是在Button和Edit上却不行,后面会逐个介绍每个控件的自绘属性!
那么这里我们可以使用CreateWindowsEx选择对应的类信息,来创建控件:
首先先定义一个全局变量和宏
#define ID_LIST_1 0x00000001 //list id
HWND hList; //listbox 句柄
在创建
hList = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_OWNERDRAWVARIABLE, 10, 100, 80, 90, hWnd1, (HMENU)ID_LIST_1, NULL, NULL);
创建完成之后运行一下看下效果如何:
控件成功显示出来了,那么我们如何对其添加几个列表选项呢?
这里我们就要通过消息来完成了:
使用:LB_ADDSTRING消息,此消息为ListBox添加子选项消息,并且添加的信息存放在附加参数二里:
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)TEXT("list str1"));
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)TEXT("list str2"));
ListBox控件接受到此消息会自动执行添加选项代码,当然我们也可以拦截此消息不让它添加!
运行效果:
运行时你会发现:
为什么没有添加进来?
答:
此控件具有重绘属性,当触发LB_ADDSTRING等消息时都会触发WM_DRAWITEM消息,因为添加完选项或重新绘制时都会被触发WM_DRAWITEM消息然后跳转到WM_DRAWITEM消息执行代码里去了,其实选项已经添加了,只是这个控件具有重绘消息,所以Windows不执行绘制控件代码,仅执行了WM_PAINT消息绘制出了外观,但ListBox还需要绘制子选项内容,在具有重绘属性的控件上,Windows会直接触发WM_DRAWITEM消息由用户来完成绘制!
所以这个消息也就间接的失效了。
不信当我们点击上去看看:
可以看到子选项具有焦点,但是文字信息却没有被绘制出来,因为绘制ListBox子选项视图的消息已经被我们所接管了!
那么这里在来介绍一下:WM_DRAWITEM消息,当控件具有自绘属性时,比如ListBox,那么当控件部分视图将要被重绘时将发送WM_DRAWITEM消息给控件的父窗口,注意不是给控件的消息队列,而是控件的父窗口,也就是窗口消息队列,且在附加消息二里包含了控件的相关信息,我们可以根据此相关信息来进行重绘
所以我们要在父窗口的消息处理函数里对WM_DRAWITEM消息进行处理:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DRAWITEM)
{
}
//判断消息ID
switch (uMsg){
case WM_DESTROY: // 窗口销毁消息
PostQuitMessage(0); // 发送退出消息
return 0;
}
// 其他的消息调用缺省的消息处理程序
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
上面说过当触发WM_DRAWITEM消息时,第二个附加参数lParam里包含了控件的相关信息,所以我们要首先获取控件的相关信息,那么在此之前给大家介绍一个结构体:
tagDRAWITEMSTRUCT
其原型如下:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
ULONG_PTR itemData;
} DRAWITEMSTRUCT, NEAR *PDRAWITEMSTRUCT, FAR *LPDRAWITEMSTRUCT
成员介绍:
结构成员:
上述的结构中的每个成员的具体含义,用途如下:
CtlType :
指定了控件的类型,其取值如下表所示。
常数名称 | 取值 | 含义 |
ODT_BUTTON | 4 | 按钮控件 |
ODT_COMBOBOX | 3 | 组合框控件 |
ODT_LISTBOX | 2 | 列表框控件 |
ODT_LISTVIEW | 102 | 列表视图控件 |
ODT_MENU | 1 | 菜单项 |
ODT_STATIC | 5 | 静态文本控件 |
ODT_TAB | 101 | Tab控件 |
经实测这些常量值是十进制的。
CtlID:
指定了自绘控件的ID值,而对于菜单项则不需要使用该成员
itemID:
表示菜单项ID,也可以表示列表框或者组合框中某项的索引值。对于一个空的列表框或组合框,该成员的值为–1。这时应用程序只绘制焦点矩形(该矩形的坐标由rcItem 成员给出)虽然此时控件中没有需要显示的项,但是绘制焦点矩形还是很有必要的,因为这样做能够提示用户该控件是否具有输入焦点。当然也可以设置itemAction 成员为合适值,使得无需绘制焦点。
itemAction:
指定绘制行为,其取值可以为下表中所示值的一个或者多个的联合。
ODA_DRAWENTIRE=H1:当整个控件都需要被绘制时,设置该值
ODA_FOCUS=H4:如果控件需要在获得或失去焦点时被绘制,则设置该值。此时应该检查itemState成员,以确定控件是否具有输入焦点。
ODA_SELECT=H2
如果控件需要在选中状态改变时被绘制,则设置该值。此时应该检查itemState 成员,以确定控件是否处于选中状态。
itemState:
指定了当前绘制操作完成后,所绘项的可见状态。例如,如果菜单项应该被灰色显示,则可以指定ODS_GRAYED状态标志。其取值可以为下表中所示值的一个或者多个的联合。
ODS_CHECKED=H8:如果菜单项将被选中,则可设置该值。该值只对菜单项有用。
ODS_COMBOBOXEDIT=H1000:在自绘组合框控件中只绘制选择区域。
ODS_DEFAULT=H20:默认值。
ODS_DISABLED=H4:如果控件将被禁止,则设置该值。
ODS_FOCUS=H10:如果控件需要输入焦点,则设置该值。
ODS_GRAYED=H2:如果控件需要被灰色显示,则设置该值。该值只在绘制菜单时使用。
ODS_HOTLIGHT=H40:Windows 98/Me, Windows 2000/XP: 如果鼠标指针位于控件之上,则设置该值,这时控件会显示高亮颜色。
ODS_INACTIVE=H80:Windows 98/Me, Windows 2000/XP: 表示没有激活的菜单项。
ODS_NOACCEL=H100:Windows 2000/XP: 控件是否有快速键盘。
ODS_NOFOCUSRECT=H200:Windows 2000/XP: 不绘制捕获焦点的效果。
ODS_SELECTED=H1:选中的菜单项。
hwndItem:
指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象时菜单项,则表示包含该菜单项的菜单句柄。
hDC:
指定了绘制操作所使用的设备环境。
rcItem:
指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。
itemData:
对于菜单项,该成员的取值可以是由
AppendMenu、InsertMenu或者ModifyMenu等函数传递给菜单的值。
对于列表框或组合框,该成员的值可以为由
、InsertString、AddString或者CListBox::InsertString等传递给控件的值。
如果ctlType 的取值是ODT_BUTTON或者ODT_STATIC, itemData的取值为0
注意这里说一下,lParan原型是一个long *类型的指针,指向一块内存,而这个结构体恰好和这个内存字节序对齐,这里我们只需要利用结构体的内存对齐机制来指向此内存块就可以了,而此内存块里的数据是和此结构体是对齐的,这里我们使用结构体定义的far指针LPDRAWITEMSTRUCT来显示指向内存:
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;(消除歧义)
得到了相关信息,那么接下来我们开始绘制图标:
注意这里要先绘制子表项的背景,以便用于区分选中状态:
HBRUSH colo; //colo
colo = CreateSolidBrush(RGB(123, 255, 255)); //创建一个颜色刷子
FillRect(lpDIS->hDC, &lpDIS->rcItem, colo); //绘制矩形
DeleteObject(colo); //删除颜色刷子
然后我们在使用LoadImage函数来加载本地资源图标,然后在使用DrawIconEx函数将其绘制出来:
HICON hicon = (HICON)LoadImage(
NULL, //本地资源无需给出进程模块句柄,如果是资源文件需要进程模块句柄,可以使用GetModuleHandle(NULL)获取,null代表获取当前进程下的模块实例句柄
TEXT("C:\\Users\\ZZH\\Desktop\\1.ico"),
IMAGE_ICON,
32, 32,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE); //加载ico
DrawIconEx(lpDIS->hDC, lpDIS->rcItem.left + 5, lpDIS->rcItem.top + 5, hicon, 32, 32, 0, 0, 0 | 3); //绘制32*32ico
DeleteObject(hicon); //删除ico句柄
这里需要声明一个问题,当我们绘制listbox的时候产生的消息比如:
listbox有三个子选项,那么当此控件需要重绘并触发WM_DRAWITEM消息的时候,传递进来的相关信息包含了子选项的hdc,实则上触发了三次,第一次是子选项1,第二次是子选项2的hdc…
那么我们开始绘制网名和签名:
HFONT t_hFont1 = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体")); //创建字体
HGDIOBJ select = SelectObject(lpDIS->hDC, &t_hFont1); //设置字体
//注意这里使用:
//HFONT t_hFont1 = CreateFont(32, 0, 0, 0, 400, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体"));
//SendMessage(hList, WM_SETFONT, WPARAM(t_hFont1), true);
//消息方式设置字体是失效的,即便你设置上了,但是具有重绘消息后,Windows只会添加子选项而不会根据字体信息来绘制数据,上面说过了,这里就不做多解释了!
那么继续:
SetBkMode(lpDIS->hDC, TRANSPARENT);//设置绘图文字画刷背景色为透明
SetTextColor(lpDIS->hDC, RGB(0, 0, 0)); //设置绘图文字颜色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 5, "周志豪", strlen("周志豪")); //绘网名
SetTextColor(lpDIS->hDC, RGB(125, 125, 125)); //设置绘图文件颜色-灰色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 20, "有些事值得去奋斗", strlen(“有些事值得去奋斗”));//画个性签名
SelectObject(lpDIS->hDC, select); //把旧的字体设置回去
DeleteObject(t_hFont1); //删除字体句柄
return 0; //直接返回防止系统重绘
这里的绘图文字大多数是和hdc环境有关,所以我们将字体选入hdc,那么调用绘图函数,会兼容hdc环境~
运行效果:
会发现出现子选项相互遮挡的问题了,其实原因很简单,是因为子选项高度不对,我们绘制的图标,和文字超出子选项客户区范围(hdc)导致的,那么如何解决呢?
这里还有一个消息:
WM_MEASUREITEM
此消息是在当组合框或菜单或列表框的子选项被创建时会触发,注意Windows绘制组合框菜单或者列表时,是先创建子选项,然后在对其子选项重绘,此消息可以供我们修改创建子选项时的子选项大小,注意也只有当控件具有重绘消息时才会被触发:
那么当此消息被触发时,第二个参数里指向了listbox控件用于描述子表现相关信息的结构体首地址,注意这里要介绍一个新的结构体:
LPMEASUREITEMSTRUCT
ypedef struct tagMEASUREITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemWidth;
UINT itemHeight;
ULONG_PTR itemData;
} MEASUREITEMSTRUCT, NEAR *PMEASUREITEMSTRUCT, FAR *LPMEASUREITEMSTRUCT;
其余的就不介绍了,可以参考上面的对tagDRAWITEMSTRUCT结构体的介绍,这里来看一下:
itemWidth,itemHeight,分别对应子选项宽与高,所以这里我们只需要修改高度即可
if (uMsg == WM_MEASUREITEM){ //size
if ((UINT)wParam == ID_LIST_1)
{
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam; //ptr
lpmis->itemHeight = 40; //height
return 0; //必须返回,防止系统重新设置此值
}
}
这里设置完之后立即返回,当我们修改控件内存中用于描述子选项信息的值以后,那么操作系统下次重绘此控件时会根据此参数值来重绘!
注意控件也相当于一个内存,有hdc,有内存存放控件信息,操作系统根据这项信息来绘制控件的hdc!
运行效果:
那么这样就差不多了,但是你会发现,鼠标点击上去没有任何反应,是因为这些都要我们自己来重绘了,其实很简单,上面说过了:
itemState成员用于描述控件的状态,我们只需要在选中状态里修改一下背景画刷就可以啦:
if (lpDIS->itemState & ODS_SELECTED){ //是否选中
HBRUSH colo; //colo
colo = CreateSolidBrush(RGB(123, 255, 255)); //创建一个颜色刷子,选中
FillRect(lpDIS->hDC, &lpDIS->rcItem, colo); //绘制矩形,填充背景
DeleteObject(colo); //删除颜色刷子
HICON hicon = (HICON)LoadImage(
NULL, //本地资源无需给出进程模块句柄,如果是资源文件需要进程模块句柄,可以使用GetModuleHandle(NULL)获取,null代表获取当前进程下的模块实例句柄
TEXT("C:\\Users\\ZZH\\Desktop\\1.ico"),
IMAGE_ICON,
32, 32,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE); //加载ico
DrawIconEx(lpDIS->hDC, lpDIS->rcItem.left + 5, lpDIS->rcItem.top + 5, hicon, 32, 32, 0, 0, 0 | 3); //绘制32*32ico
DeleteObject(hicon); //删除ico句柄
HFONT t_hFont1 = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体")); //创建字体
HGDIOBJ select = SelectObject(lpDIS->hDC, &t_hFont1); //设置字体
SetBkMode(lpDIS->hDC, TRANSPARENT);//设置绘图文字画刷背景色为透明
SetTextColor(lpDIS->hDC, RGB(0, 0, 0)); //设置绘图文字颜色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 5, "周志豪", strlen("周志豪")); //绘网名
SetTextColor(lpDIS->hDC, RGB(125, 125, 125)); //设置绘图文件颜色-灰色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 20, "有些事值得去奋斗", strlen("有些事值得去奋斗"));//画个性签名
SelectObject(lpDIS->hDC, select); //把旧的字体设置回去
DeleteObject(t_hFont1); //删除字体句柄
return 0; //直接返回防止系统重绘
}
else{
HBRUSH colo; //colo
colo = CreateSolidBrush(RGB(255, 255, 255)); //创建一个颜色刷子,非选中
FillRect(lpDIS->hDC, &lpDIS->rcItem, colo); //绘制矩形,填充背景
DeleteObject(colo); //删除颜色刷子
HICON hicon = (HICON)LoadImage(
NULL, //本地资源无需给出进程模块句柄,如果是资源文件需要进程模块句柄,可以使用GetModuleHandle(NULL)获取,null代表获取当前进程下的模块实例句柄
TEXT("C:\\Users\\ZZH\\Desktop\\1.ico"),
IMAGE_ICON,
32, 32,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE); //加载ico
DrawIconEx(lpDIS->hDC, lpDIS->rcItem.left + 5, lpDIS->rcItem.top + 5, hicon, 32, 32, 0, 0, 0 | 3); //绘制32*32ico
DeleteObject(hicon); //删除ico句柄
HFONT t_hFont1 = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体")); //创建字体
HGDIOBJ select = SelectObject(lpDIS->hDC, &t_hFont1); //设置字体
SetBkMode(lpDIS->hDC, TRANSPARENT);//设置绘图文字画刷背景色为透明
SetTextColor(lpDIS->hDC, RGB(0, 0, 0)); //设置绘图文字颜色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 5, "周志豪", strlen("周志豪")); //绘网名
SetTextColor(lpDIS->hDC, RGB(125, 125, 125)); //设置绘图文件颜色-灰色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 20, "有些事值得去奋斗", strlen("有些事值得去奋斗"));//画个性签名
SelectObject(lpDIS->hDC, select); //把旧的字体设置回去
DeleteObject(t_hFont1); //删除字体句柄
return 0; //直接返回防止系统重绘
}
这里解释一下:if (lpDIS->itemState & ODS_SELECTED)这段代码。
itemState可以对应不同的状态,比如itemState是32位的,高16位对应点击,低16位对应选中,而ODS_SELECTED是一个16位的值,所以我们使用&运算符进行位比较,&运算符会从低到高的顺序进行比较,将itemState的低16位与ODS_SELECTED的16位bit位对比,如果bit位相同返回0也就是真否则返回其它值,如果ODS_SELECTED是18位的那么与运算符就会与itemState的低十八位进行与运算并返回结果,所以这段代码其实就是检查是否itemState状态位是否包含对应的状态在里面!
运行效果:
完整代码:
#include "stdafx.h"
#include <windows.h>
//#include "Win32Project3.rc"
#define ID_LIST_1 0X00000001
HWND hWnd1; //progman
HWND hList; //LIST
//消息函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_MEASUREITEM){ //size
if ((UINT)wParam == ID_LIST_1)
{
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam; //ptr
lpmis->itemHeight = 40; //height
return 0; //必须返回,防止系统重新设置此值
}
}
if (uMsg == WM_DRAWITEM)
{
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
if (lpDIS->CtlType == ODT_LISTBOX){
if (lpDIS->itemState & ODS_SELECTED){ //是否选中
HBRUSH colo; //colo
colo = CreateSolidBrush(RGB(123, 255, 255)); //创建一个颜色刷子,选中
FillRect(lpDIS->hDC, &lpDIS->rcItem, colo); //绘制矩形,填充背景
DeleteObject(colo); //删除颜色刷子
HICON hicon = (HICON)LoadImage(
NULL, //本地资源无需给出进程模块句柄,如果是资源文件需要进程模块句柄,可以使用GetModuleHandle(NULL)获取,null代表获取当前进程下的模块实例句柄
TEXT("C:\\Users\\ZZH\\Desktop\\1.ico"),
IMAGE_ICON,
32, 32,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE); //加载ico
DrawIconEx(lpDIS->hDC, lpDIS->rcItem.left + 5, lpDIS->rcItem.top + 5, hicon, 32, 32, 0, 0, 0 | 3); //绘制32*32ico
DeleteObject(hicon); //删除ico句柄
HFONT t_hFont1 = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体")); //创建字体
HGDIOBJ select = SelectObject(lpDIS->hDC, &t_hFont1); //设置字体
SetBkMode(lpDIS->hDC, TRANSPARENT);//设置绘图文字画刷背景色为透明
SetTextColor(lpDIS->hDC, RGB(0, 0, 0)); //设置绘图文字颜色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 5, "周志豪", strlen("周志豪")); //绘网名
SetTextColor(lpDIS->hDC, RGB(125, 125, 125)); //设置绘图文件颜色-灰色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 20, "有些事值得去奋斗", strlen("有些事值得去奋斗"));//画个性签名
SelectObject(lpDIS->hDC, select); //把旧的字体设置回去
DeleteObject(t_hFont1); //删除字体句柄
return 0; //直接返回防止系统重绘
}
else{
HBRUSH colo; //colo
colo = CreateSolidBrush(RGB(255, 255, 255)); //创建一个颜色刷子,非选中
FillRect(lpDIS->hDC, &lpDIS->rcItem, colo); //绘制矩形,填充背景
DeleteObject(colo); //删除颜色刷子
HICON hicon = (HICON)LoadImage(
NULL, //本地资源无需给出进程模块句柄,如果是资源文件需要进程模块句柄,可以使用GetModuleHandle(NULL)获取,null代表获取当前进程下的模块实例句柄
TEXT("C:\\Users\\ZZH\\Desktop\\1.ico"),
IMAGE_ICON,
32, 32,
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE); //加载ico
DrawIconEx(lpDIS->hDC, lpDIS->rcItem.left + 5, lpDIS->rcItem.top + 5, hicon, 32, 32, 0, 0, 0 | 3); //绘制32*32ico
DeleteObject(hicon); //删除ico句柄
HFONT t_hFont1 = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, FF_MODERN, _T("宋体")); //创建字体
HGDIOBJ select = SelectObject(lpDIS->hDC, &t_hFont1); //设置字体
SetBkMode(lpDIS->hDC, TRANSPARENT);//设置绘图文字画刷背景色为透明
SetTextColor(lpDIS->hDC, RGB(0, 0, 0)); //设置绘图文字颜色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 5, "周志豪", strlen("周志豪")); //绘网名
SetTextColor(lpDIS->hDC, RGB(125, 125, 125)); //设置绘图文件颜色-灰色
TextOut(lpDIS->hDC, lpDIS->rcItem.left + 40, lpDIS->rcItem.top + 20, "有些事值得去奋斗", strlen("有些事值得去奋斗"));//画个性签名
SelectObject(lpDIS->hDC, select); //把旧的字体设置回去
DeleteObject(t_hFont1); //删除字体句柄
return 0; //直接返回防止系统重绘
}
}
}
//判断消息ID
switch (uMsg){
case WM_MEASUREITEM://ODT_LISTBOX
if ((UINT)wParam == ID_LIST_1)
{
LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
lpmis->itemWidth = 400;
lpmis->itemHeight = 22;
}
break;
case WM_DESTROY: // 窗口销毁消息
PostQuitMessage(0); // 发送退出消息
return 0;
}
// 其他的消息调用缺省的消息处理程序
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// 3、注册窗口类型
BOOL RegisterWindow(LPCSTR lpcWndName, HINSTANCE hInstance)
{
ATOM nAtom = 0;
// 构造创建窗口参数
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc; // 指向窗口过程函数
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = NULL;
wndClass.hCursor = NULL;
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = lpcWndName; // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
nAtom = RegisterClass(&wndClass);
return TRUE;
}
//创建窗口(lpClassName 一定是已经注册过的窗口类型)
HWND CreateMyWindow(LPCTSTR lpClassName, HINSTANCE hInstance)
{
HWND hWnd = NULL;
// 创建窗口
hWnd = CreateWindow(lpClassName, "test", WS_OVERLAPPEDWINDOW^WS_THICKFRAME, 0, 0, 1000, 800, NULL, NULL, hInstance, NULL);
return hWnd;
}
//显示窗口
void DisplayMyWnd(HWND hWnd)
{
//获得屏幕尺寸
int scrWidth = GetSystemMetrics(SM_CXSCREEN);
int scrHeight = GetSystemMetrics(SM_CYSCREEN);
RECT rect;
GetWindowRect(hWnd, &rect);
ShowWindow(hWnd, SW_SHOW);
//重新设置rect里的值
rect.left = (scrWidth - rect.right) / 2;
rect.top = (scrHeight - rect.bottom) / 2;
//移动窗口到指定的位置
//SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
//MoveWindow(hWnd, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
UpdateWindow(hWnd);
}
void doMessage() // 消息循环处理函数
{
MSG msg = { 0 };
// 获取消息
while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
{
DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
}
}
long OldWindowProc;
WNDPROC lpfnOldProc = 0;
LRESULT NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_DRAWITEM)
{
MessageBox(0, "FF", "FF", 0);
DRAWITEMSTRUCT litr;
HBRUSH colo;
RtlMoveMemory(&litr, &lParam, 48);
if (litr.CtlType == ODT_LISTBOX){
if (litr.itemID != -1){
colo = CreateSolidBrush(0xFFF);
FillRect(litr.hDC, &litr.rcItem, colo);
DeleteObject(colo);
return 0;
}
}
}
return CallWindowProc(lpfnOldProc, hWnd, message, wParam, lParam);//交给旧的消息处理函数
}
LPCTSTR lpClassName = "MyWnd"; // 注册窗口的名称
// 入口函数
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
RegisterWindow(lpClassName, hInstance);
HWND hWnd1 = CreateMyWindow(lpClassName, hInstance);
hList = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_OWNERDRAWVARIABLE, 10, 100, 200,200, hWnd1, (HMENU)ID_LIST_1, NULL, NULL); //创建list
//添加子表项
SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)TEXT("list str1")); SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)TEXT("list str2")); SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)TEXT("list str3"));
DisplayMyWnd(hWnd1);
doMessage();
return 0;
}