9、子窗口控件

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有几种方法:

  • 使用全局变量保存WinMainhInstance
  • WM_CREATE消息的lParam参数是一个指向CREATESTRUCT结构的指针,hInstance是其中一个成员
  • GetWindowLong(hwnd, GWL_HINSTANCE)

子窗口控件状态改变时,会向父窗口发送WM_COMMAND消息:

  • LOWORD(wParam) 子窗口ID,
  • HIWORD(wParam) 高位字是通知码,通知码描述了消息的具体含义
  • lParam 子窗口句柄

父窗口也可以主动发送消息给子窗口控件来获取或改变子窗口控件的状态。windows为不同的子窗口控件定义了专用的消息。此外,子窗口控件的本质还是窗口,除了专用消息外,子窗口控件也可以使用一些通用的WM_消息。比如,编辑框可以使用WM_CUTWM_COPYWM_PASTEWM_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_CTLCOLORBTNWM_CTLCOLORBTNwParam参数是按钮的设备环境句柄,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_VSCROLLWM_HSCROLL消息:

  • lParam 窗口滚动条时为0,滚动条控件时为滚动条窗口句柄
  • LOWORD(wParam) 滚动条动作,和窗口滚动条一样

滚动条控件的操作和窗口滚动条一样,可以使用SetScrollRangeSetScrollPosSetScrollInfo函数操作。不同的是,第一参数需要传滚动条控件句柄,第二参数需要传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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值