控件
对于常用的一些窗口,Windows已经预先定义好了这些窗口的类(比如按钮,复选框,编辑框,静态字符串标签和滚动条等)方便开发者使用,一般称它们为子窗口控件,简称控件。控件会自己处理消息,在自己状态发生改变时通知父窗口,换句话说,我们可以直接使用这些预定义好的子窗口,而不需要给它们定义回调函数,而在父窗口的回调函数中就能处理到这些子窗口的消息。
CreateWindow( LPCTSTR lpClassName,LPCTSTR lpWindowName,DWORD dwStyle,int x,int y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HANDLE hlnstance,LPVOID lpParam);
如上是我们创建窗口类的函数,该函数除了可以创建正常的窗口以外,也可以创建一些Windows已经预定义好的系统类即控件,如按钮,编辑框等等。当我们需要创建控件时,我们需要在case WM_CREATE信息中使用该函数进行控件的创建。
首先我们应该知道,所谓按钮静态文本等等控件的本质都是窗口,他们是隶属于父窗口下的子窗口。因此在创建控件前,我们首先要创建一个父窗口。此处我们直接使用Windows桌面程序创建时,程序自动为我们创建的一个窗口,该窗口表示如下:
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,750, 400, 500, 800, nullptr, nullptr, hInstance, nullptr);
此时我们已经有了一个主窗口,接下来我们开始以该窗口为父窗口,去讲解按钮等控件
在以下控件内容中,我们首先在全局变量中定义我们的控件ID,以便我们后续控件的查找
#define IDC_BUTTON_CHECK_1 0
#define IDC_BUTTON_CHECK_2 1
#define IDC_BUTTON_CHECK_3 2
按钮的创建
常见按钮类型如下:
BS_PUSHBUTTON; 按压式按钮
BS_DEFPUSHBUTTON; 显眼的按压式按钮
BS_CHECKBOX; 复选框
BS_AUTOCHECKBOX; 自动复选框
BS_RADIOBUTTON; 单选按钮
BS_AUTORADIOBUTTON; 自动单选按钮
BS_3STATE; 三状态按钮
BS_AUTO3STATE; 自动三状态按钮
BS_GROUPBOX; 按钮群
BS_OWNERDRAW; 自绘按钮
由于控件本身是作为父窗口的子窗口所存在,所以我们需要在窗口主过程的窗口创建的消息中进行创建,而非在WM_PAINT中绘制控件。也就是如下函数中创建:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时我们创建一个显眼的按钮
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)//程序句柄hInstance只在WinMain入口函数提及且可用,在WndProc函数中不可用。因此我们需要使用这种方式来获取应用程序实例句柄
);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
通过以上步骤我们创建好了一个按钮,按钮如下:
按钮的应用
当我们点击按钮时,系统会发送一个点击消息,该消息由父窗口主过程的WM_COMMAND信息接收,然后进行二次分发。因此我们要编写的应用代码位置如下:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)//应用程序实例句柄
);
}
break;
case WM_COMMAND:
{
//此处开始编写控件应用代码
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
在应用按钮之前,我们应该知道wParam:高16为时通知码,低16位是子窗口ID,IParam存储的是触发消息的子控件的句柄
接下来我们将使用前文中创建的按钮进行应用
应用一:单击按钮,按钮名称改变
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
}
break;
case WM_COMMAND:
{
//此处开始编写控件应用代码
wParam; //获取发送的消息按钮信息
if (LOWORD(wParam) == IDC_BUTTON_CHECK_1) //此处判断发送的信息的控件是否和我们应用的控件一致
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_1); //该函数参数依次为父窗口的句柄和控件ID,函数返回控件的句柄
SetWindowText(hSWnd,L’sad’); //此时修改按钮标题为sad
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时,我们点击按钮,按钮名字发生改变
应用二:点击一个按钮把另一个按钮名称改变
我们首先要创建第二个按钮:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
//此处开始编写控件应用代码
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时第二个按钮创建完毕
应用三:点击按钮1,修改按钮2名称
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_BUTTON_CHECK_1) //此处判断是否点击按钮1
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_2); //获取按钮2句柄
SetWindowText(hSWnd,L’sadXS’); //此时修改按钮2标题
}
//此处开始编写控件应用代码
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
应用四:点击按钮2,使按钮2名称修改为按钮1的名称
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_BUTTON_CHECK_2) //此处判断是否点击按钮2
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_1); //获取按钮1句柄
//获取按钮1名称方法一
WCHAR szBuffer[256] = { 0 };
//获取按钮1名称方法二
DWORD dwLength = GetWindowTextLength(hSWnd);
WCHAR *szBuffer = new WCHAR[dwLengthh];
GetWindowsText(hSWnd, szBuffer, 256)//将按钮1名称保存到数组中
SetWindowText((HWND)lParam,szBuffer); //此时修改按钮2标题
}
//此处开始编写控件应用代码
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
应用五:隐藏和显示目标按钮
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_BUTTON_CHECK_1) //此处判断是否点击按钮1
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_2); //获取按钮2句柄
if(IsWindowVisible(hSWnd)) //判断窗口2可不可见,此处为可见
{
ShowWindow(hSWnd, SW_HIDE); //隐藏目标窗口
}
else
{
ShowWindow(hSWnd, SW_SHOW); //显示目标窗口
}
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
应用六:禁用按钮
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hwndButton = CreateWindow(
L"BUTTON", //窗口类型,此处是按钮类名
L"OK", //按钮显示的文字
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, //按钮样式
10, //窗口相对于父窗口左上角x坐标
10, // 窗口相对于父窗口左上角y坐标
80, //窗口宽度
20, //窗口高度
hWnd, //控件所属的父窗口的句柄
(HMENU)IDC_BUTTON_CHECK_1, //控件ID,一般是全局变量。由于该函数返回的句柄可能不是全局变量,因此每个子窗口都要有ID,以便我们可以通过该ID找控件句柄
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE)
);
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == IDC_BUTTON_CHECK_1) //此处判断是否点击按钮1
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_2); //获取按钮2句柄
if(IsWindowEnabled(hSWnd)) //判断按钮是否正常,此处为正常
{
EnableWindow(hSWnd, FALSE); //禁用目标按钮
}
else
{
EnableWindow(hSWnd, TURE); //启用目标窗口
}
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
复选框的创建
首先我们创建两个复选框
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
CreateWindow(L"BUTTON", L"OK", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 10, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //注:BS_NOTIFY 用于双击,BS_NOTIFY多选按钮,该类型按钮,点击后,按钮前置空格不会把对号勾上
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
复选框的应用
应用一:点击复选框,弹出窗口
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
CreateWindow(L"BUTTON", L"OK", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 10, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //注:BS_NOTIFY 用于双击,BS_NOTIFY多选按钮,该类型按钮,点击后,按钮前置空格不会把对号勾上
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
switch (HIWORD(wParam)) //通知码
{
case BN_CLICKED: //单击点击时
{
MessageBox(NULL, L"单击", L"Msg",MB_OK);
break;
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时点击复选框就会弹出窗口
应用二:双击复选框,弹出窗口
窗口样式要加上BS_NOTIFY
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
CreateWindow(L"BUTTON", L"OK", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 10, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //注:BS_NOTIFY 用于双击,BS_NOTIFY多选按钮,该类型按钮点击后,按钮前置空格不会把对号勾上
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
switch (HIWORD(wParam)) //通知码
{
case BN_DBLCLK: //双击点击时
{
MessageBox(NULL, L"双击", L"Msg",MB_OK);
break;
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
应用三:点击复选框,前面空格画勾
需要将按钮样式BS_CHECKBOX 改为BS_AUTOCHECKBOX
下面进行演示如何在不修改样式的情况下,依然使空格画勾
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
CreateWindow(L"BUTTON", L"OK", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 10, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //注:BS_NOTIFY 用于双击,BS_NOTIFY多选按钮,该类型按钮,点击后,按钮前置空格不会把对号勾上
CreateWindow(L"BUTTON", L"but2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_NOTIFY, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
break;
case WM_COMMAND:
{
switch (HIWORD(wParam)) //通知码
{
case BN_CLICKED: //单击点击时
{
SendMessage((HWND)lParam,BM_SETCHECK,!SendMessage((HWND)lParam,BM_GETCHECK, 0, 0), 0);
break;
}
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
原理是:单击时发送消息BM_SETCHECK给触发控件的窗口设置该控件是否被点击,条件是再次发送信息BM_GETCHECK给该窗口以获取该窗口点击状态并对该信息取反,如此执行,该多选按钮点击后会有对号勾上
单选框的创建
首先我们要创建一个群用于存放我们的单选框
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hGroup = CreateWindow(L"BUTTON", L"Radio", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 10, 10, 400, 300, hWnd, (HMENU)0, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); //创建一个群
CreateWindow(L"BUTTON", L"Radio1", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 50, 40, 80, 40, hGroup, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); //以下创建两个单选框
CreateWindow(L"BUTTON", L"Radio2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 50, 80, 80, 40, hGroup, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
}
break;
case WM_COMMAND:
{
switch (HIWORD(wParam)) //通知码
{
case BN_CLICKED: //单击点击时
{
SendMessage((HWND)lParam,BM_SETCHECK,!SendMessage((HWND)lParam,BM_GETCHECK, 0, 0), 0);
break;
}
}
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序后,在一个群中,多个按钮只能有一个被勾选
单选框的应用
应用一:判断某按钮是否被选中
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建控件
HWND hGroup = CreateWindow(L"BUTTON", L"Radio", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 10, 10, 400, 300, hWnd, (HMENU)0, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); //创建一个群
CreateWindow(L"BUTTON", L"Radio1", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 10, 10, 80, 20, hGroup, (HMENU)IDC_BUTTON_CHECK_1, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); //以下创建两个单选框
CreateWindow(L"BUTTON", L"Radio2", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON, 10, 40, 80, 20, hGroup, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
CreateWindow(L"BUTTON", L"OK", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX, 10, 70, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_3, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL); //创建一个点击框
}
break;
case WM_COMMAND:
{
switch(HIWORD(wParam))
{
case BN_CLICKED:
{
SendMessage((HWND)lParam,BM_SETCHECK,!SendMessage((HWND)lParam,BM_GETCHECK, 0, 0), 0);
if (LOWORD(wParam) == IDC_BUTTON_CHECK_3) //判断按钮3是否被点击
{
HWND hSWnd = GetDlgItem(hWnd, IDC_BUTTON_CHECK_1);//获取窗口1句柄以判断点取状态
if (SendMessage(hSWnd, BM_GETCHECK, 0, 0) == TRUE) //判断该按钮是否被d点击
{
MessageBox(NULL, L"选中", L"Msg", MB_OK);
}
else
{
MessageBox(NULL, L"未选中", L"Msg", MB_OK);
}
}
}
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时点击单选框,再点击OK便可以判断是否被选中了
自绘按钮的创建
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建自绘按钮
HWND BTN = CreateWindow(L"BUTTON", L"", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWENRORAW | BS_ICON,10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //BS_OWENRORAW 可以给图标上色 BS_ICON 可以在图标上加载图片
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序,发现创建了一个空的按钮
自绘按钮的应用
应用一:按钮上涂色
在窗口主过程中,增加一个WM_CTLCOLORBTN消息,在该消息处进行按钮的设计
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建自绘按钮
HWND BTN = CreateWindow(L"BUTTON", L"", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWENRORAW | BS_ICON,10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //BS_OWENRORAW 可以给图标上色 BS_ICON 可以在图标上加载图片
break;
}
case WM_CTLCOLORBTN: //此处对按钮涂色
return (LRESULT)CreateSolidBrush(RGB(0, 0, 255));
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序,按钮变成蓝色
应用二:按钮加载图像
按钮样式需要加上BS_ICON
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建自绘按钮
HWND BTN = CreateWindow(L"BUTTON", L"", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWENRORAW | BS_ICON,10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON_CHECK_2, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL); //BS_OWENRORAW 可以给图标上色 BS_ICON 可以在图标上加载图片
SendMessage(BTN, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon(NULL, IDI_ERROR));//按钮加载图片
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时,按钮会加载一个叉号图标
静态文本
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建静态文本
CreateWindow(L"static", L"test", WS_VISIBLE | WS_CHILD | SS_LEFT, 10, 10, 100, 30, hWnd,NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);//此处应用新的获取程序实例句柄的方式
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序以后,有一个靠左上的test的文本
静态文本应用
应用一:静态文本显示图标
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建静态文本
HWND hStatic = CreateWindow(L"static", L"", WS_VISIBLE | WS_CHILD | SS_ICON, 10, 10, 100, 30, hWnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
SendMessage(hStatic, STM_SETICON, (WPARAM)LoadIcon(NULL, IDI_ERROR), NULL);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序,显示一个用静态文本形式显示的叉号的图标
应用二:静态文本显示位图
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处显示位图
HWND hStatic = CreateWindow(L"static", L"", WS_VISIBLE | WS_CHILD | SS_BITMAP, 10, 10, 100, 30, hWnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
HBITMAP hBitMap = (HBITMAP)LoadImage(NULL, L"bg.bmp", IMAGE_BITMAP, 500, 100, LR_LOADFROMFILE); //加载位图,假设bg.bmp是我们的位图名,处于该本程序目录下
SendMessage(hStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitMap);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序会加载出我们自己的位图
编辑框
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
//此处创建编辑框
HWND hwndEdit = CreateWindowEx(0, L"EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL |ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,100, 10, 80, 200, hWnd, (HMENU)12138, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),NULL);
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
此时运行程序出现一个编辑框,会有垂直滚动条
作业
实现一个登录窗口,具有用户密码和登录按钮。可以判断是否输入正确
// WinSDK登录框.cpp : 定义应用程序的入口点。
//
#include "framework.h"
#include "WinSDK登录框.h"
#define MAX_LOADSTRING 100
#define user 1 //登录框用户
#define password 2 //登录框密码
#define register 3 //登录按钮
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINSDK, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINSDK));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINSDK));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINSDK);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, L"登录框", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
CreateWindow(L"EDIT",0, WS_CHILD | WS_VISIBLE | WS_BORDER, 500, 200, 200, 20, hWnd, (HMENU)user, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 0);
CreateWindow(L"EDIT", 0, WS_CHILD | WS_VISIBLE | WS_BORDER |ES_PASSWORD , 500, 230, 200, 20, hWnd, (HMENU)password, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 0);
CreateWindow(L"BUTTON", L"登录", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 580, 260, 50, 20, hWnd, (HMENU)register, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 0);
}
case WM_COMMAND:
{
int wmId = LOWORD(wParam);//用于判断点击的是哪个控件
// 分析菜单选择:
switch (wmId)
{
case register:
{
WCHAR UserWord[256] = { 0 };
WCHAR PassWord[256] = { 0 };
GetDlgItemText(hWnd, user, UserWord, sizeof(UserWord));//用于将在user编辑框输入的字符复制到缓冲区
GetDlgItemText(hWnd, password, PassWord, sizeof(PassWord));//用于将在password编辑框输入的字符复制到缓冲区
if (wcscmp(UserWord, L"user") == 0 && wcscmp(PassWord, L"password") == 0)
{
MessageBox(hWnd, L"登录成功", L"提示", MB_OK);
}
else
{
MessageBox(hWnd, L"登录失败", L"提示", MB_RETRYCANCEL);
}
}
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
TextOut(hdc, 470, 200, L"user", wcslen(L"user"));
TextOut(hdc, 435, 230, L"password", wcslen(L"password"));
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}