第九章 子窗口控件
子窗口可以作为控制屏幕图形显示,响应用户输入,以及在有重要输入事件的时候通知另一窗口。
标准子窗口控件,按钮,复选框,编辑框,列表框,组合框,文本字符串和滚动条。
可以使用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 按钮的颜色