win32程序之子窗口编程

             

一丶简介.什么是子窗口

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

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

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

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

二丶创建子窗口

1.创建EDIT子窗口

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

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

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

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#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 <stdio.h>
#include <stdlib.h>
#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);
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏c#开发者

datagrid资料+ by iCeSnaker - Program rhapsody

datagrid资料+ by iCeSnaker - Program rhapsody 关于datagrid的打印 http://www.chinaaspx.c...

37090
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(43)-工作流设计-字段分类设计

建立好42节的表之后,每个字段英文表示都是有意义的说明。先建立,就知道表的关系和用处了,当然,我的设计只是一个参考,你可能有很多改进的地方。 我们的工作流具体细...

25380
来自专栏pangguoming

c#以POST方式模拟提交表单

这是我一年前写的一个用C#模拟以POST方式提交表单的代码,现在记录在下面,以免忘记咯。那时候刚学C#~忽忽。。很生疏。。代码看上去也很幼稚 臃肿不堪 #reg...

35990
来自专栏我和未来有约会

从Flash到Silverlight进阶教程-用代码来创建动画

从Flash到Silverlight进阶教程 用代码来创建动画 这节里将要讲述一个自定义用户控件最基本的操作,就好象Flash中的MovieClips一样,动态...

38150
来自专栏施炯的IoT开发专栏

Windows Phone Developer Tools RTW 新特性-Pivot控件

    IE9 Beta发布了,Windows Phone Developer Tools RTW也来了,开发者和发烧友又有得忙活了。园子里好多朋友已经开始了研...

19560
来自专栏葡萄城控件技术团队

Xamarin.Forms入门-使用 Xamarin.Forms 来创建跨平台的用户界面

Xamarin.Forms 是一个跨平台的、基于原生控件的UI工具包,开发人员可以轻松的创建适用于 Android,iOS 以及 Windows Phone的用...

50670
来自专栏林德熙的博客

win10 uwp 改变鼠标 设置光标移动鼠标

经常在应用需要修改光标,显示点击、显示输入,但是有些元素不是系统的,那么如何设置鼠标? 本文主要:UWP 设置光标,UWP 移动鼠标

16210
来自专栏葡萄城控件技术团队

如何将GridViewEX升级到UWP(Universal Windows Platform)平台

引言 上一篇文章中,我们主要讲解了如何在保证GridView控件的用户体验基础上,扩展GridView生成GridViewEx控件,增加动态添加新分组功能等,本...

23180
来自专栏前端知识分享

第159天:前端知识体系框架

Web Components:http://css-tricks.com/modular-future-web-components//

15030
来自专栏学海无涯

iOS开发之XLForm的使用

在iOS开发中,开发"表单"界面,字段稍微多一点的一般都用UITableView来做,而XLForm就是这样一个框架,它是创建动态表格视图最牛逼的iOS库, 用...

47480

扫码关注云+社区

领取腾讯云代金券