Skip to content

标准控件

创建控件仍然需要使用CreateWindow()函数

c++
HWND CreateWindow(
  [in, optional] LPCSTR    lpClassName,
  [in, optional] LPCSTR    lpWindowName,
  [in]           DWORD     dwStyle,
  [in]           int       x,
  [in]           int       y,
  [in]           int       nWidth,
  [in]           int       nHeight,
  [in, optional] HWND      hWndParent,
  [in, optional] HMENU     hMenu,
  [in, optional] HINSTANCE hInstance,
  [in, optional] LPVOID    lpParam
);

参数

  • lpClassName:控件的种类,不区分大小写,BUTTONbutton

注意:可以在 lpClassName 参数中指定以下预定义控件类。 请注意可在 dwStyle 参数中使用的相应控件样式。

含义
BUTTON指定一个小矩形子窗口,该窗口表示用户可以单击以将其打开或关闭的按钮。 按钮控件可以单独使用,也可以成组使用,并且可以不带文本标记或显示。 当用户单击按钮控件时,按钮控件通常会更改外观。 按钮按钮样式
COMBOBOX指定由列表框和类似于编辑控件的选择字段组成的控件。 使用此样式时,应用程序应随时显示列表框或启用下拉列表框。 如果列表框可见,在选择字段中键入字符会突出显示与键入的字符匹配的第一个列表框条目。 相反,选择列表框中的项会在选择字段中显示所选文本。 组合框组合框样式
EDIT指定一个矩形子窗口,用户可以从键盘键入文本。 用户选择控件,并通过单击控件或通过按 TAB 键移动到控件来为它提供键盘焦点。 编辑控件显示闪烁的插入点时,用户可以键入文本;使用鼠标移动光标,选择要替换的字符,或定位光标以插入字符:或使用 键删除字符。 有关详细信息, 编辑控件编辑控件样式
LISTBOX指定字符串列表。 每当应用程序必须提供用户可从中选择的名称列表(如文件名)时,请指定此控件。 用户可以通过单击来选择字符串。 突出显示所选字符串,并将通知消息传递到父窗口。 列表框列表框样式
MDICLIENT指定 MDI 客户端窗口。 此窗口接收控制 MDI 应用程序的子窗口的消息。 建议的样式位是 WS_CLIPCHILDRENWS_CHILD。 指定 WS_HSCROLLWS_VSCROLL 样式以创建允许用户将 MDI 子窗口滚动到视图中的 MDI 客户端窗口。 多文档接口
RichEdit指定 Microsoft Rich Edit 1.0 控件。 此窗口允许用户使用字符和段落格式查看和编辑文本,并且可以将嵌入式组件对象模型 (COM) 对象。 Rich Edit 控件Rich Edit 控件样式
RICHEDIT_CLASS指定 Microsoft Rich Edit 2.0 控件。 此控件允许用户使用字符和段落格式查看和编辑文本,并且可以包含嵌入的 COM 对象。 Rich Edit 控件Rich Edit 控件样式
SCROLLBAR指定一个矩形,该矩形包含一个滚动框,两端都有方向箭头。 每当用户单击控件时,滚动条就会向其父窗口发送通知消息。 如有必要,父窗口负责更新滚动框的位置。 滚动条滚动条控件样式
STATIC指定用于标记、框或分隔其他控件的简单文本字段、框或矩形。 静态控件不采用任何输入,也不提供任何输出。 静态控件静态控件样式
  • lpWindowName:控件中的文字
  • dwStyle:控件样式, 此参数可以是 窗口样式值以及“备注”部分中指示的控件样式的组合。
  • X:控件的初始水平位置
  • Y:控件的初始垂直位置
  • nWidth:控件的宽度(以设备单位为单位)
  • nHeight:控件的高度(以设备单位为单位)
  • hWndParent:正在创建的控件的父窗口或所有者窗口的句柄,若无则为NULL
  • hMenu:控件的ID
  • hInstance:要与窗口关联的模块实例的句柄。
  • lpParam:指向要通过 CREATESTRUCT 结构传递到窗口的值的指针,

1.按钮

按钮控件允许用户通过单击来执行操作,既可以显示文本,又可以显示图像,当该按钮被单击时,它看起来像是被按下,然后被释放。

  • 按钮既是Windows标准控件,也是子窗口,窗口类名是button
  • 按钮分为普通按钮、单选按钮、复选框,dwStyle分别对应BS_PUSHBUTTONBS_AUTORADIOBUTTONBS_CHECKBOX

image-20240828171739997

按钮的创建需要在WM_CREATE消息的响应中创建

c++
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
		HWND btn = CreateWindow(
			"button",
			"普通按钮",
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			15,
			5,
			100,
			40, 
			hWnd,
			(HMENU)1001,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		// 创建单选按钮
		HWND radio = CreateWindow(
			"button",
			"单选按钮",
			WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
			15,
			60,
			100,
			40,
			hWnd,
			(HMENU)1002,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);

		// 复选框
		HWND checkbox = CreateWindow(
			"button",
			"单选按钮",
			WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
			15,
			110,
			100,
			40,
			hWnd,
			(HMENU)1003,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		break;
	
	return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

image-20240829104724150

1.1 种类和样式

按钮种类和样式通过CreateWindow的第三个参数确定,以下参数可通过|运算符合并

dwStyle可以设置为以下按钮风格的任意组合。

  • BS_PUSHBUTTON:指定一个命令按钮,也就是普通按钮
  • BS_CHECKBOX:指定在矩形按钮右侧带有标题的选择框,不会自动反选择
  • BS_AUTOCHECKBOX:同BS_CHECKBOX,不过单击鼠标时按钮会自动反选
  • BS_RADIOBUTTON:指定一个单选按钮,在圆按钮的右边显示正文
  • BS_AUTORADIOBUTTON:同BS_RADIOBUTTON,不过单击鼠标时按钮会自动反转
  • BS_AUTO3STATE:同BS_3STATE,不过单击按钮时会改变状态
  • BS_DEFPUSHBUTTON:指定默认的命令按钮,这种按钮的周围有一个黑框,用户可以按回车键来快速选 择该按钮
  • BS_GROUPBOX:指定一个组框
  • BS_LEFTTEXT:使控件的标题显示在按钮的左边
  • BS_OWNERDRAW:指定一个自绘式按钮
  • BS_3STATE:同BS_CHECKBOX,不过控件有3种状态一选择、未选择和变灰

以下为按钮的通用属性

  • WS_BORDER:创建具有边框的窗口。
  • WS_CAPTION:创建该窗口具有标题栏(意味着WS_BORDER样式)。
  • WS_CHILD:创建一个子窗口。不能与使用WS_POPUP样式。
  • WS_DISABLED:创建最初处于禁用状态的窗口。
  • WS_VISIBLE:创建初始可见的窗口。
  • WS_GROUP:指定一组控件在其中用户可以从一个控件移到下的箭头键的第一个控件。
  • WS_HSCROLL:创建该窗口具有水平滚动条。
  • WS_VSCROLL:创建该窗口具有一个垂直滚动条。
  • WS_ICONIC:创建开始最小化窗口。与相同WS_MINIMIZE样式。
  • WS_MAXIMIZEBOX:创建一个窗口,都有一个最大化按钮。
  • WS_MINIMIZEBOX:创建具有最小化按钮的窗口。
  • WS_OVERLAPPED:创建重叠的窗口。重叠的窗口通常具有标题和边框。
  • WSPOPUP:创建一个弹出窗口。不能与使用WSCHILD样式。
  • WS_POPUPWINDOW:创建与一个弹出窗口WS_BORDER,WS_POPUP,和WS_SYSMENU样式。
  • WS_SIZEBOX:创建具有大小调整边框的窗口。与相同WS_THICKFRAME样式。
  • WS_SYSMENU:创建该窗口具有标题栏中的控件菜单框。使用仅为带有标题栏的窗口。
  • WS_TABSTOP:控件通过其用户可以通过使用TAB键移动的任意数量之一指定。TAB键将用户移至指定的下一个控件WS_TABSTOP样式。
  • WS_TILEDWINDOW:与相同WS_OVERLAPPEDWINDOW样式。

TIP

一般会为按钮设置WS_CHILDWS_VISIBLEWS_TABSTOP等风格,WS_TABSTOP风格使按钮控件具有tab停止属性,即按tab键切换焦点控件时能够将焦点停在按钮控件上。

创建一组单选按钮时第一个按钮的风格应设置为WS_CHILD|WS_VISIBLE|WS_TABSTOP |WS_GROUP|BS_AUTORADIOBUTTON,其他单选按钮的风格应为WS_CHILD|WS_VISIBLE |BS_AUTORADIOBUTTON,不包含WS_TABSTOPWS_GROUP

1.2 按钮消息

按钮控件的操作会向父窗口发送命令消息WM_COMMAND,常用的有单击和双击操作

  • BN_CLICKED:用户在按钮上单击鼠标时会向父窗口发送BN_CLICKED消息
  • BN_DOUBLECLICKED:双击鼠标时会向父窗口发送BN_DOUBLECLICKED消息

在发送WM_COMMAND时,LOWORD(wParam)是控件ID,HIWORD(wParam)Notification Code,也就是事件类别

c++
case WM_CREATE: {
    HWND btn = CreateWindow(
        "button",
        "普通按钮",
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        15,
        5,
        100,
        40,
        hWnd,
        (HMENU)1001,					// 控件ID
        ((LPCREATESTRUCT)lParam)->hInstance,
        NULL
    );
}
case WM_COMMAND:{
    switch (LOWORD(wParam)) {
        case 1001:			// 判断控件ID
            if (HIWORD(wParam) == BN_CLICKED) {			// 判断事件
                MessageBox(NULL, "你学会了点击按钮", "按钮消息",MB_OK);
            }
            break;
    }
}

对于单选框和复选框,我们需要获取其选中状态

态,可以使用SendMessage()向控件发送BM_GETCHECK消息或直接使用Button_GetCheck宏,其返回值为:

返回代码描述
BST_CHECKED按钮被选中。
BST_INDETERMINATE按钮呈灰色,表示处于不确定状态(仅在按钮具有BS_3STATE或BS_AUTOSTATE样式时适用)。
BST_UNCHECKED按钮未被选中。

通过SendMessage()获取单选框的消息

c++
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE: {
		HWND radio = CreateWindow(
			"button",
			"男",
			WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP | BS_AUTORADIOBUTTON,
			15,
			60,
			50,
			40,
			hWnd,
			(HMENU)1002,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		HWND radio1 = CreateWindow(
			"button",
			"女",
			WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
			65,
			60,
			50,
			40,
			hWnd,
			(HMENU)1003,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		break;
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
            case 1002:{
                HWND radio = (HWND)lParam;
                if (SendMessage(radio, BM_GETCHECK, 0, 0) == BST_CHECKED) {
                    MessageBox(NULL, "男被选择", "按钮消息", MB_OK);
                }
                break;
            }	
            case 1003:{
                HWND radio = (HWND)lParam;
                if (SendMessage(radio, BM_GETCHECK, 0, 0) == BST_CHECKED) {
                    MessageBox(NULL, "女被选择", "按钮消息", MB_OK);
                }
                break;
            }
		}
		return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

通过Button_GetCheck获取复选框的消息,注意引入windowsx.h头文件

Button_GetCheck的本质也是通过SendMessage()

c++
#include <windows.h>
#include <windowsx.h>
#include <string>

using namespace std;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE: {
		HWND checkbox = CreateWindow(
			"button",
			"唱",
			WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
			15,
			110,
			50,
			40,
			hWnd,
			(HMENU)1004,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		HWND checkbox1 = CreateWindow(
			"button",
			"跳",
			WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
			65,
			110,
			60,
			40,
			hWnd,
			(HMENU)1005,
			((LPCREATESTRUCT)lParam)->hInstance,
			NULL
		);
		break;
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case 1004:{
			HWND checkabox = (HWND)lParam;
			if (Button_GetCheck(checkabox) == BST_CHECKED) {
				MessageBox(NULL, "你喜欢唱", "按钮消息", MB_OK);
			}
			else {
				MessageBox(NULL, "你不喜欢唱", "按钮消息", MB_OK);
			}
			break;
		}	
		case 1005:{
			HWND checkabox1 = (HWND)lParam;
			if (Button_GetCheck(checkabox1) == BST_CHECKED) {
				MessageBox(NULL, "你喜欢跳", "按钮消息", MB_OK);
			}
			else {
				MessageBox(NULL, "你不喜欢跳", "按钮消息", MB_OK);
			}
			break;
		}
		}
		return 0;
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

MIT Licensed