第九章 子窗口控件
子窗口可以作为控制屏幕图形显示,响应用户输入,以及在有重要输入事件的时候通知另一窗口。
标准子窗口控件,按钮,复选框,编辑框,列表框,组合框,文本字符串和滚动条。
可以使用CreateWindow来创建子窗口控件,或者在程序的资源脚本里编辑好各种属性。
使用预定义控件不需要再注册相应的子窗口类,这些类已经存在于windows中并且已经有了预定义的名称。
在调用CreateWindow时,只需要使用该名称作为窗口类的参数即可。
在窗口表明直接创建子窗口,所涉及的任务比使用对话框内的子窗口控件更底层。对于对话框,对话框管理器在你的程序和控件之间增加了一个隔离层。
比如支持TAB,方向键切换焦点。 子窗口控件可以得到输入焦点,可是一旦得到焦点,它就无法把输入焦点交回给其父窗口。
标准控件, 通用控件。
9.1 按钮类
#include <windows.h> struct { int iStyle; TCHAR * szText; } button[] = { BS_PUSHBUTTON, TEXT("PUSHBUTTON"), BS_DEFPUSHBUTTON, TEXT("DEFPUSHBUTTON"), BS_CHECKBOX, TEXT("CHECKBOX"), BS_AUTOCHECKBOX, TEXT("AUTOCHECKBOX"), BS_RADIOBUTTON, TEXT("RADIOBUTTON"), BS_3STATE, TEXT("3STATE"), BS_AUTO3STATE, TEXT("AUTO3STATE"), BS_GROUPBOX, TEXT("GROUPBOX"), BS_AUTORADIOBUTTON, TEXT("AUTORADIO"), BS_OWNERDRAW, TEXT("OWNERDRAW") }; #define NUM (sizeof button / sizeof button[0]) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("BtnLook"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Button Look"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndButton[NUM]; static RECT rect; static TCHAR szTop[] = TEXT("message wParam lParam"), szUnd[] = TEXT("_______ ______ ______"), szFormat[] = TEXT("%-16s%04X-%04X %04X-%04X"), szBuffer[50]; static int cxChar, cyChar; HDC hdc; PAINTSTRUCT ps; int i; switch (message) //get the message { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); for (i = 0; i < NUM; i++) hwndButton[i] = CreateWindow(TEXT("button"), button[i].szText, WS_CHILD | WS_VISIBLE | button[i].iStyle, cxChar, cyChar * (1 + 2 * i), 20 * cxChar, 7 * cyChar / 4, hwnd, (HMENU)i, ((LPCREATESTRUCT)lParam)->hInstance, NULL); return 0; case WM_SIZE: rect.left = 24 * cxChar; rect.top = 2 * cyChar; rect.right = LOWORD(lParam); rect.bottom = HIWORD(lParam); return 0; case WM_PAINT: InvalidateRect(hwnd, &rect, TRUE); hdc = BeginPaint(hwnd, &ps); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); SetBkMode(hdc, TRANSPARENT); TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop)); TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd)); EndPaint(hwnd, &ps); return 0; case WM_DRAWITEM: case WM_COMMAND: ScrollWindow(hwnd, 0, -cyChar, &rect, &rect); hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1), szBuffer, wsprintf(szBuffer, szFormat, message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") : TEXT("WM_COMMAND"), HIWORD(wParam), LOWORD(wParam), HIWORD(lParam), LOWORD(lParam))); ReleaseDC(hwnd, hdc); ValidateRect(hwnd, &rect); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
9.1.1 创建子窗口
GetDialogBaseUnits 来获得字符默认的字体的宽度和高度。 低位和高位分别是宽度和高度
与GetTextMetrics返回类似的数据
- GetTextMetrics(hdc, &tm);
- cxChar = tm.tmAveCharWidth;
- cyChar = tm.tmHeight + tm.tmExternalLeading;
每个子窗口的ID是唯一的
在WM_CREATE消息中lParam 是一个指向CREATESTRUCT结构的指针。 hInstance是改结构的成员 即 ( (LPCREATESTRUCT)lParam)->hInstance,
或者
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE)
9.1.2 子窗口传递信息给父窗口
点击按钮时,子窗口发送WM_COMMAND 消息给父窗口。
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知码
lParam 子窗口句柄
通知码定义
通知码6,7 只有当包含 BS_NOTIFY样式时才会启用
子窗口有输入焦点以后,所有的键盘消息都会送到这个子窗口控件,而不是主窗口。按钮控件一旦获得输入焦点,就会忽略所有按键操作,但空格键除外,此时空格键具有和单击鼠标一样的效果。
9.1.3 父窗口传递信息给子窗口
例如父窗口发送 BM_GETCHECK BM_CHECK 给子窗口控件,以获得和设置复选框和单选框的选择状态。
在用鼠标或空格点击窗口时,BM_GETSTATE 和 BM_SETATETE消息反应一个窗口的状态是正常的还是被单击了。
BM_SETSTYLE 允许在创建按钮后改变按钮的样式。
id=GetWindowLong(hwndChild, GWL_ID);
id = GetDlgCtrlID (hwndChild);
hwndChild = GetDlgItem(hwndParent, id);
9.1.4 按钮
按钮控件 ,调用CreateWindow 或者MoveWindow可以改变其大小和位置, 是一个矩形。 按钮上显示文字
BS_PUSHBUTTON
BS_DEFPUSHBUTTON 较重的轮廓
按钮最佳视觉高度是字符高度的 7/4, 而宽度需要额外容纳两个文本
给子窗口发送BM_SETSTATE消息可以模拟按钮状态变化
SendMessage(hwndButton, BM_SETSTATE, 1, 0); //按钮被按下
SendMessage(hwndButton, BM_SETSTATE, 0, 0); //按钮回到正常状态
也可以给按钮发送一个BM_GETSTATE消息,子窗口控件返回当前按钮的状态。如果按钮时按下返回TRUE,否则FALSE.
9.1.5 复选框
checkBox
文本通常出现在复选框右侧,如果包含 BS_LEFTTEXT样式,则出现在左侧; 组合BS_RIGHT样式使文本右对齐
BS_CHECKBOX
必须给控件发送一个BM_SETCHECK消息来设置其选中标记。 wParam 设置1 会选中标记, 0则清楚标记。
发送BM_GETCHECK来获得复选框当前的状态。
在处理WM_COMMAND消息的时候
SendMessage((HWND)lParam , BM_SETCHECK, (WPARAM)
! SendMessage((HWND)lParam, BM_GETCHECK, 0, 0 ), 0 );
BS_AUTOCHECKBOX
按钮本身负责切换选定和取消标记,可以忽略WM_COMMAND消息
在需要按钮状态时
iCheck = (int) SendMessage(hwndButton, BM_GETCHECK, 0, 0 ); //选中为TRUE ,否则为FALSE
另外 BS_3STATE 和 BS_AUTO3STATE 有3种状态 BM_SETCHECK消息射wParam 为 2 表示灰色
复选框最低高度是一个字符高度,最小宽度是现有字符再加2个字符宽度。
9.1.6 单选按钮
任意时刻只有一个按钮可以被按下。
BS_RADIOBUTTON, BS_AUTORADIOBUTTON(只用于对话框)
在处理WM_COMMAND消息是应该向其发送消息表明其选中
SendMessage((HWND)lParam, BM_SETCHECK, 1, 0);
发送以下消息表明取消选中
SendMessage((HWND)lParam, BM_SETCHECK, 0, 0);
9.1.7 组合框
GroupBox
通常用来包容其他类型的控件
9.1.8 改变按钮文本
可以调用SetWindowText 来改变按钮的文本
SetWindowText(hwnd, pszString); //包括主窗口和子窗口
iLength = GetWindowText(hwnd, pszBuffer, iMaxLength); //获得窗口当前的文本 Caption属性
可以调用函数使程序对可以接受特定文本长度有所准备
iLength = GetWindowTextLength(hwnd);
9.1.9 可见的按钮和启动的按钮
如果创建子窗口未包含WM_VISIBLE 按钮时不可见的,除非调用
ShowWindow(hwndChild, SW_SHOWNORMAL);
如果窗口可见可以隐藏他
ShowWindow(hwndChild, SW_HIDE);
可以调用一下函数判断窗口是否可见
IsWindowVisible(hwndChild);
启用或禁用子窗口
EnableWindow(hwndChild, FALSE);
EnableWindow(hwndChild, TRUE);
调用一下函数判断子窗口是否被启用
IsWindowEnabled(hwndChild);
9.1.10 按钮和输入焦点
子窗口控件获得输入焦点后,父窗口就会失去输入焦点。之后所有的键盘输入将送到子窗口而不是其父窗口。
Windows把输入焦点从一个窗口切换到另一个窗口时,它首先会像要失去输入焦点的窗口发送一条消息WM_KILLFOCUS. 相应的wParam参数是将要获得输入焦点的窗口的句柄。然后Windows向要接受输入焦点的窗口发送WM_SETFOCUS消息,用wParam指定失去输入焦点的窗口的句柄。
父窗口可以通过WM_KILLFOCUS消息来阻止子窗口控件获得输入焦点。
case WM_KILLFOCUS: for (i = 0; i < NUM; i++) if (hwndButton[i] == (HWND)wParam) { SetFocus(hwnd); break; }
这样子窗口将不会获得输入焦点
或者
case WM_KILLFOCUS: if (hwnd == GetParent((HWND)wParam)) SetFocus(hwnd);
单这么做以后按钮将不能再响应消息。因为按钮从未得到输入焦点。
9.2 控件和颜色
9.2.1 系统颜色
windows有29种系统颜色来支持各部分显示。可以使用GetSysColor 和SetSysColor获取并设置这些颜色。
9.2.2 按钮的颜色
COLOR_BTNFACE 按钮表面
COLOR_BTNSHADOW 用于按钮底部和右侧,复选框方块的内部和单选按钮的圆圈内,用来表示阴影。
COLOR_BTNTEXT 文本颜色
COLOR_WINDOWTEXT 其他控件文本颜色
为了在客户区表面显示按钮,首先选用COLOR_BTNFACE作为客户区的背景颜色
wndClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE+1); //为了防止出现NULL值
TextOut显示文本时,windows使用设备环境中定义的值作为文本背景色,预设是白色背景 黑色文本。
SetTextColor SetBkColor 改变文办颜色和背景颜色
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
如果用户更改了系统颜色,则需要更改文办的背景颜色和文办颜色
case WM_SYSCOLORCHANGE:
InvalidateRect(hwnd, NULL, TRUE);
break;
修改后的Button Look 程序
#include <windows.h> struct { int iStyle; TCHAR * szText; } button[] = { BS_PUSHBUTTON, TEXT("PUSHBUTTON"), BS_DEFPUSHBUTTON, TEXT("DEFPUSHBUTTON"), BS_CHECKBOX, TEXT("CHECKBOX"), BS_AUTOCHECKBOX, TEXT("AUTOCHECKBOX"), BS_RADIOBUTTON, TEXT("RADIOBUTTON"), BS_3STATE, TEXT("3STATE"), BS_AUTO3STATE, TEXT("AUTO3STATE"), BS_GROUPBOX, TEXT("GROUPBOX"), BS_AUTORADIOBUTTON, TEXT("AUTORADIO"), BS_OWNERDRAW, TEXT("OWNERDRAW") }; #define NUM (sizeof button / sizeof button[0]) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("BtnLook"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Button Look"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndButton[NUM]; static RECT rect; static TCHAR szTop[] = TEXT("message wParam lParam"), szUnd[] = TEXT("_______ ______ ______"), szFormat[] = TEXT("%-16s%04X-%04X %04X-%04X"), szBuffer[50]; static int cxChar, cyChar; HDC hdc; PAINTSTRUCT ps; int i; switch (message) //get the message { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); for (i = 0; i < NUM; i++) hwndButton[i] = CreateWindow(TEXT("button"), button[i].szText, WS_CHILD | WS_VISIBLE | button[i].iStyle, cxChar, cyChar * (1 + 2 * i), 20 * cxChar, 7 * cyChar / 4, hwnd, (HMENU)i, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE)/*((LPCREATESTRUCT)lParam)->hInstance*/, NULL); return 0; case WM_SIZE: rect.left = 24 * cxChar; rect.top = 2 * cyChar; rect.right = LOWORD(lParam); rect.bottom = HIWORD(lParam); return 0; case WM_SYSCOLORCHANGE: InvalidateRect(hwnd, NULL, TRUE); break; case WM_PAINT: InvalidateRect(hwnd, &rect, TRUE); hdc = BeginPaint(hwnd, &ps); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); SetBkMode(hdc, TRANSPARENT); SetBkColor(hdc, GetSysColor(COLOR_BTNFACE)); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); TextOut(hdc, 24 * cxChar, cyChar, szTop, lstrlen(szTop)); TextOut(hdc, 24 * cxChar, cyChar, szUnd, lstrlen(szUnd)); EndPaint(hwnd, &ps); return 0; case WM_DRAWITEM: case WM_COMMAND: ScrollWindow(hwnd, 0, -cyChar, &rect, &rect); hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); SetBkColor(hdc, GetSysColor(COLOR_BTNFACE)); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); TextOut(hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1), szBuffer, wsprintf(szBuffer, szFormat, message == WM_DRAWITEM ? TEXT("WM_DRAWITEM") : TEXT("WM_COMMAND"), HIWORD(wParam), LOWORD(wParam), HIWORD(lParam), LOWORD(lParam))); switch(LOWORD(wParam)) { case 0: EnableWindow(hwndButton[1], FALSE); break; case 2://checkbox SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM) !SendMessage((HWND)lParam, BM_GETCHECK, 0, 0), 0); break; case 4://Radiobutton SendMessage((HWND)lParam, BM_SETCHECK, 1, 0); break; } ReleaseDC(hwnd, hdc); ValidateRect(hwnd, &rect); break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
9.2.3 WM_CTLCOLORBTN 消息
最好不要用SetSysColors改变按钮外观,这将改变windows环境下正在运行的所有程序
或者重载WM_CTLCOLORBTN消息,当子窗口需要重绘时按钮会把这个消息发给父窗口。可以利用这个机会来改变子窗口颜色
当父窗口收到WM_CTLCOLORBTN消息时,wParam是子窗口的设备环境句柄,lParam是子窗口句柄。
SetTextColor 设置文本颜色
SetBkColor 设置文本背景色
返回子窗口的画刷句柄 子窗口使用这个画刷来着色背景。在不需要画刷时,你需要负责销毁画刷。
9.2.4 自绘按钮
#include <windows.h> #define ID_SMALLER 1 #define ID_LARGER 2 #define BTN_WIDTH (8 * cxChar) #define BTN_HEIGHT (4 * cyChar) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. HINSTANCE hInst; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("OwnDraw"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class hInst = hInstance; wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = szAppName; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Owner-Draw Button Demo"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void Triangle(HDC hdc, POINT pt[]) { SelectObject(hdc, GetStockObject(BLACK_BRUSH)); Polygon(hdc, pt, 3); SelectObject(hdc, GetStockObject(WHITE_BRUSH)); } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndSmaller, hwndLarger; static int cxClient, cyClient, cxChar, cyChar; int cx, cy; LPDRAWITEMSTRUCT pdis; POINT pt[3]; RECT rc; switch (message) //get the message { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); //Create the owner-draw pushbuttons hwndSmaller = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU) ID_SMALLER, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); hwndLarger = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_LARGER, ((LPCREATESTRUCT)lParam)->hInstance, NULL); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); //Move the buttons to the new center MoveWindow(hwndSmaller, cxClient / 2 - 3 * BTN_WIDTH / 2, cyClient / 2 - BTN_HEIGHT / 2, BTN_WIDTH, BTN_HEIGHT, TRUE); MoveWindow(hwndLarger, cxClient / 2 + BTN_WIDTH / 2, cyClient / 2 - BTN_HEIGHT / 2, BTN_WIDTH, BTN_HEIGHT, TRUE); return 0; case WM_COMMAND: GetWindowRect(hwnd, &rc); //Make the wnidow 10% smaller or larger switch (wParam) { case ID_SMALLER: rc.left += cxClient / 20; rc.right -= cxClient / 20; rc.top += cyClient / 20; rc.bottom -= cyClient / 20; break; case ID_LARGER: rc.left -= cxClient / 20; rc.right += cxClient / 20; rc.top -= cyClient / 20; rc.bottom += cyClient / 20; } MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); return 0; case WM_DRAWITEM: pdis = (LPDRAWITEMSTRUCT)lParam; FillRect(pdis->hDC, &pdis->rcItem, (HBRUSH)GetStockObject(WHITE_BRUSH)); FrameRect(pdis->hDC, &pdis->rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH)); //Draw inward and outward black triangles cx = pdis->rcItem.right - pdis->rcItem.left; cy = pdis->rcItem.bottom - pdis->rcItem.top; switch (pdis->CtlID) { case ID_SMALLER: pt[0].x = 3 * cx / 8; pt[0].y = 1 * cy / 8; pt[1].x = 5 * cx / 8; pt[1].y = 1 * cy / 8; pt[2].x = 4 * cx / 8; pt[2].y = 3 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 7 * cx / 8; pt[0].y = 3 * cy / 8; pt[1].x = 7 * cx / 8; pt[1].y = 5 * cy / 8; pt[2].x = 5 * cx / 8; pt[2].y = 4 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 5 * cx / 8; pt[0].y = 7 * cy / 8; pt[1].x = 3 * cx / 8; pt[1].y = 7 * cy / 8; pt[2].x = 4 * cx / 8; pt[2].y = 5 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 1 * cx / 8; pt[0].y = 5 * cy / 8; pt[1].x = 1 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 3 * cx / 8; pt[2].y = 4 * cy / 8; Triangle(pdis->hDC, pt); break; case ID_LARGER: pt[0].x = 5 * cx / 8; pt[0].y = 3 * cy / 8; pt[1].x = 3 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 4 * cx / 8; pt[2].y = 1 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 5 * cx / 8; pt[0].y = 5 * cy / 8; pt[1].x = 5 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 7 * cx / 8; pt[2].y = 4 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 3 * cx / 8; pt[0].y = 5 * cy / 8; pt[1].x = 5 * cx / 8; pt[1].y = 5 * cy / 8; pt[2].x = 4 * cx / 8; pt[2].y = 7 * cy / 8; Triangle(pdis->hDC, pt); pt[0].x = 3 * cx / 8; pt[0].y = 3 * cy / 8; pt[1].x = 3 * cx / 8; pt[1].y = 5 * cy / 8; pt[2].x = 1 * cx / 8; pt[2].y = 4 * cy / 8; Triangle(pdis->hDC, pt); break; } //Invert the rectangle if the button is selected if (pdis->itemState & ODS_SELECTED) InvertRect(pdis->hDC, &pdis->rcItem); //Draw a focus rectangle if the button has the focus if (pdis->itemState & ODS_FOCUS) { pdis->rcItem.left += cx / 16; pdis->rcItem.top += cy / 16; pdis->rcItem.right -= cx / 16; pdis->rcItem.bottom -= cy / 16; DrawFocusRect(pdis->hDC, &pdis->rcItem); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
该例子绘制了两个按钮,点击能分别扩大和缩小窗口客户区的尺寸
在按钮上显示图标或位图,可以使用BS_ICON 或者BS_BITMAP样式。 设置位图可以使用BM_SETIMAGE消息。
但BS_OWNERDRAW允许你完全自己绘制按钮。
GetWindowRect存储整个客户区的矩形区域,位置是相对屏幕的坐标
DRAWITEMSTRUCT结构中与绘制按钮相关的字段
hDC 按钮设备环境
rcItem RECT结构
CtlID 控件的ID
itemState 按钮是否按下或者有输入焦点
InvertRect 用来颠倒矩形区域的颜色 ODS_SELECTED
DrawFocusRect 绘制焦点框 ODS_FOCUS
9.3 静态类
使用CreateWindow函数和静态窗口类来创建静态子窗口控件。
当单机静态子窗口时,会获得WM_NCHITTEST消息, 并向windows返回HITTRANSPARENT值。 通常交给DefWindowProc处理
静态窗口样式
SS_BLACKRECT SS_BLACKFRAME
SS_GRAYRECT SS_GRAYFRAME
SS_WHITERECT SS_WHITEFRAME
基于下表所显示的系统颜色
CreateWindow 调用的窗口文本字段将被忽略。 矩形左上角坐标是相对父窗口。 可以使用SS_ETCHEDHORZ, SS_ETCHEDVERT 或者SS_ETCHEDFRAME样式来用白色和灰色创建一个阴影框架
静态类包含三个文本样式: SS_LEFT, SS_RIGHT 和 SS_CENTER。 这样会创建左对齐,右对齐和居中的文本。 文本右CreateWindow函数窗口文本参数确定
可以使用SetWindowText修改。 当静态类显示文本时,会使用DT_WORDBREAK, DT_NOCLIP 和 DT_EXPANDTABS等参数来调用DrawText函数。
子窗口矩形框具有文本自动换行功能。
这三种样式的背景颜色通常是 COLOR_BTNFACE, 文本颜色是 COLOR_WINDOWTEXT.
可以复WM_COLORSTATIC消息,调用SetTextColor 和SetBkColor 分别改变文本颜色和背景颜色,同时返回背景画刷句柄。
静态类还有两个样式 SS_ICON SS_USERITEM 然后当它用作子窗口控件,这些样式时无效的。
9.4 滚动条类
滚动条不发送WM_COMMAND消息到父窗口。 他发送WM_VSCROLL 和 WM_HSCROLL lParam参数区分窗口滚动条和滚动条控件。 lParam为0 就是窗口滚动条
如果等于滚动条窗口句柄,就是滚动条空降 wParam 高位和低位对于窗口滚动条和滚动条控件含义是一样的
可以使用MoveWindow 或者CreateWindow来设定滚动条控件的大小。
可以使用GetSystemMetrics(SM_CYHSCROLL) 水平滚动条的高度
GetSystemMetrics(SM_CXVSCROLL); 垂直滚动条的宽度
滚动条窗口样式标识符 SBS_LEFTALIGN, SBS_RIGHTALIGN, SBS_TOPALIGN 和 SBS_BOTTOMALIGN 都为滚动条提供标准尺寸。
SetScrollRange(hwndScroll, SB_CTL, iMin, iMax, bRedraw);
SetScrollPos(hwndScroll, SB_CTL, iPos, bRedraw);
SetScrollInfo(hwndScroll, SB_CTL, &si, bRedraw);
滚动条两端按钮颜色基于 COLOR_BTNFACE, COLOR_BTNHILIGHT, COLOR_BTNSHADOW, COLOR_BTNTEXT(给小箭头使用),COLOR_DKSHADOW 以及COLOR_BTNLIGHT.
两端按钮之间的大片区域基于 COLOR_BTNFACE和 COLOR_BTNHILIGHT的某种组合。
如果俘获了WM_CTLCOLORSCROLLBAR消息,就可以从这个消息返回一个画刷来改变原理的颜色。
9.4.1 COLORS1 程序
#include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. LRESULT CALLBACK ScrollProc(HWND, UINT, WPARAM, LPARAM); int idFocus; WNDPROC OldScroll[3]; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Colors1"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Color Scroll"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static COLORREF crPrim[3] = { RGB(255, 0, 0), RGB(0, 255, 0), RGB(0, 0, 255) }; static HBRUSH hBrush[3], hBrushStatic; static HWND hwndScroll[3], hwndLabel[3], hwndValue[3], hwndRect; static int color[3], cyChar; static RECT rcColor; static TCHAR * szColorLabel[] = { TEXT("Red"), TEXT("Green"), TEXT("Blue") }; HINSTANCE hInstance; int i, cxClient, cyClient; TCHAR szBuffer[10]; switch (message) //get the message { case WM_CREATE: hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); //Create the white-rectangle window against which the //scroll bars will be positioned. The child window ID is 9. hwndRect = CreateWindow(TEXT("static"), NULL, WS_CHILD | WS_VISIBLE | SS_WHITERECT, 0, 0, 0, 0, hwnd, (HMENU) 9, hInstance, NULL); for (i = 0; i < 3; i++) { //The three scroll bars have IDs 0, 1, and 2, with //scroll bar ranges from 0 through 255. hwndScroll[i] = CreateWindow(TEXT("scrollbar"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL); SetScrollRange(hwndScroll[i], SB_CTL, 0, 255, FALSE); SetScrollPos (hwndScroll[i], SB_CTL, 0, FALSE); //The three color-name labels have IDs 3, 4, and 5, //and text strings "Red", "Green", "Blue". hwndLabel[i] = CreateWindow(TEXT("static"), szColorLabel[i], WS_CHILD | WS_VISIBLE | SS_CENTER, 0, 0, 0, 0, hwnd, (HMENU) (i + 3), hInstance, NULL); //The three color-value text fields have Ids 6, 7, //and 8, and initial text strings of "0". hwndValue[i] = CreateWindow(TEXT("static"), TEXT("0"), WS_CHILD | WS_VISIBLE | SS_CENTER, 0, 0, 0, 0, hwnd, (HMENU)(i + 6), hInstance, NULL); OldScroll[i] = (WNDPROC)SetWindowLong(hwndScroll[i], GWL_WNDPROC, (LONG)ScrollProc); //Register the windowproc to sub windows. hBrush[i] = CreateSolidBrush(crPrim[i]); } hBrushStatic = CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT)); cyChar = HIWORD(GetDialogBaseUnits()); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); SetRect(&rcColor, cxClient / 2, 0, cxClient, cyClient); MoveWindow(hwndRect, 0, 0, cxClient / 2, cyClient, TRUE); for (i = 0; i < 3; i++) { MoveWindow(hwndScroll[i], (2 * i + 1) * cxClient / 14, 2 * cyChar, cxClient / 14, cyClient - 4 * cyChar, TRUE); MoveWindow(hwndLabel[i], (4 * i + 1) * cxClient / 28, cyChar / 2, cxClient / 7, cyChar, TRUE); MoveWindow(hwndValue[i], (4 * i + 1) * cxClient / 28, cyClient - 3 * cyChar / 2, cxClient / 7, cyChar, TRUE); } SetFocus(hwnd); return 0; case WM_SETFOCUS: SetFocus(hwndScroll[idFocus]); return 0; case WM_VSCROLL: i = GetWindowLong((HWND)lParam, GWL_ID); switch (LOWORD(wParam)) { case SB_PAGEDOWN: color[i] += 15; //fall through case SB_LINEDOWN: color[i] = min(255, color[i] + 1); break; case SB_PAGEUP: color[i] -= 15; //fall through case SB_LINEUP: color[i] = max(0, color[i] - 1); break; case SB_TOP: color[i] = 0; break; case SB_BOTTOM: color[i] = 255; break; case SB_THUMBPOSITION: case SB_THUMBTRACK: color[i] = HIWORD(wParam); break; default: break; } SetScrollPos(hwndScroll[i], SB_CTL, color[i], TRUE); wsprintf(szBuffer, TEXT("%i"), color[i]); SetWindowText(hwndValue[i], szBuffer); DeleteObject((HBRUSH) SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush(RGB(color[0], color[1], color[2])))); InvalidateRect(hwnd, &rcColor, TRUE); return 0; case WM_CTLCOLORSCROLLBAR: i = GetWindowLong((HWND)lParam, GWL_ID); return (LRESULT)hBrush[i]; case WM_CTLCOLORSTATIC: i = GetWindowLong((HWND)lParam, GWL_ID); if (i >= 3 && i <= 8) // static text controls { SetTextColor((HDC)wParam, crPrim[i % 3]); SetBkColor((HDC)wParam, GetSysColor(COLOR_BTNHIGHLIGHT)); return (LRESULT)hBrushStatic; } break; case WM_SYSCOLORCHANGE: DeleteObject(hBrushStatic); hBrushStatic = CreateSolidBrush(GetSysColor(COLOR_BTNHIGHLIGHT)); return 0; case WM_DESTROY: DeleteObject((HBRUSH) SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) GetStockObject((WHITE_BRUSH)))); for (i = 0; i < 3; i++) DeleteObject(hBrush[i]); DeleteObject(hBrushStatic); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } LRESULT CALLBACK ScrollProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { int id = GetWindowLong(hwnd, GWL_ID); switch (message) { case WM_KEYDOWN: if (wParam == VK_TAB) SetFocus(GetDlgItem(GetParent(hwnd), (id + (GetKeyState(VK_SHIFT) < 0 ? 2 : 1)) % 3)); break; case WM_SETFOCUS: idFocus = id; break; } return CallWindowProc(OldScroll[id], hwnd, message, wParam, lParam); }
运行结果
9.4.2 自动键盘接口
在CreateWindow 中加入WS_TABSTOP 标识符。当滚动条获得输入焦点时,滚动条上会显示一个闪烁的灰色快。
父窗口在获得焦点以后要捕获WM_SETFOCUS消息,并传递给子窗口
SetFocus(hwndScroll[idFocus]);
9.4.3 窗口子类
滚动条类的窗口过程在windows内部
。 但是可以调用GetWindowLong来获取地址使用GWL_WNDPROC标识符作为参数即可。还可以调用SetWindowLong 为滚动条设置一个新的窗口过程, 称为窗口子类。
OldScroll[i] = (WNDPROC)SetWindowLong(hwndScroll[i], GWL_WNDPROC, (LONG)ScrollProc); //Register the windowproc to sub windows.
设置新的滚动条窗口过程,并且保留现有滚动条窗口过程的地址。
9.4.4 背景颜色
使用GetClassWord 和SetClassWord来获取和设置窗口背景画刷的句柄。
你可以创建新的画刷,把句柄存入到窗口类的结构中,然后删除旧的画刷
DeleteObject((HBRUSH) SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush(RGB(color[0], color[1], color[2]))));
下次windows重绘窗口时,就会使用这个新的画刷
可以强制让windows擦除背景
InvalidateRect(hwnd, &rcColor, TRUE);
UpdateWindow(hwnd); 立即更新 不过可能会拖慢键盘和鼠标处理速度。
在处理WM_DESTORY消息时, 删除当前创建的画刷
DeleteObject((HBRUSH) SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG) GetStockObject((WHITE_BRUSH))));
9.4.5 给滚动条和静态文本着色
滚动条着色是通过处理 WM_CTLCOLORSCROLLBAR消息来完成的。
返回一个已经创建的画刷 return (LRESULT) hBrush[i];
这些画刷必须在WM_DESTROY消息期间被销毁
for(i = 0; i < 3; i++)
DeleteObject(hBrush[i]);
静态文本框颜色是通通过WM_CTLCOLORSTATIC消息和调用SetTextColor完成。 文本背景调用SetBkColor设置为 COLOR_BTNHIGHLIGHT,但该颜色仅限制于显示文字的区域。同时返回一个hBrushStatic 设置静态文本窗口的整个背景颜色。
为了防止系统颜色改变
在WM_SYSCOLORCHANGE消息时用心颜色重新创建hBrushStatic.
9.5 编辑类
POPPAD1
#include <windows.h> #define ID_EDIT 1 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. TCHAR szAppName[] = TEXT("PopPad1"); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { //static TCHAR szAppName[] = TEXT("Colors1"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("PopPad1"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndEdit; switch (message) //get the message { case WM_CREATE: hwndEdit = CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hwnd, (HMENU)ID_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL); return 0; case WM_SETFOCUS: SetFocus(hwndEdit); return 0; case WM_SIZE: MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); return 0; case WM_COMMAND: if (LOWORD(wParam) == ID_EDIT) if (HIWORD(wParam) == EN_ERRSPACE || HIWORD(wParam) == EN_MAXTEXT) MessageBox(hwnd, TEXT("Edit control out of spae."), szAppName, MB_OK | MB_ICONSTOP); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
运行结果如下
9.5.1 编辑类的样式
在失去焦点依然保持高亮要选用 ES_NOHIDESEL样式
在POPPAD1 程序创建编辑控件时,相应的样式时在CreateWindow调用中指定的
WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
WS_BORDER | ES_LEFT | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL
编辑控件的尺寸是在收到WM_SIZE以后调用MoveWindow设置的
MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
9.5.2 编辑控件的通知消息
9.5.3 使用编辑控件
如果使用了多个编辑控件,需要拦截TAB消息或者SHIFT TAB来实现焦点切换。
使用SetWindowText 像编辑框插入内容。 从编辑控件读取文本使用GetWindowTextLength 和GetWindowText
9.5.4 传递给编辑控件的消息
SendMessage(hwndEdit, WM_CUT, 0, 0 );
SendMessage(hwndEdit, WM_COPY, 0, 0 );
SendMessage(hwndEdit, WM_CLEAR, 0, 0);
WM_CUT消息把当前选择从编辑控件中移走并发送给剪贴板。
WM_COPY 把当前选择从编辑框控件拷贝到剪贴板
WM_CLEAR 把当前选择从编辑控件删除并且不传递给剪贴板
SendMessage(hwndEdit, WM_PASTE, 0, 0);
把剪贴板上的文本插入到编辑控件的当前位置
使用如下函数获得当前选择文本的出师位置和终止位置
SendMesssage(hwndEdit, EM_GETSEL, (WPARAM)&iSart, (LPARAM) &iEnd);
终止位置是你选择最后一个字符的位置加上1
可以如下选择文本
SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd);
使用其他文本取代当前选择的文本
SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szString);
获得文本编辑器的行数
iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0);
获得从编辑器缓冲区的起点到这行的位移量
iOffset = SendMessage(hwndEdit, EM_LINEINDEX, iLine, 0 );
行的长度
iLength = SendMessage(hwndEdit, EM_LINELENGTH, iLine, 0);
复制某一行到缓冲区
iLength = SendMessage(hwndEdit, EM_GETLINE, iLine, (LPARAM)szBuffer);
9.6 列表框控件
支持单选或者多选 当有项目被选择会发送WM_COMMAND消息到其父窗口
被选中的项目会高亮显示
在单选列表框中,空格键选择项目。方向键可以移动光标和当前选择,可以滚动列表框的内容。 上下翻页也可以移动光标来滚动列表框,但不会移动项目,单击或者双击也可以选中项
多选列表框中,空格键用于切换光标所在项目的状态。方向键取消所有一切选定的项目,并移动光标和选中项。
9.6.1 列表框样式
WS_CHILD 子窗口控件
LBS_NOTIFY 使父窗口能够接受从列表框发来的WM_COMMAND信息
LBS_SORT 对项目进行排序
LBS_MULTIPLESEL 默认是单选 多选使用该样式
LBS_NOREDRAW 防止列表框自动更新
使用WM_SETREDRAW 消息
WS_BORDER 显示边框
WS_VSCROLL 显示垂直滚动条
在Windows头文件中定义了一个最标准的列表框样式
LBS_STANDARD 包含 (LBS_NOFITY | LBS_SORT | WS_VSCROLL | WS_BORDER)
WS_SIZEBOX 和 WS_CAPTION 允许用户调整列表框的尺寸,以及在父窗口的客户区移动它
GetSystemMetrics(SM_CXVSCROLL) 垂直滚动条的宽度
列表框的宽度 = 最长字符串的长度 + 滚动条的宽度。
9.6.2 向列表框添加字符
SendMessage 和索引来引用 最上方索引为0
hwndList iIndex
SendMessage传入文本字符串的时候 ,lParam参数是一个以空字符结尾的字符串的指针。
如果列表框用完了存储空间,SendMessage调用可能返回 LB_ERRSPACE值 ,如果其他错误则返回LB_ERR, 如果成功返回LB_OKAY
如果设定了LBS_SORT样式则 使用LB_ADDSTRING来添加字符串
SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);
如果未使用LBS_SORT则使用
SendMessage(hwndList, LB_INSERTSTRING, iIndex, (LPARAM)szString); //不排序
SendMessage(hwndList, LB_DELETESTRING, iIndex, 0 ) 删除列表框的指定位置处删除一字符串
SendMessage(hwndList, LB_RESETCONTENT, 0, 0); 删除所有项目
SendMessage(hwndList, WM_SETREDRAW, FALSE, 0 ); 关掉列表框重绘
SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); 开启重绘
LBS_NOREDRAW样式的列表框最初开始会关闭重绘标志
9.6.3 项目的选取和提取
返回值LB_ERR
iCount = SendMessage(hwndList, LB_GETCOUNT, 0, 0 ); 了解列表框有多少项目
对于单选
SendMessage(hwndList, LB_SETCURSEL, iIndex, 0); 设定选中项目
SendMessage(hwndList, LB_SETCURSEL, iIndex, -1); 取消选中项目
iIndex = SendMessage(hwndList, LB_SELECTSTRING, iIndex, (LPARAM)szSearchString); 根据起始字符选择
在收到来自列表框的WM_COMMAND消息时,可以使用LB_GETCURSEL获得当前选择的索引值
iIndex = SendMessage(hwndList, LB_GETCURSEL, 0, 0 );
如果未选择则返回 LB_ERR
iLength = SendMessage(hwndList, LB_GETTEXTLEN, iIndex, 0); 获得列表框中任何字符串的长度
并把改项目复制到文本缓冲区
iLength = SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)szBuffer);
可以利用GETTEXTLEN来为字符串存储预先分配空间。
对于多选框
不可使用 LB_SETCURSEL, LB_GETCURSEL 或者 LB_SELECTSTRING
应该使用LB_SETSEL
SendMessage(hwndList, LB_SETSEL, wParam, iIndex); 设定某个特定项目的选择状态。 wParam非0 选择并高亮显示, 如果是0. 取消选择。如果lParam则选择全部或者全部取消选择
iSelect = SendMessage(hwndList, LB_GETSEL, iIndex, 0); 判断指定项目是否被选择
9.6.4 接收来自列表框的消息
SetFocus(hwndList) 把焦点传给列表框
只有包含了LBS_NOFITY 列表框控件才会向父窗口发送LBN_SELCHANGE 和 LBN_DBLCLK.
9.6.5 简单的列表框程序
#include <windows.h> #define ID_LIST 1 #define ID_TEXT 2 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Environ"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("Environment List Box"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void FillListBox(HWND hwndList) { int iLength; TCHAR * pVarEnv, * pVarBlock, *pVarBeg, *pVarEnd, *pVarName; pVarEnv = GetEnvironmentStrings(); // Get pointer to environment block. pVarBlock = pVarEnv; while (*pVarBlock) { if (*pVarBlock != TEXT('=')) //Skip variable names beginning with '=' { pVarBeg = pVarBlock; //Beginning of variable name while (*pVarBlock++ != TEXT('=')); //Scan until '=' pVarEnd = pVarBlock - 1; //Points to '=' sign iLength = pVarEnd - pVarBeg; //Length of variable name //Allocate memory for the variable name and terminating //zero. Copy the variable name and append a zero. pVarName = (TCHAR*)calloc(iLength + 1, sizeof(TCHAR)); CopyMemory(pVarName, pVarBeg, iLength * sizeof(TCHAR)); pVarName[iLength] = TEXT('\0'); //Put the variable name in the list box and free memory. SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)pVarName); free(pVarName); } while (*pVarBlock++ != TEXT('\0')); //Scan until terminating zero } FreeEnvironmentStrings(pVarEnv); } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hwndList, hwndText; int iIndex, iLength, cxChar, cyChar; TCHAR * pVarName, *pVarValue; switch (message) //get the message { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); //Create listbox and static text windows. hwndList = CreateWindow(TEXT("listbox"), NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD, cxChar, cyChar * 3, cxChar * 16 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 6, hwnd, (HMENU)ID_LIST, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); hwndText = CreateWindow(TEXT("static"), NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, cxChar, cyChar, GetSystemMetrics(SM_CXSCREEN), cyChar, hwnd, (HMENU)ID_TEXT, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); FillListBox(hwndList); return 0; case WM_SETFOCUS: SetFocus(hwndList); return 0; case WM_COMMAND: if (LOWORD(wParam) == ID_LIST && HIWORD(wParam) == LBN_SELCHANGE) { //Get current selection. iIndex = SendMessage(hwndList, LB_GETCURSEL, 0, 0); iLength = SendMessage(hwndList, LB_GETTEXTLEN, iIndex, 0) + 1; pVarName = (TCHAR*)calloc(iLength, sizeof(TCHAR)); SendMessage(hwndList, LB_GETTEXT, iIndex, (LPARAM)pVarName); //Get environment string. iLength = GetEnvironmentVariable(pVarName, NULL, 0); pVarValue = (TCHAR*)calloc(iLength, sizeof(TCHAR)); GetEnvironmentVariable(pVarName, pVarValue, iLength); //Show it in window SetWindowText(hwndText, pVarValue); free(pVarName); free(pVarValue); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
运行结果
9.6.6 列出文件
SendMessage(hwndList, LB_DIR, iAttr, (LPARAM) szFileSpec);
将文件目录列表写入列表框中,包括子目录和有效的磁盘驱动器。
lParam 是一个指向文件限定字符(*.*)的指针。该文件限定不影响列表框包含的子目录
实例代码
hwndListDir = CreateWindow(TEXT("listbox"), NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD , cxChar, cyChar * 10, cxChar * 16 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 6, hwnd, (HMENU)ID_LIST, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
SendMessage(hwndListDir, LB_DIR, DDL_READWRITE | DDL_DRIVES | DDL_DIRECTORY,
(LPARAM)(TEXT("*.*")));
9.6.7 Windows的HEAD程序
#include <windows.h> #define ID_LIST 1 #define ID_TEXT 2 #define MAXREAD 8192 #define DIRATTR (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES) #define DTFLAGS (DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP | DT_NOPREFIX) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure. LRESULT CALLBACK ListProc(HWND, UINT, WPARAM, LPARAM); WNDPROC OldList; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("head"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class. wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);//(HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szAppName; //Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)) { MessageBox(NULL, TEXT("This program require Windows NT!"), szAppName, MB_ICONERROR); return 0; } //This function will generate an WM_CREATE message. hwnd = CreateWindow(szAppName, //Window class name TEXT("head"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); //This function will generate a WM_PAINT message. /* The message loop for this program. if received the WM_QUIT message, the function will return 0.*/ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //define the Window Procedure WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static BOOL bValidFile; static BYTE buffer[MAXREAD]; static HWND hwndList, hwndText; static RECT rect; static TCHAR szFile[MAX_PATH + 1]; HANDLE hFile; HDC hdc; int i, cxChar, cyChar; PAINTSTRUCT ps; TCHAR szBuffer[MAX_PATH + 1]; switch (message) //get the message { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); rect.left = 20 * cxChar; rect.top = 3 * cyChar; //Create listbox and static text windows. hwndList = CreateWindow(TEXT("listbox"), NULL, WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD, cxChar, cyChar * 3, cxChar * 13 + GetSystemMetrics(SM_CXVSCROLL), cyChar * 10, hwnd, (HMENU)ID_LIST, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); GetCurrentDirectory(MAX_PATH + 1, szBuffer); hwndText = CreateWindow(TEXT("static"), szBuffer, WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT, cxChar, cyChar, cxChar * MAX_PATH, cyChar, hwnd, (HMENU)ID_TEXT, (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL); OldList = (WNDPROC)SetWindowLong(hwndList, GWL_WNDPROC, (LPARAM)ListProc); SendMessage(hwndList, LB_DIR, DIRATTR, (LPARAM)TEXT("*.*")); return 0; case WM_SIZE: rect.right = LOWORD(lParam); rect.bottom = HIWORD(lParam); return 0; case WM_SETFOCUS: SetFocus(hwndList); return 0; case WM_COMMAND: if (LOWORD(wParam) == ID_LIST && HIWORD(wParam) == LBN_DBLCLK) { i = SendMessage(hwndList, LB_GETCURSEL, 0, 0); if (i == LB_ERR) break; SendMessage(hwndList, LB_GETTEXT, i, (LPARAM)szBuffer); if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szBuffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))) { CloseHandle(hFile); bValidFile = TRUE; lstrcpy(szFile, szBuffer); GetCurrentDirectory(MAX_PATH + 1, szBuffer); if (szBuffer[lstrlen(szBuffer) - 1] != TEXT('\\')) lstrcat(szBuffer, TEXT("\\")); SetWindowText(hwndText, lstrcat(szBuffer, szFile)); } else { bValidFile = FALSE; szBuffer[lstrlen(szBuffer) - 1] = TEXT('\0'); //if setting the directory doesn't work, maybe it's //a drive change, so try that. if (!SetCurrentDirectory(szBuffer + 1)) { szBuffer[3] = TEXT(':'); szBuffer[4] = TEXT('\0'); SetCurrentDirectory(szBuffer + 2); } //Get the new directory name and fill the list box. GetCurrentDirectory(MAX_PATH + 1, szBuffer); SetWindowText(hwndText, szBuffer); SendMessage(hwndList, LB_RESETCONTENT, 0, 0); SendMessage(hwndList, LB_DIR, DIRATTR, (LPARAM)TEXT("*.*")); } InvalidateRect(hwnd, NULL, TRUE); } return 0; case WM_PAINT: if (!bValidFile) break; if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))) { bValidFile = FALSE; } ReadFile(hFile, buffer, MAXREAD, (LPDWORD)&i, NULL); CloseHandle(hFile); //i now equals the number of bytes in buffer. //Commence getting a device context for displaying text. hdc = BeginPaint(hwnd, &ps); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); SetBkColor(hdc, GetSysColor(COLOR_BTNFACE)); //Assume the file is ASCII DrawTextA(hdc, (LPCSTR)buffer, i, &rect, DTFLAGS); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } LRESULT CALLBACK ListProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if(message == WM_KEYDOWN && wParam == VK_RETURN) SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(1, LBN_DBLCLK), (LPARAM)hwnd); return CallWindowProc(OldList, hwnd, message, wParam, lParam); }