一丶简介.什么是子窗口

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

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

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

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

 

二丶创建子窗口

1.创建EDIT子窗口

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

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

win32程序之子窗口编程

// 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;
}

win32程序之子窗口编程

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

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

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

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

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

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

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

2.创建按钮子窗口

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

win32程序之子窗口编程

     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);

win32程序之子窗口编程

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

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

 

结果演示.

win32程序之子窗口编程

 

 三丶响应子窗口的消息.

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

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

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

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

win32程序之子窗口编程

 

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

win32程序之子窗口编程

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

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

win32程序之子窗口编程

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

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

查询MSDN 查询WM_COMMAND消息.

win32程序之子窗口编程

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

win32程序之子窗口编程

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

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);

} 

最终实现结果.

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

win32程序之子窗口编程

 

 

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

win32程序之子窗口编程

四丶完整代码.

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

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

// 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(HWNDUINTWPARAMLPARAM);

 

 

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);

}

相关文章: