(1)子窗口控件:①子窗口,其parent为父窗口句柄;②子窗口状态发生变化时,会处理鼠标和键盘消息,并且通知其父窗口。可分为自定义子窗口控件和标准的子窗口控件(如按钮)
(2)子窗口控件的使用场合
①在对话框里使用最广——有内在机制支持Tab和光标移动键来转移焦点。
②在窗口表面直接使用:没内在机制支持Tab键和光标移动键来移动焦点到另一个控件;
对于自定义的控件,当单击子窗口时,父窗口会得到焦点。但对于标准子窗口控件,单击时会自动获得焦点(估计子窗口过程内部在WM_LBUTTONDOWN中实现了SetFocus(hwnd)了)。不管是哪类子窗口,都可以得到输入焦点,但一旦得到焦点,它自己无法把输入焦点交回给其父窗口。(可以通过窗口子类化来达到目的)。
(3)子窗口的销毁:在父窗口销毁的同时,会自动销毁子窗口,程序不必自己处理。
9.1 按钮类
9.1.1 创建子窗口:CreateWindow各参数设置
| 
 参数  | 
 值  | 
 备注  | 
| 
 lpClassName  | 
 Text(“button”)  | 
 预定义的名称,不可更改  | 
| 
 lpWindowName  | 
 button[i].szText  | 
 按钮上显示的文本  | 
| 
 dwStyle  | 
 WS_CHILE|WS_VISIBLE|button[i].style  | 
 
  | 
| 
 x  | 
 cxChar  | 
 相对于父窗口客户区左上角  | 
| 
 y  | 
 cyChar*(1+2*i)  | 
 相对于父窗口客户区左上角  | 
| 
 nWidth  | 
 20*xChar  | 
 
  | 
| 
 nHeight  | 
 7*yChar/4  | 
 
  | 
| 
 hWndParent  | 
 Hwnd  | 
 
  | 
| 
 hMenu  | 
 (HMENU)i  | 
 子窗口ID,每个窗口唯一  | 
| 
 hInstance  | 
 ((LPCREATESTRUCT)lParam)->hInstance  | 
 WM_CREATE的lParam指向一个CREATESTRUCT结构,可从中获得hInstance句柄  | 
| 
 lpParam  | 
 NULL  | 
 额外参数  | 
★获取hInstance的4种方法:
①设置全局变量hInst,在WinMain函数中 hInst = hInstance;
②hInstance =GetWindowLong(hwnd,GWL_INSTANCE);
③在WM_CREATE消息中从lParam中获取:hInstance = ((LPCREATESTRUCT)lParam)->hInstance
④hInstance =GetModuleHandle(NULL);
9.1.2 子窗口传递信息给父窗口
(1)子窗口过程获取父窗口句柄的方法:hwndParent =GetParent(hwnd);
(2)子窗口向父窗口过程发送消息:SendMessage(hwndParent,message,wParam,lParam);
★注意发送自定义消息时:message可以是大于等于WM_USER的任何值。
wParam:可设为子窗口ID。
lParam:可设置额外的信息。
(3)单击按钮,子窗口会向父窗口发送WM_COMMAND消息。各参数如下
| 
 参数  | 
 值  | 
 备注  | 
|
| 
 lParam  | 
 子窗口句柄  | 
 
  | 
|
| 
 wParam  | 
 LOWORD(wParam) :子窗口ID  | 
 
  | 
|
| 
 HIWORD(wParam):通知码 
 通知码以BN_开头,表示消息传输的方向是给父窗口的通知消息。  | 
 BN_CLICKED (0)  | 
 释放鼠标时发生  | 
|
| 
 BN_PAINT (1)  | 
 为过时的按钮样式BS_USERBUTTON所用(己被BS_OWNERDRAW和另一套通知机制取代)  | 
||
| 
 BN_HILITE或BN_PUSHED (2)  | 
|||
| 
 BN_UNHILITE或BN_UNPUSHED (3)  | 
|||
| 
 BN_DISABLE (4)  | 
|||
| 
 BN_DOUBLECLICKED或BN_DBLCLK (5)  | 
 仅用于BS_RADIOBUTTON、BS_AUTORADIOBUTTON和BS_OWNERDRAW或BS_NOTIFY的按钮使用  | 
||
| 
 BN_SETFOCUS (6)  | 
 仅用于BS_NOTIFY样式的按钮  | 
||
| 
 BN_KILLFOCUS (7)  | 
|||
9.1.3 父窗口传递信息给子窗口:按钮消息以BM_开头(不是WM_开头的)
(1)获取子窗口ID或子窗口句柄或方法
①id = GetWindowLong(hwndChild,GWL_ID)或 id =GetDlgCtrlID(hwndChild)
②hwndChild = GetDlgItem(hwndParent,id);
(2)SentMessage的message(消息)设置
| 
 按钮消息  | 
 备注  | 
| 
 BM_GETCHECK (0x00F0)  | 
 单击或复选框按钮  | 
| 
 BM_SETCHECK (0x00F1)  | 
|
| 
 BM_GETSTATE (0x00F2)  | 
 返回 BST_CHECKED:被单击 BST_FOCUS:具有输入焦点 BST_PUSHED:按住鼠标 ……  | 
| 
 BM_SETSTATE (0x00F3)  | 
|
| 
 BM_CLICK (0x00F4)  | 
 模拟按钮单击  | 
| 
 BM_GETIMAGE (0x00F5)  | 
 
  | 
| 
 BM_SETIMAGE (0x00F6)  | 
 
  | 
9.1.4 按钮
(1)按键按钮的最佳视觉高度:字符高度的7/4。宽度:文本宽度 + 2个字符宽度。
(2)模拟按键按钮的状态
①按住状态:SendMessage(hwndButton,BM_SETSTATE,1,0);
②正常状态:SendMessage(hwndButton,BM_SETSTATE,0,0);
9.1.5 复选框
| 
 样式  | 
 状态  | 
| 
 BS_CHECKBOX  | 
 1、SendMessage(hwndButton,BM_SETCHECK,1,0); //选中 2、SendMessage(hwndButton,BM_SETCHECK,0,0); //取消选中 3、SendMessage((HWND)lParam,BM_SETCHECK,(WPARAM) //切换 !SendMessage((HWND)lParam,BM_GETCHECK,0,0),0);  | 
| 
 BS_AUTOCHECKBOX  | 
 1、自动切换 2、获取状态:iCheck = (int)SendMessage(hwndButton,BM_GETCHECK,0,0);  | 
| 
 BS_3STATE  | 
 SendMessage(hwndButton,BM_SETCHECK,2,0); //第3种状态  | 
| 
 BS_AUTO3STATE  | 
 //自动切换  | 
(1)宽度和高度:最低高度——1个字符的高度,最小宽度——字符数+2个字符的宽度。
(2)复选框中的文本位置及文本对齐
 
9.1.6 单选按钮——第二次单击时,不会状态切换,其状态保持不变
(1)BS_AUTORADIOBUTTON:
(2)BS_RADIOBUTTON:父窗口收到WM_COMMAND消息时,可以
选中单选按钮:SendMessage(hwndButton,BM_SETCHECK,1,0);
取消选中: SendMessage(hwndButton,BM_SETCHECK,0,0);
9.1.7 组合框(Group Boxes)——不发送WM_COMMAND消息,也不接收鼠标和键盘消息。
9.1.8 改变按钮文本
(1)设置文本:SetWindowText(hwnd,pszString);
(2)获取文本: iLength = GetWindowTextLength(hwnd);//先获取文本长度
GetWindowText(hwnd,pszBuffer,iLength);//获取文本到指定缓冲区
 9.1.9 可见的按钮和启用的按钮
(1)显示子窗口:ShowWindow(hwndChild,SW_SHOWNORMAL); //窗口类没WS_VISIBLE时不显示。
(2)判断窗口可见性:IsWindowVisible(hwndChild);
(3)启用或禁用:EnableWindow(hwndChild,TRUE)或EnableWindow(hwndChild,FALSE);
(4)子窗口是否被启动:IsWindowEnabled(hwndChild);
9.1.10 按钮或输入焦点
(1)单击鼠标时,按钮会自动得到焦点,键盘消息发往子窗口(即按钮的内部窗口过程)
①子窗口只对空格键做出响应(类似鼠标单击)
②父窗口失去对键盘处理的控制——解决方法就是窗口子类化
(2)当焦点从父窗口向子窗口转移时的消息顺序
①WM_KILLFOCUS发往父窗口,wParam为要获得焦口的窗口句柄。
②WM_SETFOCUS发往子窗口,wParam为失去焦点的窗口句柄。
(3)阻止子窗口获得输入焦点
| 
 方法1  | 
 方法2  | 
| 
 case WM_KILLFOCUS: //NUM为子窗口的数目 for (int i = 0; i < NUM;i++) { //wParam为要获取焦点的窗口句柄 if (hwndChild[i] == (HWND)wParam) { SetFocus(hwnd); break; } } return 0;  | 
 case WM_KILLFOCUS: //要获得焦点的是子窗口,则抢回焦点 if (hwnd == GetParent((HWND)wParam)) SetFocus(hwnd);//父窗口抢回焦点 
 return 0;  | 
【BtnLook程序】
效果图:
 
/*------------------------------------------------------------ BTNLOOK.C -- Button Look Program (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> struct { int iStyle; //按钮样式 TCHAR* szText; //显示文本 } button[] = { BS_DEFPUSHBUTTON, 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("AutoRadioButton"), BS_OWNERDRAW, TEXT("OwnerDraw") }; #define NUM (sizeof(button)/sizeof(button[0])) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("BtnLook"); 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("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); 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; static HWND hwndButton[NUM]; static int cxChar, cyChar; static TCHAR szTop[] = TEXT("message wParam lParam"), szUnd[] = TEXT("_______ ______ ______"), szFormat[] = TEXT("%-16s%04X-%04X %04X-%04X"), szBuffer[50]; static RECT rect; switch (message) { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits());//对话框基本单位, cyChar = HIWORD(GetDialogBaseUnits());//为系统字体字符的平均宽度和高度 //创建NUM个按钮 for (int i = 0; i < NUM; i++) { hwndButton[i] = CreateWindow( TEXT("button"), button[i].szText, WS_VISIBLE | WS_CHILD | button[i].iStyle, cxChar, cyChar*(1 + 2 * i), //每个按钮高度为7/4*cyChar,加1/4间隔,得2*cyChar 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_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); //因为ScrollWindow会将生无效区, //而ValidateRect会将无效区有效化,阻止发送WM_PAINT消息。 //如果注释掉ValidateRect,将引起不断的重绘。 return 0; case WM_PAINT: /* 1、窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前从屏幕底部 (假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的排序,这个排序不关注父窗口还 是子窗口。当任意一个窗口接受到WM_PAINT消息产生重绘,更新区绘制完成以后,就搜索它的前面 的一个窗口,如果此窗口的范围和更新区有交集,就向这个发送wm_paint消息,周而复始,直到执 行到顶层窗口才算完成。 2、主窗口接受WM_PAINT绘制完成后,会引起更新区上所有子窗口的重绘(所有子窗口也是从底到外排序的) 3、子窗口无效不会引起父窗口重绘。父窗口无效,如果父窗口(没有WS_CLIPCHILDREN属性)收到WM_PAINT, 则所有子窗口都会在父窗口处理WM_PAINT之后收到WM_PAINT重绘消息。当然,如果父窗口带有属性 WS_CLIPCHILDREN,则不会引起子窗口重绘。 */ //以下将客户区刷白,因子窗口范围与无效区有交集,会导致子窗口重绘,因此本例中WM_DRAWITEM会被触发, //而WM_DRAWITEM过程中的SrollWindow会引起WM_PAINT,可在WM_DRAWITEM里,将无效区有效化,阻止 //ScrollWindow引发的WM_PAINT,从而防止死循环。 InvalidateRect(hwnd, NULL, TRUE);//因为在WM_COMMAND过程中绘制的内容,在WM_PAINT中无法重绘出来, //为防止主窗口被遮挡,再移开时,出现WM_COMMMAND中会制的无法重现, //最省事的做法是,那些内容全部不要了。 hdc = BeginPaint(hwnd, &ps); //将无效区有效化,阻止重复发送WM_PAINT消息。 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_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
9.2 控件和颜色
(1)29种系统颜色:GetSysColor(SetSysColor)
| 
 常量索引值  | 
 注册表键值  | 
 默认RGB值  | 
| 
 COLOR_SCROLLBAR  | 
 Scrollbar  | 
 C0-C0-C0  | 
| 
 COLOR_BACKGROUND  | 
 Background  | 
 00-80-80  | 
| 
 COLOR_ACTIVECAPTION  | 
 ActiveTile  | 
 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  | 
| 
 [没有标识符,使用值25]  | 
 ButtonAlternateFace  | 
 B8-B4-B8  | 
| 
 COLOR_HOTLIGHT  | 
 HotTrackingColor  | 
 00-00-FF  | 
| 
 COLOR_GRADIENTACTIVECAPTION  | 
 GradientActiveTitle  | 
 00-00-80  | 
| 
 COLOR_GRADIENTINACTIVECAPTION  | 
 GradientInactiveTitle  | 
 80-80-80  | 
(2)使用举例
①将客户区背景色设为COLOR_BTNFACE
wndClass.hbrBackground=(HBRUSH)(COLOR_BTNFACE + 1);
//注意:hbrBackground值很低时,指的是系统颜色,而不是实际的句柄。+1是为了防止NULL。
②TextOut显示的文本和其背景颜色
SetBkColor(hdc,GetSysColor(COLOR_BTNFACE));
SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEXT));
(3)用户更改系统颜色,会发送WM_SYSCOLORCHANGE消息
caseWM_SYSCOLORCHANGE:InvalidateRect(hwnd,NULL,TRUE);break;
9.2.2 按钮的颜色
| 
 颜色  | 
 说明  | 
| 
 COLOR_BTNFACE  | 
 按钮主表面颜色或其他按钮的背景颜色  | 
| 
 COLOR_BTNSHADOW  | 
 按钮右侧和底部的阴影颜色  | 
| 
 COLOR_BTNTEXT  | 
 对按键按钮而言的,为上面的文本颜色  | 
| 
 COLOR_WINDOWTEXT  | 
 其他控件的文本颜色  | 
9.2.3 WM_CTLCOLORBTN消息:当子窗口即将重绘其客户区时,发送其给父窗口
| 
 wParam  | 
 按钮的设备环境句柄  | 
| 
 lParam  | 
 按钮的窗口句柄  | 
(1)只要按钮按钮和自绘按钮会发送WM_CTLCOLORBTN消息
(2)自绘按钮由父窗口过程来负责绘制
(3)处理WM_CTLCOLORBTN消息时,可以
①SetTextColor:设置文本颜色
②SetBkColor设置文本背景颜色
③返回子窗口的画刷句柄
9.2.4 自绘按钮Owner-Draw Button
(1)按钮风格
| 
 BS_ICON、BS_BITMAP  | 
 显示为一个图标或位图,可使用BM_SETIMAGE消息  | 
| 
 BS_OWNERDRAW  | 
 可完全控制的绘制  | 
(2)WM_DRAWITEM消息中的lParam参数——DRAWITEMSTRUCT结构
| 
 成员  | 
 常见值  | 
 备注  | 
| 
 CtlType  | 
 ODT_BUTTON: 按钮控件 ODT_COMBOBOX:组合框控件 ODT_LISTBOX: 列表框控件 ODT_LISTVIEW:列表视图控件 ODT_MENU: 菜单项 ODT_STATIC: 静态文本控件 ODT_TAB: Tab控件  | 
 控件类型  | 
| 
 CtlID  | 
 自绘控件ID,而对于菜单项则不需要使用该成员  | 
|
| 
 itemID  | 
 菜单项ID  | 
|
| 
 itemAction  | 
 ODA_DRAWENTIRE:要整个控件被绘制,设置该值 ODA_FOCUS: 要在获得或失去焦点时被绘制。 ODA_SELECT:要在选中状态改变时被绘制。  | 
 指定绘制行为,其取值可以为右边中所示值的一个或者多个的联合  | 
| 
 itemState  | 
 ODS_CHECKED:要菜单被选中,可设置该值 ODS_COMBOBOXEDIT:只绘制组合框中选择区域 ODS_DEFAULT:默认值 ODS_DISABLED:禁用控件 ODS_FOCUS:控件需要焦点,则该置该值 ODS_GRAYED:控件被灰色 ODS_SELECTED:选中的菜单项 ……  | 
 所绘项的可见状态  | 
| 
 hwndItem  | 
 自绘控件的窗口句柄;如果自绘的对象时菜单项,则表示包含该菜单项的菜单句柄。  | 
|
| 
 hDC  | 
 指定了绘制操作所使用的设备环境  | 
|
| 
 rcItem  | 
 指定了将被绘制的矩形区域  | 
|
| 
 itemData  | 
 
  | 
|
(3)自绘按钮坐标

(4)DrawFocusRect函数用来绘制虚线矩形
(5)DRAWITEMSTRUCT结构中的设备环境的状态必须保持原样,被选入hdc的GDI对象必须设置回原有的不被选中的状态。
【OwnerDraw程序】
效果图
 
/*------------------------------------------------------------ OWNDRAW.C -- Owner-Draw Button Demo Program (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> #define ID_SMALLER 1 #define ID_LARGER 2 #define BTN_WIDTH (8 * cxChar) #define BTN_HEIGHT (4 * cyChar) HINSTANCE hInst; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("OwnDraw"); HWND hwnd; MSG msg; 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 = 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("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); 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 cxClient, cyClient, cxChar, cyChar; DRAWITEMSTRUCT* pdis; int cx, cy; POINT pt[3]; RECT rc; switch (message) { case WM_CREATE: cxChar = LOWORD(GetDialogBaseUnits()); cyChar = HIWORD(GetDialogBaseUnits()); //创建自绘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); //移动按钮到新的中心 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); switch (LOWORD(wParam)) { //每次10%放大或缩小窗口 case ID_SMALLER: rc.left += cxClient / 20; //左边缩小5% rc.right -= cxClient / 20; //左边缩小5% rc.top += cyClient / 20; //上边缩小5% rc.bottom -= cyClient / 20; //下边缩小5% break; case ID_LARGER: rc.left -= cxClient / 20; //左边扩大5% rc.right += cxClient / 20; //左边扩大5% rc.top -= cyClient / 20; //上边扩大5% rc.bottom += cyClient / 20; //下边扩大5% break; } 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, GetStockObject(WHITE_BRUSH)); FrameRect(pdis->hDC, &pdis->rcItem, GetStockObject(BLACK_BRUSH)); //画向内和向外的黑三角形 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 = 4 * cx / 8; pt[0].y = 5 * cy / 8; pt[1].x = 5 * cx / 8; pt[1].y = 7 * cy / 8; pt[2].x = 3 * cx / 8; pt[2].y = 7 * cy / 8; Triangle(pdis->hDC, pt); //左边的三角形 pt[0].x = 1 * cx / 8; pt[0].y = 3 * cy / 8; pt[1].x = 3 * cx / 8; pt[1].y = 4 * cy / 8; pt[2].x = 1 * cx / 8; pt[2].y = 5 * cy / 8; Triangle(pdis->hDC, pt); //右边的三角形 pt[0].x = 5 * cx / 8; pt[0].y = 4 * cy / 8; pt[1].x = 7 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 7 * cx / 8; pt[2].y = 5 * cy / 8; Triangle(pdis->hDC, pt); break; case ID_LARGER: //上边的三角形 pt[0].x = 4 * cx / 8; pt[0].y = 1 * cy / 8; pt[1].x = 5 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 3 * cx / 8; pt[2].y = 3 * 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 = 1 * cx / 8; pt[0].y = 4 * cy / 8; pt[1].x = 3 * cx / 8; pt[1].y = 3 * cy / 8; pt[2].x = 3 * cx / 8; pt[2].y = 5 * cy / 8; Triangle(pdis->hDC, pt); //右边的三角形 pt[0].x = 5 * cx / 8; pt[0].y = 3 * cy / 8; pt[1].x = 7 * cx / 8; pt[1].y = 4 * cy / 8; pt[2].x = 5 * cx / 8; pt[2].y = 5 * cy / 8; Triangle(pdis->hDC, pt); break; } //选中时,反转矩形 if (pdis->itemState & ODS_SELECTED) InvertRect(pdis->hDC, &pdis->rcItem); //按钮获得焦点时画虚线框 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); }
9.3 静态类
(1)创建:CreateWindow中的参数——“static”
(2)静态窗口的样式
| 
 样式  | 
 说明  | 
| 
 SS_BLACKRECT/SS_BLACKFRAME  | 
 填充矩形或画边框,分别对应的系统颜色为COLOR_3DDSHADOW、COLOR_BTNSHADOW、COLOR_BTNHIGHLIGHT。在CreateWindow调用的窗口文本字段将被忽略(因为被填充了)。坐标是相对于父窗口的  | 
| 
 SS_GRAYRECT / SS_GRAYRECT  | 
|
| 
 SS_WHITERECT/SS_WHITEFRAME  | 
|
| 
 SS_ETCHEDHORZ  | 
 用白色和灰色,建立一个边框,并将顶端边框设置为浮雕风格  | 
| 
 SS_ETCHEDVERT  | 
 用白色和灰色,建立一个边框,并将左侧边框设置为浮雕风格  | 
| 
 SS_ETCHEDFRAME  | 
 建立一个浮雕边框(阴影边框)  | 
| 
 SS_LEFT  | 
 文本的对齐方式,相应的文本由CreateWindow的文本参数指定,可通过SetWindowsText修改。窗口过程内部使用DrawText函数带DT_WORDBREAK、DT_NOCLIP、DT_EXPANDTABS参数来显文本  | 
| 
 SS_RIGHT  | 
|
| 
 SS_CENTER  | 
|
| 
 SS_ICON  | 
 作为子窗口控件时,该样式是无意义的。  | 
| 
 SS_USERITEM  | 
(3)消息
①不接受鼠标和键盘输入,也不向父窗口发送WM_COMMAND消息
②单击时子窗口捕获WM_NCHITTEST,并返回HTTRANSPARENT,导致Windows向底层窗口发送相同的WM_NCHITTEST消息,通常父窗口将该消息传给DefWindowProc,在那里会转换为客户区的鼠标消息。
                    
                
                
            
        
浙公网安备 33010602011771号