1、基础 |
---|
windows提供了多种预定义控件,这些控件是已经注册的窗口类,只需要调用CreateWindow
函数直接创建即可。CreateWindow
的传参和创建普通窗口时稍有不同:
CreateWindow(
_In_opt_ LPCWSTR lpClassName, // 窗口类名称
_In_opt_ LPCWSTR lpWindowName, // 窗口文本
_In_ DWORD dwStyle, // 窗口样式,需包含WS_CHILD以及控件样式
_In_ int X, // 窗口位置x坐标
_In_ int Y, // 窗口位置y坐标
_In_ int nWidth, // 窗口宽
_In_ int nHeight, // 窗口高
_In_opt_ HWND hWndParent, // 父窗口句柄
_In_opt_ HMENU hMenu, // 子窗口控件ID(创建普通窗口时是窗口菜单句柄)
_In_opt_ HINSTANCE hInstance, // 应用程序句柄
_In_opt_ LPVOID lpParam); // 创建参数
CreateWindow
需要应用程序句柄hInstance
传参,创建普通窗口一般在WinMain
中,WinMain
的第一个参数就是hInstance
,而控件的创建一般在窗口过程中。在窗口过程中获取hInstance
有几种方法:
- 使用全局变量保存
WinMain
中hInstance
WM_CREATE
消息的lParam
参数是一个指向CREATESTRUCT
结构的指针,hInstance
是其中一个成员GetWindowLong(hwnd, GWL_HINSTANCE)
子窗口控件状态改变时,会向父窗口发送WM_COMMAND
消息:
LOWORD(wParam)
子窗口ID,HIWORD(wParam)
高位字是通知码,通知码描述了消息的具体含义lParam
子窗口句柄
父窗口也可以主动发送消息给子窗口控件来获取或改变子窗口控件的状态。windows为不同的子窗口控件定义了专用的消息。此外,子窗口控件的本质还是窗口,除了专用消息外,子窗口控件也可以使用一些通用的WM_
消息。比如,编辑框可以使用WM_CUT
、WM_COPY
、WM_PASTE
和WM_CLEAR
等来实现复制、粘贴等功能;下拉框可以使用WM_SIZE
来允许用户改变大小,使用WM_CAPTION
来允许用户移动控件等。
子窗口的句柄和ID具有映射关系,可以通过其中一个来获取另一个:
id = GetWindowLong(hwndChild, GWL_ID);
id = GetDlgCtrlID(hwndChild);
hwndChild = GetDlgItem(hwndParant, id);
2、按钮类 |
---|
类名:button
样式:
#define BS_PUSHBUTTON 0x00000000L
#define BS_DEFPUSHBUTTON 0x00000001L
#define BS_CHECKBOX 0x00000002L
#define BS_AUTOCHECKBOX 0x00000003L
#define BS_RADIOBUTTON 0x00000004L
#define BS_3STATE 0x00000005L
#define BS_AUTO3STATE 0x00000006L
#define BS_GROUPBOX 0x00000007L
#define BS_USERBUTTON 0x00000008L
#define BS_AUTORADIOBUTTON 0x00000009L
#define BS_PUSHBOX 0x0000000AL
#define BS_OWNERDRAW 0x0000000BL
#define BS_TYPEMASK 0x0000000FL
#define BS_LEFTTEXT 0x00000020L
#if(WINVER >= 0x0400)
#define BS_TEXT 0x00000000L
#define BS_ICON 0x00000040L
#define BS_BITMAP 0x00000080L
#define BS_LEFT 0x00000100L
#define BS_RIGHT 0x00000200L
#define BS_CENTER 0x00000300L
#define BS_TOP 0x00000400L
#define BS_BOTTOM 0x00000800L
#define BS_VCENTER 0x00000C00L
#define BS_PUSHLIKE 0x00001000L
#define BS_MULTILINE 0x00002000L
#define BS_NOTIFY 0x00004000L
#define BS_FLAT 0x00008000L
#define BS_RIGHTBUTTON BS_LEFTTEXT
#endif /* WINVER >= 0x0400 */
通知码:
#define BN_CLICKED 0
#define BN_PAINT 1
#define BN_HILITE 2
#define BN_UNHILITE 3
#define BN_DISABLE 4
#define BN_DOUBLECLICKED 5
#if(WINVER >= 0x0400)
#define BN_PUSHED BN_HILITE
#define BN_UNPUSHED BN_UNHILITE
#define BN_DBLCLK BN_DOUBLECLICKED
#define BN_SETFOCUS 6
#define BN_KILLFOCUS 7
#endif /* WINVER >= 0x0400 */
专用消息:
#define BM_GETCHECK 0x00F0
#define BM_SETCHECK 0x00F1
#define BM_GETSTATE 0x00F2
#define BM_SETSTATE 0x00F3
#define BM_SETSTYLE 0x00F4
#if(WINVER >= 0x0400)
#define BM_CLICK 0x00F5
#define BM_GETIMAGE 0x00F6
#define BM_SETIMAGE 0x00F7
#endif /* WINVER >= 0x0400 */
#if(WINVER >= 0x0600)
#define BM_SETDONTCLICK 0x00F8
#endif /* WINVER >= 0x0600 */
3、焦点问题 |
---|
窗口拥有焦点时,才能响应键盘输入。子窗口控件获取焦点后,所有的键盘输入都将发送到子窗口控件,父窗口将失去对键盘输入的控制。
如果需要避免这种情况,可以利用焦点切换的机制。焦点切换时,windows先向失去焦点的窗口发送WM_KILLFOCUS
消息,wParam
参数是将获取焦点的窗口句柄;再向将接受焦点的窗口发送WM_SETFOCUS
消息,wParam
参数是失去焦点的窗口句柄。那么,我们可以在父窗口失去焦点,而获取焦点的是子窗口控件时,重新把焦点切换到父窗口:
// 父窗口失去焦点
case WM_KILLFOCUS:
if (hwnd = GetParent((HWND)wParam))
SetFocus(hwnd);
return 0;
4、控件和颜色 |
---|
windows定义了一系列带COLOR_
前缀的颜色标识符,这些标识符描述了子窗口控件各个部分绘制时使用的默认颜色,使用GetSysColor
可以获取标识符对应的颜色值。
有时候,在使用按钮控件的窗口中,为了达到更好的视觉效果,可以把窗口背景颜色和TextOut
输出的背景颜色设置成按钮的背景颜色,把TextOut
输出的文本颜色设置成按钮的标题颜色:
// 关于(HBRUSH)(COLOR_BTNFACE + 1),书上的解释是,当HBRUSH值很小的时候
// windows知道它不是一个真正的句柄,而是一个系统颜色标识符
// 并要求这样使用系统颜色标识符时,需要+1
wndclass.hbrbackground = (HBRUSH)(COLOR_BTNFACE + 1);
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
当然,也可以改变控件的颜色,最直接的办法是调用SetSysColor
。系统颜色被改变后,windows会向窗口发送WM_SYSCOLORCHANGE
消息,可以在WM_SYSCOLORCHANGE
的消息处理中使窗口无效,从而刷新成新的颜色:
case WM_SYSCOLORCHANGE:
InvalidateRect(hwnd, NULL, TRUE);
break;
这种方法的缺点是它会影响其他程序的外观,更好的办法的处理WM_CTLCOLOR
类消息,windows会在控件重绘前给父窗口发送该消息。针对不同的控件,该消息带有不同的后缀,比如按钮类的WM_CTLCOLORBTN
。WM_CTLCOLORBTN
的wParam
参数是按钮的设备环境句柄,lParam
参数是按钮的窗口句柄。在WM_CTLCOLORBTN
的处理中,可以
- 设置文本颜色
SetTextColor
- 设置文本背景颜色
SetBkColor
- 创建画刷并返回其句柄,按钮使用该画刷重绘背景(不再使用后需要销毁)
对于按钮类,只有按键按钮和自绘按钮会发送WM_CTLCOLORBTN
消息给父窗口,且只有自绘按钮会使用WM_CTLCOLORBTN
消息返回的画刷。
5、BS_OWNERDRAW自绘按钮 |
---|
自绘按钮在需要重绘时,windows会向父窗口发送WM_DRAWITEM
消息,lParam
参数是一个DRAWITEMSTRUCT
指针,可以利用这个结构体提供的信息绘制按钮。
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType; // 控件类型,ODT_前缀标识符
UINT CtlID; // 控件ID
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem; // 控件窗口句柄
HDC hDC; // 控件设备环境句柄
RECT rcItem;
ULONG_PTR itemData;
} DRAWITEMSTRUCT, NEAR *PDRAWITEMSTRUCT, FAR *LPDRAWITEMSTRUCT;
6、静态类 |
---|
类名:static
样式:
#define SS_LEFT 0x00000000L
#define SS_CENTER 0x00000001L
#define SS_RIGHT 0x00000002L
#define SS_ICON 0x00000003L
#define SS_BLACKRECT 0x00000004L // 静态矩形框,内部填充
#define SS_GRAYRECT 0x00000005L
#define SS_WHITERECT 0x00000006L
#define SS_BLACKFRAME 0x00000007L // 静态矩形框,内部不填充
#define SS_GRAYFRAME 0x00000008L
#define SS_WHITEFRAME 0x00000009L
#define SS_USERITEM 0x0000000AL
#define SS_SIMPLE 0x0000000BL
#define SS_LEFTNOWORDWRAP 0x0000000CL
#if(WINVER >= 0x0400)
#define SS_OWNERDRAW 0x0000000DL
#define SS_BITMAP 0x0000000EL
#define SS_ENHMETAFILE 0x0000000FL
#define SS_ETCHEDHORZ 0x00000010L
#define SS_ETCHEDVERT 0x00000011L
#define SS_ETCHEDFRAME 0x00000012L
#define SS_TYPEMASK 0x0000001FL
#endif /* WINVER >= 0x0400 */
#if(WINVER >= 0x0501)
#define SS_REALSIZECONTROL 0x00000040L
#endif /* WINVER >= 0x0501 */
#define SS_NOPREFIX 0x00000080L /* Don't do "&" character translation */
#if(WINVER >= 0x0400)
#define SS_NOTIFY 0x00000100L
#define SS_CENTERIMAGE 0x00000200L
#define SS_RIGHTJUST 0x00000400L
#define SS_REALSIZEIMAGE 0x00000800L
#define SS_SUNKEN 0x00001000L
#define SS_EDITCONTROL 0x00002000L
#define SS_ENDELLIPSIS 0x00004000L
#define SS_PATHELLIPSIS 0x00008000L
#define SS_WORDELLIPSIS 0x0000C000L
#define SS_ELLIPSISMASK 0x0000C000L
#endif /* WINVER >= 0x0400 */
通知码:
#define STN_CLICKED 0
#define STN_DBLCLK 1
#define STN_ENABLE 2
#define STN_DISABLE 3
专用消息:
#define STM_SETICON 0x0170
#define STM_GETICON 0x0171
#if(WINVER >= 0x0400)
#define STM_SETIMAGE 0x0172
#define STM_GETIMAGE 0x0173
默认情况,静态类控件不会响应鼠标或键盘的输入,也不会向父窗口发送WM_COMMAND
消息。当鼠标在静态类控件上移动或者点击时,静态类控件窗口过程处理WM_NCHITTEST
消息时会返回HTTRANSPARENT
。这导致windows向其下一层窗口(通常为父窗口)发送相同的WM_NCHITTEST
消息。 父窗口窗口过程将消息交给DefWindowProc
处理,并被转换为客户区鼠标消息。如果希望静态类控件可以响应鼠标点击动作,必须使用SS_NOTIFY
样式。
7、滚动条 |
---|
类名:scrollbar
样式:
#define SBS_HORZ 0x0000L
#define SBS_VERT 0x0001L
#define SBS_TOPALIGN 0x0002L
#define SBS_LEFTALIGN 0x0002L
#define SBS_BOTTOMALIGN 0x0004L
#define SBS_RIGHTALIGN 0x0004L
#define SBS_SIZEBOXTOPLEFTALIGN 0x0002L
#define SBS_SIZEBOXBOTTOMRIGHTALIGN 0x0004L
#define SBS_SIZEBOX 0x0008L
#if(WINVER >= 0x0400)
#define SBS_SIZEGRIP 0x0010L
#endif /* WINVER >= 0x0400 */
消息:
#define WM_HSCROLL 0x0114
#define WM_VSCROLL 0x0115
滚动条控件和其他控件不同,不会发送WM_COMMAND
消息给父窗口,而是和窗口滚动条一样,发送WM_VSCROLL
和WM_HSCROLL
消息:
lParam
窗口滚动条时为0,滚动条控件时为滚动条窗口句柄LOWORD(wParam)
滚动条动作,和窗口滚动条一样
滚动条控件的操作和窗口滚动条一样,可以使用SetScrollRange
、SetScrollPos
和SetScrollInfo
函数操作。不同的是,第一参数需要传滚动条控件句柄,第二参数需要传SB_CTL
。
8、关于WM_TABSTOP、IsDialogMessage和WM_GETDLGCODE |
---|
学习对话框部分内容后再回来补充。
9、编辑框 |
---|
类名:edit
样式:
#define ES_LEFT 0x0000L
#define ES_CENTER 0x0001L
#define ES_RIGHT 0x0002L
#define ES_MULTILINE 0x0004L
#define ES_UPPERCASE 0x0008L
#define ES_LOWERCASE 0x0010L
#define ES_PASSWORD 0x0020L
#define ES_AUTOVSCROLL 0x0040L
#define ES_AUTOHSCROLL 0x0080L
#define ES_NOHIDESEL 0x0100L
#define ES_OEMCONVERT 0x0400L
#define ES_READONLY 0x0800L
#define ES_WANTRETURN 0x1000L
#if(WINVER >= 0x0400)
#define ES_NUMBER 0x2000L
#endif /* WINVER >= 0x0400 */
通知码:
#define EN_SETFOCUS 0x0100
#define EN_KILLFOCUS 0x0200
#define EN_CHANGE 0x0300 // 内容将变化
#define EN_UPDATE 0x0400 // 内容已变化
#define EN_ERRSPACE 0x0500 // 没有空间
#define EN_MAXTEXT 0x0501
#define EN_HSCROLL 0x0601 // 单击水平滚动条
#define EN_VSCROLL 0x0602
#if(_WIN32_WINNT >= 0x0500)
#define EN_ALIGN_LTR_EC 0x0700
#define EN_ALIGN_RTL_EC 0x0701
#endif /* _WIN32_WINNT >= 0x0500 */
#if(WINVER >= 0x0604)
#define EN_BEFORE_PASTE 0x0800
#define EN_AFTER_PASTE 0x0801
#endif /* WINVER >= 0x0604 */
消息:
#define EM_GETSEL 0x00B0
#define EM_SETSEL 0x00B1
#define EM_GETRECT 0x00B2
#define EM_SETRECT 0x00B3
#define EM_SETRECTNP 0x00B4
#define EM_SCROLL 0x00B5
#define EM_LINESCROLL 0x00B6
#define EM_SCROLLCARET 0x00B7
#define EM_GETMODIFY 0x00B8
#define EM_SETMODIFY 0x00B9
#define EM_GETLINECOUNT 0x00BA
#define EM_LINEINDEX 0x00BB
#define EM_SETHANDLE 0x00BC
#define EM_GETHANDLE 0x00BD
#define EM_GETTHUMB 0x00BE
#define EM_LINELENGTH 0x00C1
#define EM_REPLACESEL 0x00C2
#define EM_GETLINE 0x00C4
#define EM_LIMITTEXT 0x00C5
#define EM_CANUNDO 0x00C6
#define EM_UNDO 0x00C7
#define EM_FMTLINES 0x00C8
#define EM_LINEFROMCHAR 0x00C9
#define EM_SETTABSTOPS 0x00CB
#define EM_SETPASSWORDCHAR 0x00CC
#define EM_EMPTYUNDOBUFFER 0x00CD
#define EM_GETFIRSTVISIBLELINE 0x00CE
#define EM_SETREADONLY 0x00CF
#define EM_SETWORDBREAKPROC 0x00D0
#define EM_GETWORDBREAKPROC 0x00D1
#define EM_GETPASSWORDCHAR 0x00D2
#if(WINVER >= 0x0400)
#define EM_SETMARGINS 0x00D3
#define EM_GETMARGINS 0x00D4
#define EM_SETLIMITTEXT EM_LIMITTEXT /* ;win40 Name change */
#define EM_GETLIMITTEXT 0x00D5
#define EM_POSFROMCHAR 0x00D6
#define EM_CHARFROMPOS 0x00D7
#endif /* WINVER >= 0x0400 */
#if(WINVER >= 0x0500)
#define EM_SETIMESTATUS 0x00D8
#define EM_GETIMESTATUS 0x00D9
#endif /* WINVER >= 0x0500 */
#if(WINVER >= 0x0604)
#define EM_ENABLEFEATURE 0x00DA
#endif /* WINVER >= 0x0604 */
// 此外,还可以使用
#define WM_CUT 0x0300
#define WM_COPY 0x0301
#define WM_PASTE 0x0302
#define WM_CLEAR 0x0303
10、下拉列表 |
---|
类名:listbox
样式:
#define LBS_NOTIFY 0x0001L
#define LBS_SORT 0x0002L
#define LBS_NOREDRAW 0x0004L // 增删项目时不会更新显示,也可以使用WM_SETREDRAW来动态改变
#define LBS_MULTIPLESEL 0x0008L
#define LBS_OWNERDRAWFIXED 0x0010L
#define LBS_OWNERDRAWVARIABLE 0x0020L
#define LBS_HASSTRINGS 0x0040L
#define LBS_USETABSTOPS 0x0080L
#define LBS_NOINTEGRALHEIGHT 0x0100L
#define LBS_MULTICOLUMN 0x0200L
#define LBS_WANTKEYBOARDINPUT 0x0400L
#define LBS_EXTENDEDSEL 0x0800L
#define LBS_DISABLENOSCROLL 0x1000L
#define LBS_NODATA 0x2000L
#if(WINVER >= 0x0400)
#define LBS_NOSEL 0x4000L
#endif /* WINVER >= 0x0400 */
#define LBS_COMBOBOX 0x8000L
#define LBS_STANDARD (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
默认的列表框不会向父窗口发送WM_COMMAND
消息,所以通常创建列表框控件时都需要LBS_NOTIFY
样式。
消息:
#define LB_ADDSTRING 0x0180
#define LB_INSERTSTRING 0x0181
#define LB_DELETESTRING 0x0182
#define LB_SELITEMRANGEEX 0x0183
#define LB_RESETCONTENT 0x0184
#define LB_SETSEL 0x0185
#define LB_SETCURSEL 0x0186
#define LB_GETSEL 0x0187
#define LB_GETCURSEL 0x0188
#define LB_GETTEXT 0x0189
#define LB_GETTEXTLEN 0x018A
#define LB_GETCOUNT 0x018B
#define LB_SELECTSTRING 0x018C
#define LB_DIR 0x018D
#define LB_GETTOPINDEX 0x018E
#define LB_FINDSTRING 0x018F
#define LB_GETSELCOUNT 0x0190
#define LB_GETSELITEMS 0x0191
#define LB_SETTABSTOPS 0x0192
#define LB_GETHORIZONTALEXTENT 0x0193
#define LB_SETHORIZONTALEXTENT 0x0194
#define LB_SETCOLUMNWIDTH 0x0195
#define LB_ADDFILE 0x0196
#define LB_SETTOPINDEX 0x0197
#define LB_GETITEMRECT 0x0198
#define LB_GETITEMDATA 0x0199
#define LB_SETITEMDATA 0x019A
#define LB_SELITEMRANGE 0x019B
#define LB_SETANCHORINDEX 0x019C
#define LB_GETANCHORINDEX 0x019D
#define LB_SETCARETINDEX 0x019E
#define LB_GETCARETINDEX 0x019F
#define LB_SETITEMHEIGHT 0x01A0
#define LB_GETITEMHEIGHT 0x01A1
#define LB_FINDSTRINGEXACT 0x01A2
#define LB_SETLOCALE 0x01A5
#define LB_GETLOCALE 0x01A6
#define LB_SETCOUNT 0x01A7
#if(WINVER >= 0x0400)
#define LB_INITSTORAGE 0x01A8
#define LB_ITEMFROMPOINT 0x01A9
#endif /* WINVER >= 0x0400 */
#if defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0400)
#define LB_MULTIPLEADDSTRING 0x01B1
#endif
#if(_WIN32_WINNT >= 0x0501)
#define LB_GETLISTBOXINFO 0x01B2
#endif /* _WIN32_WINNT >= 0x0501 */
#if(_WIN32_WINNT >= 0x0501)
#define LB_MSGMAX 0x01B3
#elif defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0400)
#define LB_MSGMAX 0x01B1
#elif(WINVER >= 0x0400)
#define LB_MSGMAX 0x01B0
#else
#define LB_MSGMAX 0x01A8
#endif
#endif /* !NOWINMESSAGES */
通知码:
#define LBN_ERRSPACE (-2)
#define LBN_SELCHANGE 1
#define LBN_DBLCLK 2
#define LBN_SELCANCEL 3
#define LBN_SETFOCUS 4
#define LBN_KILLFOCUS 5