【第四节】windows sdk编程:windows 中的窗口
前言
在掌握了Windows编程框架后,我们可以在窗口回调函数中逐步实现消息处理。然而,通过向导生成的Windows窗口程序通常非常简单,而日常使用的Windows应用程序却形态各异。例如,有像任务管理器这样的普通窗口程序,有像游戏登录器这样的无边框程序,还有像电脑管家这样的简洁窗口,以及像Office这样的复杂窗口。这些窗口背后涉及哪些知识?本节将深入探讨窗口的相关内容。
一、窗口的概念
窗口(Window) 是Windows操作系统中用于显示信息和接收用户输入的基本单元。负责管理窗口相关功能的操作系统组件被称为 **窗口管理器(Window Manager)**。
在Windows操作系统初始化时,会创建一个名为 **桌面窗口(Desktop Window)** 的特殊窗口,它覆盖整个屏幕,所有其他窗口都在其之上显示。可以通过调用 `GetDesktopWindow` 函数获取桌面窗口的句柄。
二、窗口的标准风格
Windows中的窗口风格多种多样,其中最常见的三种类型是:
1. 重叠窗口(Overlapped Window)
2. 弹出窗口(Popup Window)
3. 子窗口(Child Window)
在创建窗口时(通过 `CreateWindowEx` 函数),可以通过指定以下窗口风格来定义窗口的类型:
- WS_OVERLAPPED:重叠窗口。
- WS_POPUP:弹出窗口。
- WS_CHILD:子窗口。
如果不指定任何窗口类型,默认创建的是重叠窗口。
2.1 重叠窗口
重叠窗口是顶级窗口,通常作为应用程序的主窗口。它具有以下特点:
- 包含标题栏、边框和客户区。
- 可以包含窗口菜单、最小化/最大化按钮和滚动条。
- 通过 `CreateWindowEx` 函数指定 `WS_OVERLAPPED` 或 `WS_OVERLAPPEDWINDOW` 风格来创建。
- `WS_OVERLAPPED`:窗口具有标题栏和边框。
- `WS_OVERLAPPEDWINDOW`:窗口具有标题栏、边框、窗口菜单、最小化/最大化按钮。
在VS IDE中使用向导创建的默认Win32窗口程序就是重叠窗口的典型示例。
2.2 弹出窗口
弹出窗口也是顶级窗口,通常用于对话框或消息框。它具有以下特点:
- 具有 `WS_POPUP` 风格,隐含带有 `WS_CLIPSIBLINGS` 风格。
- 通常用于显示对话框或消息框。例如,`MessageBox` 函数创建的信息框就是弹出窗口。
在默认的Win32窗口程序中,关于菜单中显示的对话框就是具有 `WS_POPUP` 风格的弹出窗口。
2.3 子窗口
子窗口具有以下特点:
- 具有 `WS_CHILD` 风格,且必须分配在父窗口的客户区内。
- 父窗口可以是重叠窗口、弹出窗口或其他子窗口。
- 通过 `CreateWindowEx` 函数创建,并指定父窗口句柄。
例如,在默认的Win32窗口程序中,可以通过以下代码创建一个子窗口:
case IDM_ABOUT:
CreateWindowEx(NULL, szWindowClass, TEXT("你好"),
WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS,
30, 30, 400, 300, hWnd, NULL, hInst, NULL);
子窗口无法移出父窗口 的客户区
三、窗口的扩展风格
除了标准风格外,窗口还可以通过 **扩展风格** 实现更复杂的外观和行为。扩展风格需要通过 `CreateWindowEx` 函数设置。以下是一些常用的扩展风格:
四、 窗口的层次结构
窗口管理器以树状结构组织和管理系统中的所有窗口。树状结构的根是 **桌面窗口**,其下属第一层是 **顶层窗口**,顶层窗口之下是 **子窗口**。
通过一系列API函数,可以遍历系统中的所有窗口。例如,以下代码展示了如何遍历桌面窗口的所有子窗口:
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[]) {
// 获取桌面窗口句柄
HWND hWnd = GetDesktopWindow();
// 获取第一个子窗口
hWnd = GetWindow(hWnd, GW_CHILD);
char szName = {0};
// 循环遍历所有子窗口
while (hWnd != NULL) {
memset(szName, 0, 266);
GetWindowTextA(hWnd, szName, 266);
printf("%s\n", szName);
hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
}
return 0;
}
代码中用到的函数包括:
- `GetDesktopWindow`:获取桌面窗口句柄。
- `GetWindow`:获取与指定窗口有特定关系(如子窗口)的窗口句柄。
- `memset`:将指定缓冲区初始化为指定值。
- `GetWindowTextA`:获取指定窗口的标题。
- `GetNextWindow`:获取Z-Order顺序中的下一个窗口句柄。
五、Z-Order的作用
Z-Order 是指窗口在屏幕上显示的前后顺序。在子窗口链中,位置越靠前的窗口在屏幕上显示时也越靠前。可以通过 `SetWindowPos` 函数改变窗口的Z-Order顺序。
子窗口相互覆盖示意图(无WS_CLIPSIBLINGS风格)
如果想使Z-Order在前的子窗口显示时也在前(覆盖Z-Order在后的子窗口),需要使用WS_CLIPSIBLINGS窗口风格 。
子窗口相互覆盖示意图(有WS_CLIPSIBLINGS风格)
以下是关于 Z-Order 的详细说明:
1. Z-Order 的基本概念
- 子窗口链:窗口在子窗口链中的位置决定了其显示顺序,位置越靠前的窗口显示时也越靠前。
- 顶层窗口:Z-Order 在前的顶层窗口会遮挡 Z-Order 在后的顶层窗口。
- 刷新顺序:当屏幕上的一块区域需要刷新(Update)时,同一个子窗口链中 Z-Order 在前的窗口会先刷新,Z-Order 在后的窗口后刷新。
- 父/子窗口:对于有父子关系的窗口,父窗口会先刷新,子窗口后刷新。
2. 顶层窗口的 Z-Order
- 生成时的处理:当顶层窗口生成时,窗口管理器会将其添加到桌面窗口的子窗口链的最前面,即 Z-Order 的最前面,以确保整个窗口都可见。
- 遮挡关系:Z-Order 在前的顶层窗口会遮挡 Z-Order 在后的顶层窗口。
- 调整 Z-Order:可以通过 `SetWindowPos` 函数动态调整窗口的 Z-Order。
3. 子窗口的 Z-Order
- 与父窗口的关系:子窗口的 Z-Order 高于其父窗口,因此子窗口会显示在父窗口的前面。
- 与兄弟窗口的关系:子窗口的 Z-Order 不会超过其父窗口的 Z-Order 更靠前的兄弟窗口。
- 生成时的处理:与顶层窗口不同,子窗口生成时,窗口管理器会将其添加到父窗口的子窗口链的最后面。这一设计看似反直觉,但实际上有其原因:
- 显示效果:在兄弟窗口间有重叠的情况下,后生成的窗口能够显示在前面。
- Tab-Order:子窗口间的 Tab-Order 与窗口的生成顺序一致,这样的设计更符合用户的操作直觉。
4. 刷新与绘画机制
- 共用显示 DC:子窗口大多数情况下共用其父窗口的显示 DC(Device Context)。
- 刷新顺序的影响:在刷新时,子窗口可以在其兄弟窗口的客户区上进行绘制。由于 Z-Order 在后的子窗口刷新顺序在后,其绘制内容可能会覆盖 Z-Order 在前的窗口,从而造成显示效果上后生成的窗口反而在前的现象。
5. 总结
- Z-Order 决定了窗口的显示和刷新顺序,是窗口管理中的重要概念。
- 窗口管理器通过将顶层窗口添加到子窗口链的最前面,确保其可见性。
- 子窗口生成的顺序和刷新机制使得后生成的窗口能够显示在前面,同时保持 Tab-Order 的合理性。
- 通过 `SetWindowPos` 函数,开发者可以灵活调整窗口的 Z-Order,以满足特定的显示需求。
六、总结
窗口是Windows应用程序的核心组成部分,理解窗口的概念、风格、层次结构以及Z-Order顺序,对于开发复杂的Windows应用程序至关重要。通过合理使用窗口的标准风格和扩展风格,开发者可以创建出功能丰富、界面美观的应用程序。