win32程序之子窗口编程

一丶简介.什么是子窗口

在前边我们已经讲解了窗口的本质.以及如何注册窗口类跟创建窗口. 还讲了消息循环.

那么有很多窗口其实Windows已经帮我们创建出来了.我们直接使用即可. 而这些窗口都有自己的消息循环. 只有改变状态的时候.才会发送消息给我们的父窗口通知.

此时我们捕获消息就可以进行处理了.

子窗口其实就是绘制在主窗口的一个窗口. 这些窗口包含了 BUTTON (按钮控件) EDIT(编辑框控件) .....

二丶创建子窗口

1.创建EDIT子窗口

创建子窗口很简单. 使用CreteWindow API. 类名修改为EDIT. 父窗口句柄修改为我们的主窗口句柄. 并且为子窗口设置创建类型. 以及子窗口标识符即可.

具体代码如下: 当主窗口创建消息来得时候.我们创建一个EDIT编辑框.

// WindoS.cpp : 定义应用程序的入口点。

//

#include "stdafx.h"

#include

#include

#include "WindoS.h"

#define MAX_LOADSTRING 100

// 全局变量:

HINSTANCE g_hInst; // 当前实例

WCHAR szTitle[MAX_LOADSTRING] = TEXT("第一个我的窗口"); // 标题栏文本

WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow"); // 主窗口类名

#define IDC_MY_EDIT_ONE 10 //编辑框的ID 自己定义即可.

// 此代码模块中包含的函数的前向声明:

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,

_In_opt_ HINSTANCE hPrevInstance,

_In_ LPWSTR lpCmdLine,

_In_ int nCmdShow)

{

//1.自定义窗口样式

g_hInst = hInstance;

WNDCLASS wcex;

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc; //设置回调

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS));

wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS);

wcex.lpszClassName = szWindowClass;

//2.注册窗口类

BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW 扩展 RegisterClassExA /ExW

if (bRet == FALSE)

{

return 0;

}

//3.创建窗口 并且显示跟更新窗口

HWND hWnd = CreateWindowW(

szWindowClass,

szTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

0,

CW_USEDEFAULT,

0,

nullptr,

nullptr,

hInstance,

nullptr);

if (!hWnd)

{

return FALSE;

}

ShowWindow(hWnd, SW_SHOW);

UpdateWindow(hWnd);

//4.消息循环.

MSG msg;

/* 1参数是消息结构体.操作系统会往里面填写消息.

2 参数窗口句柄 因为每个线程可以有多个窗口.表示我要取那个窗口的消息

3.4 参数表示我要取这个窗口的那个消息. 后面三个参数属于过滤条件

*/

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)

{

if (bRet == -1)

{

// handle the error and possibly exit

}

else

{

TranslateMessage(&msg); //键盘消息转换为小写.

DispatchMessage(&msg); //分发消息.将我们的消息传递给我们的回调函数处理.

}

}

return 0;

}

//

// 函数: 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:

{

CreateWindowW( //创建编辑框

TEXT("EDIT"),

TEXT("编辑框所处位置"),

WS_CHILD | WS_VISIBLE | WS_VSCROLL, //前3个是通用风格.EDIT风格搜索MSDN 搜索Edit Style即可.

10,10,800,400, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_EDIT_ONE,

g_hInst,

nullptr);

break;

}

case WM_COMMAND:

{

int wmId = LOWORD(wParam);

// 分析菜单选择:

switch (wmId)

{

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;

}

上面红色的那块是很重要的. 重要参数标注出来.

1.窗口类名. 我们是使用的Windows默认的窗口类名.所以填写EDIT

2.窗口风格. 窗口风格是使用的CreateWindow 中MSDN提供的默认风格. 当然编辑框也有自己的风格.我们可以MSDN搜寻 EDIT styles 查看说明.

3.父窗口句柄. 因为这个是创建在父窗口的所以我们的父窗口句柄一定要填写.

4.实例句柄.这个必须要填写的.已经改成全局变量了.

5.编辑框的ID.编辑框的ID属于是控件ID. 这个位置在MSDN有说明. 如果创建的是父窗口.这个地方填写的则是菜单.也就是HMENU类型的.但是如果是子窗口.那么这个位置就变成了控件ID了.

具体可以查看MSDN说明. 这个控件ID很重要.关乎到我们处理消息.

2.创建按钮子窗口

上面创建了EDIT.那我们也可以创建按钮子窗口了.具体代码跟创建EDIT位置处一样.

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("设置"),

WS_CHILD | WS_VISIBLE, //

820, 30, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_ONE,

g_hInst,

nullptr);

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("获取"),

WS_CHILD | WS_VISIBLE, //

820, 80, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_TWO,

g_hInst,

nullptr);

这两行代码放到创建EDIT下面即可.

关于按钮的ID.我们设置一个自定义的整数值即可. 使用的时候需要强转为HMENU类型.

结果演示.

三丶响应子窗口的消息.

现在我们已经创建完毕子窗口了.那么我们想的是我要响应按钮消息什么的.

Windows虽然为每个子控件提供了消息处理函数. 也就是回调. 但是Windows为了让我们处理消息. 所以子窗口有一个特性. 就是说当改变状态的时候.会通知父窗口.

怎么理解.什么意思? 意思就是说.当我们点击这个按钮的时候.windows会发给我们父窗口一个消息. 我们只需要接受这个消息即可. 但是我们如何知道是哪个消息.?

既然我们知道了子窗口改变状态会发送消息.那么我们可以调试一下.打印一下消息.

也就是在我们父窗口的消息处理回调中打印一下消息. 使用DebugView查看.或者调试查看都可以.

因为当我们点击才会出现这个消息.那么我们可以看下这个消息是什么消息.

我们可以随便点击一个消息.查看定义.即可看到Windows全部的消息了. windows消息都放在了WinUser.h中

可以看到通知父窗口的是WM_COMMAND消息.

所以我们直接捕获这个消息进行处理即可.

查询MSDN 查询WM_COMMAND消息.

详细说明了.如果是WM_COMMAND消息. 那么参数三是控件ID. 还记得上面我们说的吗. 要给每个控件分配一个控件ID. 就是在这里使用的.

具体看参数就如上图所示. 告诉你了.低位才是 ID. 也就是 menu item标记. 所以我们需要取低位来判断. 因为WPARAME 是32位.所以低位是16位.

我们可以自己使用位运算取.也可以使用操作系统提供的 LOWORD 来取.

具体代码如下图所示. PS: 直接拷贝窗口回调函数了.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message)

{

case WM_CREATE:

{

CreateWindowW( //创建编辑框

TEXT("EDIT"),

TEXT("编辑框所处位置"),

WS_CHILD | WS_VISIBLE | WS_VSCROLL, //前3个是通用风格.EDIT风格搜索MSDN 搜索Edit Style即可.

10,10,800,400, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_EDIT_ONE,

g_hInst,

nullptr);

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("设置"),

WS_CHILD | WS_VISIBLE, //

820, 30, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_ONE,

g_hInst,

nullptr);

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("获取"),

WS_CHILD | WS_VISIBLE, //

820, 80, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_TWO,

g_hInst,

nullptr);

break;

}

case WM_COMMAND: //获取Command消息. 取出低位ID. 根据ID进行不同的操作.

{

int wmId = LOWORD(wParam);

// 分析菜单选择:

switch (wmId)

{

case IDM_EXIT:

DestroyWindow(hWnd);

break;

case IDC_MY_BUTTON_ONE: //按钮设置点击则回来

{

//这个ID是按钮设置的ID.所以当按钮设置就会来这里了.

SetDlgItemText(hWnd, IDC_MY_EDIT_ONE, TEXT("设置到编辑框的内容")); //此API时设置指定窗口中控件ID的显示名称.我们给编辑框设置.所以ID是编辑框的ID.

break;

}

case IDC_MY_BUTTON_TWO:

{

// ID同上所示

TCHAR str[1024] = { NULL };

GetDlgItemText(hWnd, IDC_MY_EDIT_ONE, str, sizeof(str)); //有设置就有获取. 获取就是需要提供缓冲区而已.然后Msg信息框弹出.

MessageBox(hWnd, str, NULL, NULL);

break;

}

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

}

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return DefWindowProc(hWnd, message, wParam, lParam);

}

最终实现结果.

点击设置后.编辑框的内容会改变.

点击获取后则会获取编辑框的内容.

四丶完整代码.

最后附上完整代码.拷贝就能使用. VS2015编写.不确定是否可以.不过可以参考代码.

代码如下:

// WindoS.cpp : 定义应用程序的入口点。

//

#include "stdafx.h"

#include

#include

#include "WindoS.h"

#define MAX_LOADSTRING 100

// 全局变量:

HINSTANCE g_hInst; // 当前实例

WCHAR szTitle[MAX_LOADSTRING] = TEXT("第一个我的窗口"); // 标题栏文本

WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow"); // 主窗口类名

#define IDC_MY_EDIT_ONE 10 //编辑框的ID 自己定义即可.

#define IDC_MY_BUTTON_ONE 11

#define IDC_MY_BUTTON_TWO 12

// 此代码模块中包含的函数的前向声明:

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,

_In_opt_ HINSTANCE hPrevInstance,

_In_ LPWSTR lpCmdLine,

_In_ int nCmdShow)

{

//1.自定义窗口样式

g_hInst = hInstance;

WNDCLASS wcex;

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc; //设置回调

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS));

wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS);

wcex.lpszClassName = szWindowClass;

//2.注册窗口类

BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW 扩展 RegisterClassExA /ExW

if (bRet == FALSE)

{

return 0;

}

//3.创建窗口 并且显示跟更新窗口

HWND hWnd = CreateWindowW(

szWindowClass,

szTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

0,

CW_USEDEFAULT,

0,

nullptr,

nullptr,

hInstance,

nullptr);

if (!hWnd)

{

return FALSE;

}

ShowWindow(hWnd, SW_SHOW);

UpdateWindow(hWnd);

//4.消息循环.

MSG msg;

/* 1参数是消息结构体.操作系统会往里面填写消息.

2 参数窗口句柄 因为每个线程可以有多个窗口.表示我要取那个窗口的消息

3.4 参数表示我要取这个窗口的那个消息. 后面三个参数属于过滤条件

*/

while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)

{

if (bRet == -1)

{

// handle the error and possibly exit

}

else

{

TranslateMessage(&msg); //键盘消息转换为小写.

DispatchMessage(&msg); //分发消息.将我们的消息传递给我们的回调函数处理.

}

}

return 0;

}

//

// 函数: 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:

{

CreateWindowW( //创建编辑框

TEXT("EDIT"),

TEXT("编辑框所处位置"),

WS_CHILD | WS_VISIBLE | WS_VSCROLL, //前3个是通用风格.EDIT风格搜索MSDN 搜索Edit Style即可.

10,10,800,400, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_EDIT_ONE,

g_hInst,

nullptr);

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("设置"),

WS_CHILD | WS_VISIBLE, //

820, 30, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_ONE,

g_hInst,

nullptr);

CreateWindowW( //创建按钮

TEXT("BUTTON"),

TEXT("获取"),

WS_CHILD | WS_VISIBLE, //

820, 80, 100, 40, //设置X Y 坐标.设置高度跟宽度.

hWnd, //父类句柄

(HMENU)IDC_MY_BUTTON_TWO,

g_hInst,

nullptr);

break;

}

case WM_COMMAND:

{

int wmId = LOWORD(wParam);

// 分析菜单选择:

switch (wmId)

{

case IDM_EXIT:

DestroyWindow(hWnd);

break;

case IDC_MY_BUTTON_ONE:

{

//这个ID是按钮设置的ID.所以当按钮设置就会来这里了.

SetDlgItemText(hWnd, IDC_MY_EDIT_ONE, TEXT("设置到编辑框的内容"));

break;

}

case IDC_MY_BUTTON_TWO:

{

// ID同上所示

TCHAR str[1024] = { NULL };

GetDlgItemText(hWnd, IDC_MY_EDIT_ONE, str, sizeof(str));

MessageBox(hWnd, str, NULL, NULL);

break;

}

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

}

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return DefWindowProc(hWnd, message, wParam, lParam);

}