目录:
- 子窗口与主窗口的交互
- 子窗口控件
- 按钮类别 button
- 滚动条类别 scrollbar
- 静态类别 static
- 编辑框类别 edit
- 清单方块 listbox
子窗口与主窗口的交互
创建窗口要:注册窗口类型 和 创造相应窗口实例
1 //注册窗口类型 2 wndclass.style = CS_HREDRAW | CS_VREDRAW ; 3 wndclass.lpfnWndProc = WndProc ; 4 wndclass.cbClsExtra = 0 ; 5 wndclass.cbWndExtra = 0 ; 6 wndclass.hInstance = hInstance ; 7 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; 8 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; 9 wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 10 wndclass.lpszMenuName = NULL ; 11 wndclass.lpszClassName = szAppName ; 12 13 if (!RegisterClass (&wndclass)) 14 { 15 MessageBox (NULL, TEXT ("This program requires Windows NT!"), 16 szAppName, MB_ICONERROR) ; 17 return 0 ; 18 }
1 //创建窗口实例 2 hwnd = CreateWindow (szAppName, // window class name 3 TEXT ("The Hello Program"), // window caption 4 WS_OVERLAPPEDWINDOW, // window style 5 CW_USEDEFAULT, // initial x position 6 CW_USEDEFAULT, // initial y position 7 CW_USEDEFAULT, // initial x size 8 CW_USEDEFAULT, // initial y size 9 NULL, // parent window handle 10 NULL, // window menu handle 11 hInstance, // program instance handle 12 NULL) ; // creation parameters 13 14 ShowWindow (hwnd, iCmdShow) ; 15 UpdateWindow (hwnd) ;
创建子窗口和主窗口区别:
类设置比较:
- pfnWndProc字段设定为ChildWndProc [子窗口消息处理函数]。
- cbWndExtra字段设定为4字节,或者更确切地用sizeof (long)。该字段告诉Windows在其为依据此窗口类别的窗口保留的内部结构中,预留了4字节额外的空间。您能使用此空间来保存每个窗口的可能有所不同的信息。【窗口实例可用GetWindowLong(hwnd,0)获取其对应信息,第二个参数参照后文有给出】
- 因为像CHECKER3中的子窗口不需要图标,所以hIcon字段设定为NULL 。
- pszClassName字段设定为「Checker3_Child」,是类别的名称。
CreateWindow呼叫的参数间的比较:
参数 |
主窗口 |
子窗口 |
窗口类别 |
「Checker3」 |
「Checker3_Child」 |
窗口标题 |
「Checker3...」 |
NULL |
窗口样式 |
WS_OVERLAPPEDWINDOW |
WS_CHILDWINDOW | WS_VISIBLE |
水平位置 |
CW_USEDEFAULT |
0 |
垂直位置 |
CW_USEDEFAULT |
0 |
宽度 |
CW_USEDEFAULT |
0 |
高度 |
CW_USEDEFAULT |
0 |
父窗口句柄 |
NULL |
hwnd |
菜单句柄/子ID |
NULL |
(HMENU) (y << 8 | x) |
执行实体句柄 |
hInstance |
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) |
额外参数 |
NULL |
NULL |
窗口标题不需要时可为空,窗口样式 设有WS_VISIBLE则,不用调用ShowWindow函数,菜单句柄/子ID很有用(除了句柄外,唯一识别子窗口的ID)。
GetWindowLong 的第二个参数:
/** Window field offsets for GetWindowLong()*/ #define GWL_WNDPROC (-4) #define GWL_HINSTANCE (-6) #define GWL_HWNDPARENT (-8) #define GWL_STYLE (-16) #define GWL_EXSTYLE (-20) #define GWL_USERDATA (-21) #define GWL_ID (-12)
交互:
【从主窗口访问子窗口口】
//创建时取得句柄 hwndChild[x][y] = CreateWindow (szChildClass, NULL,WS_CHILDWINDOW | WS_VISIBLE,0, 0, 0, 0,
hwnd, (HMENU) (y << 8 | x), (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE), NULL) ;
//移动子窗口 MoveWindow ( hwndChild[x][y], x * cxBlock, y * cyBlock, cxBlock, cyBlock, TRUE) ;
//根据子ID 获得 对应句柄 SetFocus (GetDlgItem (hwnd, idFocus)) ; //SetFocus(HWND);
//根据子句柄 获得 对应ID id = GetWindowLong (hwndChild, GWL_ID) ;
id = GetDlgCtrlID (hwndChild) ;
【从子窗口获得父窗口信息】
//获得父窗口句柄 GetParent(hwnd);
//共享按键 鼠标消息 //在子窗口处理程序中, SendMessage (GetParent (hwnd), message, wParam, lParam) ;
子窗口控件:
1、按钮类别 button
发送消息:WM_COMMAND、WM_CTLCOLORBTN
参数wParamlParam:
LOWORD (wParam) HIWORD (wParam) lParam |
子窗口ID 通知码 Notify 子窗口句柄 |
按钮通知码标识符 |
值 |
BN_CLICKED |
0 |
BN_PAINT |
1 |
BN_HILITE or BN_PUSHED |
2 |
BN_UNHILITE or BN_UNPUSHED |
3 |
BN_DISABLE |
4 |
BN_DOUBLECLICKED or BN_DBLCLK |
5 |
BN_SETFOCUS |
6 |
BN_KILLFOCUS |
7 |
实际上,您不会看到这些按钮值中的大多数。
- 1到4的通知码是用于一种叫做BS_USERBUTTON的已不再使用的按钮的(它已经由BS_OWNERDRAW和另一种不同的通知方式所替换)。
- 6到7只有当按钮样式包括标识BS_NOTIFY才发送。
- 通知码5只对BS_RADIOBUTTON、BS_AUTORADIOBUTTON和BS_OWNERDRAW按钮发送,或者当按钮样式中包括BS_NOTIFY时,也为其它按钮发送。
父窗口消息处理程序也能向子窗口控件发送消息。这些消息包括以前缀WM开头的许多消息。另外,在WINUSER.H中还定义了8个按钮说明消息;前缀BM表示「按钮消息」。这些按钮消息如下表所示:
按钮消息 |
值 |
BM_GETCHECK |
0x00F0 |
BM_SETCHECK |
0x00F1 |
BM_GETSTATE |
0x00F2 |
BM_SETSTATE |
0x00F3 |
BM_SETSTYLE |
0x00F4 |
BM_CLICK |
0x00F5 |
BM_GETIMAGE |
0x00F6 |
BM_SETIMAGE |
0x00F7 |
不同的Button 类型:
BS_PUSHBUTTON, BS_DEFPUSHBUTTON, BS_CHECKBOX, BS_AUTOCHECKBOX, BS_RADIOBUTTON, BS_3STATE, BS_AUTO3STATE, BS_GROUPBOX, BS_AUTORADIOBUTTON, BS_OWNERDRAW
创建不同的Button:
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) ;
其效果示例图:
- 按键 BS_PUSHBUTTON, BS_DEFPUSHBUTTON
SendMessage (hwndButton, BM_SETSTATE, 1, 0) ; //按下 SendMessage (hwndButton, BM_SETSTATE, 0, 0) ; //松开
- 复选框 BS_CHECKBOX, BS_AUTOCHECK,BOXBS_3STATE, BS_AUTO3STATE
//设置反状态 SendMessage ((HWND) lParam, BM_SETCHECK, (WPARAM) !SendMessage ((HWND) lParam, BM_GETCHECK, 0, 0), 0);
SendMessage (hwndButton, BM_SETCHECK, 1, 0) ; //打钩 SendMessage (hwndButton, BM_SETCHECK, 0, 0) ; //取消打钩
//其余两种复选框样式是BS_3STATE和BS_AUTO3STATE,正如它们名字所暗示的,这两种样式能显示第三种状态-复选框内是灰色-它出现在向控件发送wParam等于2的WM_SETCHECK消息时。
//灰色是向使用者表示此框不能被选本章的或者禁止使用。
想知道按钮的状态,那么可以向它发送另一条BM_GETCHECK消息;您也可以将目前状态储存在您的窗口消息处理程序中的一个静态变量里。
- 单选按钮 BS_RADIOBUTTON, BS_AUTORADIOBUTTON
- 分组方块 BS_GROUPBOX
- 自定义按钮 BS_OWNERDRAW
/*------------------------------------------------------------------------ OWNDRAW.C -- Owner-Draw Button Demo Program (c) Charles Petzold, 1996 -------------------------------------------------------------------------*/ #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) ; HINSTANCE hInst ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("OwnDraw") ; MSG msg ; HWND hwnd ; WNDCLASS wndclass ; hInst = hInstance ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Owner-Draw Button Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; 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)) ; } 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){ 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, hInst, NULL) ; hwndLarger = CreateWindow (TEXT ("button"), TEXT (""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU) ID_LARGER, hInst, 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 window 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 ; break ; } MoveWindow ( hwnd, rc.left, rc.top, rc.right,rc.left,rc.bottom - rc.top, TRUE) ; return 0 ; case WM_DRAWITEM : pdis = (LPDRAWITEMSTRUCT) lParam ; // Fill area with white and frame it black 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) ; }
对 OWNERDRAW 示例的说明:
//对 OWNERDRAW 示例的说明 在处理WM_CREATE消息处理期间,OWNDRAW建立了两个BS_OWNERDRAW样式的按钮;按钮的宽度是系统字体的8倍,高度是系统字体的4倍(在使用预先定义好的位图绘制按钮时,这些尺寸在VGA上建立的按 钮为64图素宽64图素高,知道这些数据将非常有用)。这些按钮尚未就定位,在处理WM_SIZE消息处理期间,通过呼叫MoveWindow函数,OWNDRAW将按钮位置放在显示区域的中心。 按下这些按钮时,它们就会产生WM_COMMAND消息。为了处理这些WM_COMMAND消息,OWNDRAW呼叫GetWindowRect,将整个窗口(不只是显示区域)的位置和尺寸存放在RECT(矩形)结构中,这个位置是 相对于屏幕的。然后,根据按下的是左边还是右边的按钮,OWNDRAW调节这个矩形结构的各个字段值。程序再通过呼叫MoveWindow来重新确定位置和尺寸。这将产生另一个WM_SIZE消息,按钮被重新定位在 显示区域的中央。 如果这是程序所做的全部处理,那么这完全可以,只不过按钮是不可见的。使用BS_OWNERDRAW样式建立的按钮会在需要重新着色的任何时候都向它的父窗口发送一个WM_DRAWITEM消息。这出现在以下几种 情况中:当按钮被建立时,当按钮被按下或被放开时,当按钮得到或者失去输入焦点时,以及当按钮需要重新着色的任何时候。 在处理WM_DRAWITEM消息处理期间,lParam消息参数是指向型态DRAWITEMSTRUCT结构的指针,OWNDRAW程序将这个指针储存在pdis变量中,这个结构包含了画该按钮时程序所必需的消息(这个结构也可 以让自绘清单方块和菜单使用)。对按钮而言非常重要的结构字段有hDC (按钮的设备内容)、rcItem(提供按钮尺寸的RECT结构)、CtlID(控件窗口ID)和itemState (它说明按钮是否被按下,或 者按钮是否拥有输入焦点)。 呼叫FillRect用白色画刷抹掉按钮的内面,呼叫FrameRect在按钮的周围画上黑框,由此OWNDRAW便启动了WM_DRAWITEM处理过程。然后通过呼叫Polygon,OWNDRAW在按钮上画出4个黑色实心的三角形。 这是一般的情形。 如果按钮目前被按下,那么DRAWITEMSTRUCT的itemState字段中的某位将被设为1。您可以使用ODS_SELECTED常数来测试这些位。如果这些位被设立,那么OWNDRAW将通过呼叫InvertRect将按钮翻转 为相反的颜色。如果按钮拥有输入焦点,那么itemState的ODS_FOCUS位将被设立。在这种情况下,OWNDRAW通过呼叫DrawFocusRect,在按钮的边界内画一个虚线的矩形。 在使用拥有者绘制按钮时,应该注意以下几个方面:Windows获得设备内容并将其作为DRAWITEMSTRUCT结构的一个字段。保持设备内容处于您找到它时所处的状态,任何被选进设备内容的GDI对象都必需 被释放。另外,当心不要在定义按钮边界的矩形外面进行绘制。
改变按钮文字
SetWindowText (hwnd, pszString) ; iLength = GetWindowTextLength (hwnd) ; iLength = GetWindowText (hwnd, pszBuffer, iMaxLength) ; //iMaxLength指定复制到pszBuffer指向的缓冲区中的最大字符数。该函数传回复制的字符数。
可见的和启用的按钮
如果在建立子窗口时,您没有将WS_VISIBLE包含在窗口类别中,那么直到呼叫ShowWindow时子窗口才会被显示出来:
ShowWindow (hwndChild, SW_SHOWNORMAL) ;
如果您将WS_VISIBLE包含在窗口类别中,就没有必要呼叫ShowWindow。但是,您可以通过呼叫ShowWindow将子窗口隐藏起来:
ShowWindow (hwndChild, SW_HIDE) ;
您可以通过下面的呼叫来确定子窗口是否可见:
IsWindowVisible (hwndChild) ;
您也可以使子窗口被启用或者不被启用。在内定情况下,窗口是被启用的。您可以通过下面的呼叫使窗口不被启用:
EnableWindow (hwndChild, FALSE) ;
对于按钮控件,这具有使按钮字符串变成灰色的作用。按钮将不再对鼠标输入和键盘输入做出响应,这是表示按钮选项目前不可用的最好方法。
您可以通过下面的呼叫使子窗口再次被启用:
EnableWindow (hwndChild, TRUE) ;
您还可以使用下面的呼叫来确定子窗口是否被启用:
IsWindowEnabled (hwndChild) ;
按钮和输入焦点
当Windows将输入焦点从一个窗口(例如一个父窗口)转换到另一个窗口(例如一个子窗口控件)时,它首先给正在失去输入焦点的窗口发送一个WM_KILLFOCUS消息,
wParam参数是接收输入焦点的窗口的句柄。然后,Windows向正在接收输入焦点的窗口发送一个WM_SETFOCUS消息,同时wParam是还在失去输入焦点的窗口的句柄
(在这两种情况中,wParam值可能为NULL,它表示没有窗口拥有或者正在接收输入焦点)。
例子:
//阻止子窗口获得焦点 case WM_KILLFOCUS : if (hwnd == GetParent ((HWND) wParam)) SetFocus (hwnd) ; return 0 ;
Windows保留了29种系统颜色以供各种显示使用。您可以使用GetSysColor和SetSysColors来获得和设定这些颜色。
GetSysColor和SetSysColors |
系统登录键或WIN.INI标识符 |
内定的RGB值 |
COLOR_SCROLLBAR |
Scrollbar |
C0-C0-C0 |
COLOR_BACKGROUND |
Background |
00-80-80 |
COLOR_ACTIVECAPTION |
ActiveTitle |
00-00-80 |
COLOR_INACTIVECAPTION |
InactiveTitle |
80-80-80 |
COLOR_MENU |
Menu |
C0-C0-C0 |
COLOR_WINDOW |
Window |
FF-FF-FF |
COLOR_WINDOWFRAME |
WindowFrame |
00-00-00 |
COLOR_MENUTEXT |
MenuText |
C0-C0-C0 |
COLOR_WINDOWTEXT |
WindowText |
00-00-00 |
COLOR_CAPTIONTEXT |
TitleText |
FF-FF-FF |
COLOR_ACTIVEBORDER |
ActiveBorder |
C0-C0-C0 |
COLOR_INACTIVEBORDER |
InactiveBorder |
C0-C0-C0 |
COLOR_APPWORKSPACE |
AppWorkspace |
80-80-80 |
COLOR_HIGHLIGHT |
Highlight |
00-00-80 |
COLOR_HIGHLIGHTTEXT |
HighlightText |
FF-FF-FF |
COLOR_BTNFACE |
ButtonFace |
C0-C0-C0 |
COLOR_BTNSHADOW |
ButtonShadow |
80-80-80 |
COLOR_GRAYTEXT |
GrayText |
80-80-80 |
COLOR_BTNTEXT |
ButtonText |
00-00-00 |
COLOR_INACTIVECAPTIONTEXT |
InactiveTitleText |
C0-C0-C0 |
COLOR_BTNHIGHLIGHT |
ButtonHighlight |
FF-FF-FF |
COLOR_3DDKSHADOW |
ButtonDkShadow |
00-00-00 |
COLOR_3DLIGHT |
ButtonLight |
C0-C0-C0 |
COLOR_INFOTEXT |
InfoText |
00-00-00 |
COLOR_INFOBK |
InfoWindow |
FF-FF-FF |
[no identifier; use value 25] |
ButtonAlternateFace |
B8-B4-B8 |
COLOR_HOTLIGHT |
HotTrackingColor |
00-00-FF |
COLOR_GRADIENTACTIVECAPTION |
GradientActiveTitle |
00-00-80 |
COLOR_GRADIENTINACTIVECAPTION |
GradientInactiveTitle |
80-80-80 |
按钮颜色
对需要多种颜色的每一个按钮来说,这个问题更加地明显。COLOR_BTNFACE被用于按键主要的表面颜色,以及其它按钮主要的背景颜色(这也是用于对话框和消息框的系统颜色)。COLOR_BTNSHADOW被建议用作按键右下边、以及复选框内部和单选按钮圆点的阴影。对于按键,COLOR_BTNTEXT被用作文字颜色;而对于其它的按钮,则使用COLOR_WINDOWTEXT作为文字颜色。还有其它几种系统颜色用于按钮设计的各个部分。
因此,如果您想在我们的显示区域表面显示按钮,那么一种避免颜色冲突的方法便是屈服于这些系统颜色。首先,在定义窗口类别时使用COLOR_BTNFACE作为您显示区域的背景颜色:
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
您可以在BTNLOOK程序中尝试这种方法。当WNDCLASS结构中的hbrBackground值是这个值时,Windows会明白这实际上指的是一种系统颜色而非一个实际的句柄。Windows要求当您在WNDCLASS结构的hbrBackground栏中指定这些标识符时加上1,这样做的目的是防止其值为NULL,而没有任何其它目的。如果您的在程序执行过程中,系统颜色恰好发生了变化,那么显示区域将变得无效,而Windows将使用新的COLOR_BTNFACE值。但是现在我们又引发了另一个问题。当您使用TextOut显示文字时,Windows使用的是在设备内容中为背景颜色(它擦除文字后的背景)和文字颜色定义的值,其默认值为白色(背景)和黑色(文字),而不管系统颜色和窗口类别结构中的hbrBackground字段为何值。所以,您需要使用SetTextColor和SetBkColor将文字和文字背景的颜色改变为系统颜色。您可以在获得设备内容句柄之后这么做:
SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ; SetTextColor (hdc, GetSysColor (COLOR_WINDOWTEXT)) ;
这样,显示区域背景、文字背景和文字的颜色都与按钮的颜色一致了。但是,如果当您的程序执行时,使用者改变了系统颜色,您可能要改变文字背景颜色和文字颜色。这时您可以使用下面的程序代码:
case WM_SYSCOLORCHANGE: InvalidateRect (hwnd, NULL, TRUE) ; break ;
WM_CTLCOLORBTN消息
当子窗口即将为其显示区域着色时,由按钮控件发送给其父窗口消息处理程序的一个消息。父窗口可以利用这个机会来改变子窗口消息处理程序将用来着色的颜色(在Windows的16位版本中,一个称为WM_CTLCOLOR的消息被用于所有的控件,现在针对每种型态的标准控件,分别代之以不同的消息)。
当父窗口消息处理程序收到WM_CTLCOLORBTN消息时,wParam消息参数是按钮的设备内容句柄,lParam是按钮的窗口句柄。当父窗口消息处理程序得到这个消息时,按钮控件已经获得了它的设备内容。当您的窗口消息处理程序处理一个WM_CTLCOLORBTN消息时,您必须完成以下三个动作:
- 使用SetTextColor选择设定一种文字颜色
- 使用SetBkColor选择设定一种文字背景颜色
- 将一个画刷句柄传回给子窗口
理论上,子窗口使用该画刷来着色背景。当不再需要这个画刷时,您应该负责清除它。
下面是使用WM_CTLCOLORBTN的问题所在:只有按键和拥有者绘制按钮才给其父窗口发送WM_CTLCOLORBTN,而只有拥有者绘制按钮才会响应父窗口消息处理程序对消息的处理,而使用画刷来着色背景。这基本上是没有意义的,因为无论怎样都是由父窗口来负责绘制拥有者绘制按钮。
在本章后面,我们将说明,在某些情况下,一些类似于WM_CTLCOLORBTN但适用于其它型态控件的消息将更为有用。
:WM_CTLCOLORSCROLLBAR :如果您拦截了WM_CTLCOLORSCROLLBAR消息,那么可以在消息处理中传回画刷以取代该颜色。
:WM_CTLCOLORSTATIC
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 ; }
2、静态类别 static
发送消息:不发送
类别种类:创建时使用:
SS_BLACKRECT SS_GRAYRECT SS_WHITERECT |
SS_BLACKFRAME SS_GRAYFRAME SS_WHITEFRAME |
「BLACK」、「GRAY」、「WHITE」并不意味着黑、灰和白色,这些颜色是由系统颜色决定的,如表所示。
静态控件 |
系统颜色 |
BLACK |
COLOR_3DDKSHADOW |
GRAY |
COLOR_BTNSHADOW |
WHITE |
COLOR_BTNHIGHLIGHT |
设置文字内容、颜色 参考按钮。
3、滚动条类别 scrollbar
发送消息:不向父窗口发送WM_COMMAND消息,而是像窗口滚动条那样发送WM_VSCROLL和WM_HSCROLL消息。通过lParam参数来区分窗口滚动条与滚动条控件。
wParam参数:同窗口类别
lParam参数:子窗口的 窗口句柄
创建函数示例:
hwndScroll[i] = CreateWindow (TEXT ("scrollbar"), NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT, 0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ;
如果您想建立与窗口滚动条尺寸相同的滚动条控件,那么可以使用GetSystemMetrics取得水平滚动条的高度:
GetSystemMetrics (SM_CYHSCROLL) ;
或者垂直滚动条的宽度:
GetSystemMetrics (SM_CXVSCROLL) ;
//创建三个滚动条,对应红绿蓝三种原色,并通过改变窗口背景颜色来显示颜色组合效果 //实现子窗口的切换(窗口子类别化(Window Subclassing)) /*------------------------------------------------------------------------ COLORS1.C -- Colors Using Scroll Bars (c) Charles Petzold, 1998 -------------------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 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 ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } 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) { 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", and "Blue". hwndLabel [i] = CreateWindow (TEXT ("static"), zColorLabel[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) ; 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) ; }
//实现与上面例子类似功能,不包括滚动条切换 //显示颜色组合是直接绘画相应颜色的矩形来实现,而不是改变背景颜色 /***************************************** by BensonLaur 2016.4.5 *****************************************/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("The Hello Program"), // 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 GCL_WNDPROC int cxScreen, cyScreen ; cxScreen = GetSystemMetrics(SM_CXSCREEN); cyScreen = GetSystemMetrics(SM_CYSCREEN); MoveWindow(hwnd,2*cxScreen/7,cyScreen/6,2*cxScreen/7,2*cyScreen/3,FALSE); ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; static COLORREF crPrim[3] = { RGB (255, 0, 0), RGB (0, 255, 0),RGB (0, 0, 255) } ; static HBRUSH hBrush[3], hBrushStatic,hBrushRect ; static HWND hwndScroll[3], hwndLabel[3], hwndValue[3], hwndRect ; static int color[3], cxChar,cyChar ; static RECT rcColor ; static TCHAR *szColorLabel[] = { TEXT ("Red"), TEXT ("Green"), TEXT ("Blue") } ; HINSTANCE hInstance ; static int i, cxClient, cyClient ; TCHAR szBuffer[64] ; switch (message) { case WM_CREATE: hInstance = (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE); //创建显示颜色的矩形窗口控件,ID 为9 hwndRect = CreateWindow(TEXT("static"),NULL,SS_WHITERECT|WS_VISIBLE|WS_CHILD, 0,0,0,0, hwnd,(HMENU)9,hInstance,NULL); //创建三个滚动条窗口控件 ID 为 0,1,2 for(i=0;i<3;i++) { hwndScroll[i] = CreateWindow(TEXT("scrollBar"), NULL, WS_CHILD | WS_VISIBLE |WS_TABSTOP | SBS_HORZ, 0, 0, 0, 0, hwnd, (HMENU) i, hInstance, NULL) ; SetScrollRange(hwndScroll[i],SB_CTL,0,255,FALSE); SetScrollPos(hwnd,SB_CTL,0,FALSE); hBrush[i] = CreateSolidBrush(crPrim[i]); } //创建三个静态文本控件,显示“Red”,“Green”,和“Blue”,ID 为 3,4,5 for(i = 0 ;i<3;i++) { hwndLabel[i] = CreateWindow(TEXT("static"),szColorLabel[i], WS_CHILD|WS_VISIBLE |SS_CENTER, 0,0,0,0,hwnd,(HMENU)(i+3),hInstance,NULL); } //创建三个静态文本控件, 显示用于各个scroll的值,ID 为 6,7,8 for(i = 0 ;i<3;i++) { hwndValue[i] = CreateWindow(TEXT("static"),TEXT("0"), WS_CHILD|WS_VISIBLE |SS_CENTER, 0,0,0,0,hwnd,(HMENU)(i+6),hInstance,NULL); } hBrushStatic = CreateSolidBrush(GetSysColor (COLOR_BTNHIGHLIGHT)); 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) // 6个 { SetTextColor ((HDC) wParam, crPrim[i % 3]) ; SetBkColor ((HDC) wParam, GetSysColor (COLOR_BTNHIGHLIGHT)); return (LRESULT)hBrushStatic ; } break; case WM_SIZE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; MoveWindow(hwndRect,0,0,cxClient,cyClient/2,FALSE); for(i =0 ;i < 3 ;i++) { MoveWindow(hwndLabel[i],cxClient/2-cxClient/3-cxChar*7, cyClient/12+i*cyClient/12, cxChar*6, cyChar,FALSE); MoveWindow(hwndScroll[i],cxClient/2-cxClient/3 ,cyClient/12+i*cyClient/12 ,cxClient*2/3, cyClient/24,FALSE); MoveWindow(hwndValue[i],cxClient/2+cxClient/3+cxChar, cyClient/12+i*cyClient/12 ,cxChar*4 ,cyChar,FALSE); } SetRect(&rect,0,cyClient/2,cxClient,cyClient); return 0; case WM_HSCROLL: i=GetWindowLong((HWND)lParam,GWL_ID); switch(LOWORD(wParam)) { case SB_LINELEFT: color[i] -=1; break; case SB_LINERIGHT: color[i] +=1; break; case SB_PAGELEFT: color[i] -=15; break; case SB_PAGERIGHT: color[i] +=15; break; case SB_TOP: color[i] =0; break; case SB_BOTTOM: color[i] =255; break; case SB_THUMBTRACK: color[i] = HIWORD (wParam); break; } color[i] = max(0,min(255,color[i])); SetScrollPos (hwndScroll[i], SB_CTL, color[i], TRUE) ; wsprintf (szBuffer, TEXT ("%i"), color[i]) ; SetWindowText (hwndValue[i], szBuffer) ; hdc = GetDC((HWND)GetWindowLong((HWND)lParam,GWL_HWNDPARENT)); DeleteObject(SelectObject(hdc,CreateSolidBrush(RGB(color[0],color[1],color[2])))); Rectangle(hdc,0,cyClient/2,cxClient,cyClient); return 0; case WM_PAINT: hdc = BeginPaint(hwnd,&ps); DeleteObject(SelectObject(hdc,CreateSolidBrush(RGB(color[0],color[1],color[2])))); Rectangle(hdc,0,cyClient/2,cxClient,cyClient); EndPaint(hwnd,&ps); return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
4、编辑框类别edit
发送消息:WM_COMMAND
wParam和lParam变量的含义是与按钮相同:
LOWORD (wParam) HIWORD (wParam) lParam |
子窗口ID 通知码 子窗口句柄 |
通知码如下所示:
EN_SETFOCUS EN_KILLFOCUS EN_CHANGE EN_UPDATE EN_ERRSPACE EN_MAXTEXT EN_HSCROLL EN_VSCROLL |
编辑控件已经获得输入焦点 编辑控件已经失去输入焦点 编辑控件的内容将改变 编辑控件的内容已经改变 编辑控件执行已经超出中间 编辑控件在插入时执行超出空间 编辑控件的水平滚动条已经被按下 编辑控件的垂直滚动条已经被按下 |
向父窗口发送COMMAND 中的通知码为 EN_UPDATE 产生之后,接着会依次产生 通知码为 向父窗口发送WM_CTLCOLOREDIT 和 EN_CHANGE 的COMMAND。
程序示例:
/*--------------------------------------------------------------------------- POPPAD1.C -- Popup Editor using child window edit box (c) Charles Petzold, 1998 ---------------------------------------------------------------------------*/ #include <windows.h> #define ID_EDIT 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); TCHAR szAppName[] = TEXT ("PopPad1") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) { static HWND hwndEdit ; switch (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 space."), szAppName, MB_OK | MB_ICONSTOP) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
在POPPAD1建立其编辑控件时,CreateWindow呼叫依如下形式给出样式:
WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |WS_BORDER | ES_LEFT | ES_MULTILINE |ES_AUTOHSCROLL | ES_AUTOVSCROLL
发送给编辑控件的消息
这些消息允许您剪下、复制或者清除目前被选择的文字。使用者使用鼠标或者Shift键加上光标控件键来选择文字并进行上面的操作,这样,在编辑控件中选中的文字将被加亮:
SendMessage (hwndEdit, WM_CUT, 0, 0) ; SendMessage (hwndEdit, WM_COPY, 0, 0) ; SendMessage (hwndEdit, WM_CLEAR, 0, 0) ;
您也可以将剪贴簿上的文字插入到编辑控件中的光标位置:
SendMessage (hwndEdit, WM_PASTE, 0, 0) ;
您可以取得目前选择的起始位置和末尾位置:
SendMessage (hwndEdit, EM_GETSEL, (WPARAM) &iStart,(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) ;
行数从0开始计算,iLine值为-1时传回包含游标所在行的偏移量。您可以取得行的长度:
iLength = SendMessage (hwndEdit, EM_LINELENGTH, iLine, 0) ;
并将行本身复制到一个缓冲区中:
iLength = SendMessage (hwndEdit, EM_GETLINE, iLine, (LPARAM) szBuffer) ;
5、清单方块 listbox
发送消息:
当您使用CreateWindow建立清单方块子窗口时,您应该将「listbox」作为窗口类别,将WS_CHILD作为窗口样式。
但是,这个内定清单方块样式不向其父窗口发送WM_COMMAND消息,这样一来,程序必须向清单方块询问其中的项目的选择状态(借助于发送给清单方块控件的消息)。
所以,清单方块控件通常都包括清单方块样式标识符LBS_NOTIFY,它允许父窗口接收来自清单方块的WM_COMMAND消息。
如果您希望清单方块控件对清单方块中的项目进行排序,那么您可以使用另一种常用的样式LBS_SORT。
Windows表头文件定义了一个清单方块样式,叫做LBS_STANDARD,它包含了最常用的样式,其定义如下:(LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
参数wParam和参数lParam: 同按钮和编辑控件。
LOWORD (wParam) |
子窗口ID |
HIWORD (wParam) |
通知码 |
lParam |
子窗口句柄 |
通知码及其值如下所示:
LBN_ERRSPACE |
-2 |
LBN_SELCHANGE |
1 |
LBN_DBLCLK |
2 |
LBN_SELCANCEL |
3 |
LBN_SETFOCUS |
4 |
LBN_KILLFOCUS |
5 |
将字符串放入清单方块
建立清单方块之后,下一步是将字符串放入其中,您可以通过呼叫SendMessage为清单方块窗口消息处理程序发送消息来做到这一点。字符串通常通过以0开始计数的索引数来引用,其中0对应于最顶上的项目。在下面的例子中,hwndList是子窗口清单方块控件的句柄,而iIndex是索引值。在使用SendMessage传递字符串的情况下,lParam参数是指向以null字符结尾字符串的指针。
在大多数例子中,当窗口消息处理程序储存的清单方块内容超过了可用内存空间时,SendMessage将传回LB_ERRSPACE(定义为-2)。如果是因为其它原因而出错,那么SendMessage将传回LB_ERR(-1)。如果操作成功,那么SendMessage将传回LB_OKAY(0)。您可以通过测试SendMessage的非零值来判断这两种错误。
如果您采用LBS_SORT样式(或者如果您在清单方块中按照想要呈现的顺序排列字符串),那么填入清单方块最简单的方法是借助LB_ADDSTRING消息:
SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) szString) ;
如果您没有采用LBS_SORT,那么可以使用LB_INSERTSTRING指定一个索引值,将字符串插入到清单方块中:
SendMessage (hwndList, LB_INSERTSTRING, iIndex, (LPARAM) szString) ;
例如,如果iIndex等于4,那么szString将变为索引值为4的字符串-从顶头开始算起的第5个字符串(因为是从0开始计数的),位于这个点后面的所有字符串都将向后推移。索引值为-1时,将字符串增加在最后。您可以对样式为LBS_SORT的清单方块使用LB_INSERTSTRING,但是这个清单方块的内容不能被重新排序(您也可以使用LB_DIR消息将字符串插入到清单方块中,这将在本章的最后进行讨论)。
您可以在指定索引值的同时使用LB_DELETESTRING参数,这就可以从清单方块中删除字符串:
SendMessage (hwndList, LB_DELETESTRING, iIndex, 0) ;
您可以使用LB_RESETCONTENT清除清单方块中的内容:
SendMessage (hwndList, LB_RESETCONTENT, 0, 0) ;
当在清单方块中增加或者删除字符串时,清单方块窗口消息处理程序将更新显示。如果您有许多字符串需要增加或者删除,那么您也许希望暂时阻止这一动作,其方法是关掉控件的重画旗标:
SendMessage (hwndList, WM_SETREDRAW, FALSE, 0) ;
当您完成后,可以再打开重画旗标:
SendMessage (hwndList, WM_SETREDRAW, TRUE, 0) ;
使用LBS_NOREDRAW样式建立的清单方块开始时其重画旗标是关闭的。
选择和取得项
SendMessage完成了下面所描述的任务之后,通常传回一个值。如果出错,那么这个值将被设定为LB_ERR(定义为-1)。
当清单方块中放入一些项目之后,您可以弄清楚清单方块中有多少项目:
iCount = SendMessage (hwndList, LB_GETCOUNT, 0, 0) ;
其它一些呼叫对单项选择清单方块和多项选择清单方块是不同的。让我们先来看看单项选择清单方块。
通常,您让使用者在清单方块中选择条目。但是如果您想加亮显示一个内定选择,则可以使用:
SendMessage (hwndList, LB_SETCURSEL, iIndex, 0) ;
将iParam设定为-1则取消所有选择。
您也可以根据项目的第一个字母来选择:
iIndex = SendMessage (hwndList, LB_SELECTSTRING, iIndex, (LPARAM) szSearchString) ;
在SendMessage呼叫中将iIndex作为iParam参数时,iIndex是索引,可以根据它搜索其开头字符与szSearchString相匹配的项目。iIndex的值等于-1时从头开始搜索,SendMessage传回被选中项目的索引。如果没有开头字符与szSearchString相匹配的项目时,SendMessage传回LB_ERR。
当您得到来自清单方块的WM_COMMAND消息时(或者在任何其它时候),您可以使用LB_GETCURSEL来确定目前选项的索引:
iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ;
如果没有项目被选中,那么从呼叫中传回的iIndex值为LB_ERR。
您可以确定清单方块中字符串的长度:
iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) ;
并可以将某项目复制到文字缓冲区中:
iLength = SendMessage ( hwndList, LB_GETTEXT, iIndex, (LPARAM) szBuffer) ;
在这两种情况下,从呼叫传回的iLength值是字符串的长度。对以NULL字符终结的字符串长度来说,szBuffer数组必须够大。您也许想用LB_GETTEXTLEN先分配一些局部内存来存放字符串。
对于一个多项选择清单方块,您不能使用LB_SETCURSEL、LB_GETCURSEL或者LB_SELECTSTRING,但是您可以使用LB_SETSEL来设定某特定项目的选择状态,而不影响有可能被选择的其它项:
SendMessage (hwndList, LB_SETSEL, wParam, iIndex) ;
wParam参数不为0时,选择并加亮某一项目;wParam为0时,取消选择。如果wParam等于-1,那么将选择所有项目或者取消所有被选中的项目。您可以如下确定某特定项目的选择状态:
iSelect = SendMessage (hwndList, LB_GETSEL, iIndex, 0) ;
其中,如果由iIndex指定的项目被选中,iSelect被设为非0,否则被设为0。
使用示例:
/*------------------------------------------------------------------------- ENVIRON.C -- Environment List Box (c) Charles Petzold, 1998 ---------------------------------------------------------------------------*/ #include <windows.h> #define ID_LIST 1 #define ID_TEXT 2 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Environ") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Environment List Box"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } void FillListBox (HWND hwndList) { int iLength ; TCHAR * pVarBlock, * pVarBeg, * pVarEnd, * pVarName ; pVarBlock = GetEnvironmentStrings () ; // Get pointer to environment block while (*pVarBlock) { if (*pVarBlock != '=') // Skip variable names beginning with '=' { pVarBeg = pVarBlock ; // Beginning of variable name while (*pVarBlock++ != '=') ; // 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 = calloc (iLength + 1, sizeof (TCHAR)) ; CopyMemory (pVarName, pVarBeg, iLength * sizeof (TCHAR)) ; pVarName[iLength] = '