今天, 我们要学习的就是 子窗口控件 的使用。
在其他一些 Windows应用软件上我们经常能够看到一些大致相同的按钮、复选框、组合框、列表框等控件, 这些控件很有可能就是使用 标准子窗口控件 来实现的。
子窗口的创建
在讲解 “标准子窗口控件” 的使用之前我们首先应该知道如何去创建一个子窗口, 因为这些 “子窗口控件” 实际上都是通过创建一个子窗口的形式来进行创建的, 因此我们应该把理解的重点放在 “子窗口” 上, 而不是 “控件” 上。
子窗口的创建可以将整个客户区划分为多个矩形区域, 并且每个子窗口都可以有自己的句柄、窗口过程和客户区, 每个子窗口过程只接收与自身窗口有关的鼠标消息、鼠标消息的参数 lParam 中包含的坐标是相对于 子窗口 客户区的左上角的。简单的说, 子窗口具有一个普通窗口的一切特性。
子窗口的创建同样是使用 CreateWindow 函数进行创建的, 下面我们通过一个示例来认识这个过程: 这段代码演示的是在父窗口的(10, 10)位置处创建一个大小为 200x200 的子窗口, 效果如下:
首先从整体上把握下这段代码: 第一步: 声明父窗口回调函数和子窗口的回调函数:
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;
LRESULT CALLBACK ChildWndProc( HWND, UINT, WPARAM, LPARAM ) ;
//子窗口窗口过程
第二步: 定义WinMain函数, 在WinMain函数中建立父窗口的 wndclass 窗口类并注册:
复制代码
if( !RegisterClass(&wndclass) )
{
MessageBox( NULL, TEXT("无法注册窗口类!"), szAppName, MB_OK | MB_ICONERROR ) ;
return 0 ;
}
第三步: 改变父窗口 wndclass 类中的部分属性, 使其成为子窗口的 wndclass, 并注册子窗口窗口类:
wndclass.lpszClassName = szChildClass ; wndclass.cbWndExtra = sizeof(long) ; wndclass.lpfnWndProc = ChildWndProc ; RegisterClass( &wndclass ) ; //注册子窗口窗口类
第四步: 显示窗口、进入消息循环:
ShowWindow( hwnd, iCmdShow ) ;
UpdateWindow( hwnd ) ;
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage( &msg ) ;
DispatchMessage( &msg ) ;
}
第五步: 定义父窗口回调函数 WndProc, 在处理 WM_CREATE 消息时创建子窗口:
复制代码
case WM_CREATE:
childHwnd = CreateWindow( szChildClass, TEXT("子窗口"),
WS_CHILDWINDOW | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
10, 10,
200, 200,
hwnd,
(HMENU)1,
(HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE ),
NULL ) ;
return 0 ;
第六步: 定义子窗口回调函数 ChildWndProc:
LRESULT CALLBACK ChildWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_LBUTTONDOWN:
RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT );
MessageBox( hwnd, TEXT("你在子窗口客户区按下了鼠标左键!"), TEXT("子窗口消息"), MB_OK );
return 0 ;
}
return DefWindowProc( hwnd, message, wParam, lParam ) ;
}
一些细节把握:
1>. 全局的子窗口类名 在第四行中的代码 TCHAR szChildClass[] = TEXT(“ChildClass”) ; 我们把子窗口类名称设为全局这是因为在 WinMain 函数中和父窗口的回调函数中都需要用到它的名字。
2>. 在注册子窗口的窗口类时, 没有再重新定义一个 WNDCLASS 类型的变量, 而是简单的复用了一下父窗口中的 wndclass, 其中有3个成员与父窗口不同: lpszClassName, 即子窗口类的名称 ; cbWndExtra 被设置成一个 long 型数据所占的存储单元大小(4字节), 这个成员通知 Windows 在内部结构中给基于这个窗口类的每个窗口预留4个字节的额外存储空间, 以用来给用户为每个窗口保存不同的信息 ; lPfnWndProc 成员被设置成 ChildWndProc, 表示子窗口类的窗口过程 。
3>. 创建子窗口时的 (HINSTANCE) GetWindowLong(hwnd, RDW_ERASE ) 实际上 CreateWindow 函数在创建子窗口时需要 hInstance 句柄, 而GetWindowLong函数的作用就是获得有关指定窗口的信息, 函数也获得在额外窗口内存中指定偏移位地址的32位度整型值, GetWindowLong的原型: LONG GetWindowLong( HWND hwnd, int nIndex ) ; nIndex 为索引值, 它可以是以下标识符之一:
GWL_EXSTYLE
GWL_STYLE
GWL_WNDPROC
GWL_HINSTANCE
GWL_HWNDPARENT
GWL_ID
GWL_USERDATA
DWL_DLGPROC
DWL_MSGRESULT
DWL_USER
在子窗口的窗口过程的处理上, 我们仅仅处理了一个 WM_LBUTTONDWON 消息, 当鼠标在子窗口客户区中按下左键时就弹出一个对话框告诉用户"鼠标左键在子窗口客户区中按下!"。
这些就是创建一个子窗口的演示。使用子窗口的程序设计有助于程序的结构化和模式化, 提高代码的复用率。
如果你已经将上面示例中的代码理解的话, 那么恭喜你! 你已经又向成功迈向了一大步, 因为标准子窗口控件的使用比我们自己创建子窗口还要简单!
代码实例
#define _CRT_SECURE_NO_WARNINGS 1
#include <windows.h>
#include<strsafe.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWndProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT("ChildClass");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("CreateChildWindow");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
HBRUSH hCulorBrush = CreateSolidBrush(RGB(200, 200, 200));
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = hCulorBrush;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("这个程序需要在Windows系统执行!"), szAppName, MB_ICONERROR);
return 0;
}
wndclass.lpszClassName = szChildClass;
wndclass.cbWndExtra = sizeof(long);
wndclass.lpfnWndProc = ChildWndProc;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName,
TEXT("中年人学C语言Windows程序设计--子窗口演示"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static TCHAR szBuffer[128];
static int cxChar, cyChar, cxClient, cyClient;
static HWND childHwnd;
switch (message)
{
case WM_CREATE:
childHwnd = CreateWindow(szChildClass, TEXT("子窗口"),
WS_CHILDWINDOW | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
10, 10,
200, 200,
hWnd,
(HMENU)1,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND btnHwnd;
switch (message)
{
case WM_LBUTTONDOWN:
RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT);
MessageBox(hwnd, TEXT("你在在子窗口客户区中按下鼠标左键!"), TEXT("子窗口消息"), MB_OK);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
效果实例
在子窗口中点击鼠标左键,跳出对话框。
|