窗口功能
本概述讨论窗口的功能,如窗口类型、状态、大小和位置。
窗口类型
本部分包含以下描述窗口类型的主题。
重叠的 Windows
重叠窗口 是具有标题栏、边框和工作区的顶级窗口(非子窗口);它旨在用作应用程序的主窗口。 它还可以具有窗口菜单、最小化和最大化按钮以及滚动条。 用作主窗口的重叠窗口通常包括所有这些组件。
通过在 CreateWindowEx 函数中指定 WS_OVERLAPPED 或 WS_OVERLAPPEDWINDOW 样式,应用程序将创建重叠的窗口。 如果使用 WS_OVERLAPPED 样式,窗口具有标题栏和边框。 如果使用 WS_OVERLAPPEDWINDOW 样式,窗口具有标题栏、大小调整边框、窗口菜单以及最小化和最大化按钮。
弹出窗口
弹出窗口 是一种特殊类型的重叠窗口,用于显示在应用程序主窗口外部的对话框、消息框和其他临时窗口。 标题栏对于弹出窗口是可选的;否则,弹出窗口与 WS_OVERLAPPED 样式的重叠窗口相同。
通过在 CreateWindowEx 中指定 WS_POPUP 样式来创建弹出窗口。 若要包含标题栏,请指定 WS_CAPTION 样式。 使用 WS_POPUPWINDOW 样式创建具有边框和窗口菜单的弹出窗口。 WS_CAPTION 样式必须与 WS_POPUPWINDOW 样式相结合,以使窗口菜单可见。
子 Windows
子窗口 具有 WS_CHILD 样式,并且仅限于其父窗口的工作区。 应用程序通常使用子窗口将父窗口的工作区划分为功能区域。 通过在 createWindowEx函数中指定WS_CHILD 样式来创建子窗口。
子窗口必须具有父窗口。 父窗口可以是重叠窗口、弹出窗口,甚至是另一个子窗口。 调用 CreateWindowEx时指定父窗口。 如果在 createWindowEx 中指定 WS_CHILD 样式,但未指定父窗口,则系统不会创建该窗口。
子窗口具有工作区,但没有其他功能,除非显式请求它们。 应用程序可以请求标题栏、窗口菜单、最小化和最大化按钮、子窗口的边框和滚动条,但子窗口不能有菜单。 如果应用程序指定菜单句柄,则当它注册子窗口类或创建子窗口时,将忽略菜单句柄。 如果未指定边框样式,系统将创建无边框窗口。 应用程序可以使用无边框子窗口来划分父窗口的工作区,同时使分区对用户不可见。
本部分讨论子窗口的以下方面:
定位
系统始终将子窗口相对于其父窗口工作区左上角的位置。 子窗口的一部分从未出现在其父窗口的边框之外。 如果应用程序创建大于父窗口或定位子窗口的子窗口,以便部分或所有子窗口超出父窗口的边框,则系统会剪辑子窗口;也就是说,不会显示父窗口工作区外部的部分。 影响父窗口的作也可能会影响子窗口,如下所示。
父窗口 | 子窗口 |
---|---|
摧毁 | 在销毁父窗口之前销毁。 |
隐藏 | 隐藏父窗口之前隐藏。 仅当父窗口可见时,子窗口才可见。 |
搬 | 随父窗口的工作区一起移动。 子窗口负责在移动后绘制其工作区。 |
显示 | 显示父窗口后显示。 |
裁剪
系统不会从父窗口的工作区自动剪辑子窗口。 这意味着,如果父窗口执行与子窗口位于同一位置的任何绘图,则父窗口在子窗口上绘制。 但是,如果父窗口具有 WS_CLIPCHILDREN 样式,系统将从父窗口的工作区中剪辑子窗口。 如果剪裁了子窗口,则父窗口无法对其进行绘制。
子窗口可以重叠同一工作区中的其他子窗口。 与一个或多个其他子窗口共享同一个父窗口的子窗口称为 同级窗口。 同级窗口可以在彼此的工作区中绘制,除非其中一个子窗口具有 WS_CLIPSIBLINGS 样式。 如果子窗口确实具有此样式,则会剪裁位于子窗口内的其同级窗口的任何部分。
如果窗口具有 WS_CLIPCHILDREN 或 WS_CLIPSIBLINGS 样式,则性能会略有损失。 每个窗口占用系统资源,因此应用程序不应不分青红皂白地使用子窗口。 为了获得最佳性能,需要在主窗口的窗口过程中(而不是使用子窗口)逻辑划分主窗口的应用程序应执行此作。
与父窗口的关系
应用程序可以通过调用 SetParent 函数来更改现有子窗口的父窗口。 在这种情况下,系统会从旧父窗口的工作区中删除子窗口,并将其移动到新父窗口的工作区。 如果 SetParent 指定 NULL 句柄,桌面窗口将成为新的父窗口。 在这种情况下,子窗口在桌面上绘制,位于任何其他窗口的边框之外。 GetParent 函数检索子窗口父窗口的句柄。
父窗口将其工作区的一部分放弃给子窗口,子窗口接收来自该区域的所有输入。 对于父窗口的每个子窗口,窗口类不需要相同。 这意味着应用程序可以使用看起来不同的子窗口填充父窗口并执行不同的任务。 例如,对话框可以包含许多类型的控件,每个控件都是接受用户不同类型的数据的子窗口。
子窗口只有一个父窗口,但父窗口可以具有任意数量的子窗口。 每个子窗口反过来可以有子窗口。 在此窗口链中,每个子窗口称为原始父窗口的后代窗口。 应用程序使用 IsChild 函数来发现给定窗口是子窗口还是给定父窗口的子窗口。
EnumChildWindows 函数枚举父窗口的子窗口。 然后,EnumChildWindows 将句柄传递给应用程序定义的回调函数。 还会枚举给定父窗口的子代窗口。
消息
系统将子窗口的输入消息直接传递到子窗口;消息不会通过父窗口传递。 唯一的例外是,EnableWindow 函数禁用了子窗口。 在这种情况下,系统会将任何输入消息传递给子窗口,转而传递到父窗口。 这样,父窗口便可以检查输入消息并在必要时启用子窗口。
子窗口可以具有唯一的整数标识符。 使用控件窗口时,子窗口标识符非常重要。 应用程序通过向其发送消息来指示控件的活动。 应用程序使用控件的子窗口标识符将消息定向到控件。 此外,控件还会将通知消息发送到其父窗口。 通知消息包括控件的子窗口标识符,父级使用该标识符来标识发送消息的控件。 应用程序通过将 CreateWindowEx 函数的 hMenu 参数设置为值而不是菜单句柄来指定其他类型的子窗口标识符。
分层 Windows
使用分层窗口可以显著提高具有复杂形状的窗口的性能和视觉效果,对形状进行动画处理,或者希望使用 alpha 混合效果。 系统会自动组合和重新修补分层窗口和基础应用程序的窗口。 因此,分层窗口会平滑呈现,而不会闪烁复杂窗口区域的典型效果。 此外,分层窗口可以是半透明窗口,即 alpha 混合。
若要创建分层窗口,请在调用 CreateWindowEx 函数时指定 WS_EX_LAYERED 扩展窗口样式,或在创建窗口后调用 SetWindowLong 函数来设置 WS_EX_LAYERED。 CreateWindowEx 调用后,在为此窗口调用 SetLayeredWindowAttributes 或 UpdateLayeredWindow 函数之前,分层窗口将不可见。
注意
从 Windows 8 开始,WS_EX_LAYERED 可用于子窗口和顶级窗口。 以前的 Windows 版本仅支持顶级窗口 WS_EX_LAYERED。
若要设置给定分层窗口的不透明度级别或透明度颜色键,请调用 SetLayeredWindowAttributes。 调用后,系统可能仍要求窗口在显示或调整其大小时绘制窗口。 但是,由于系统存储分层窗口的图像,因此系统不会要求窗口绘制其部分是否由于相对窗口在桌面上移动而显示。 如果旧版应用程序想要为窗口添加半透明或透明度效果,则不需要重新构造其绘制代码,因为系统将调用 SetLayeredWindowAttributes 的窗口的绘制重定向到屏幕外内存中,并重新编译它以实现所需的效果。
若要获得更快、更高效的动画,或者如果需要每像素 alpha,请调用 UpdateLayeredWindow。 UpdateLayeredWindow 主要用于应用程序必须直接提供分层窗口的形状和内容,而无需使用系统通过 SetLayeredWindowAttributes提供的重定向机制。 此外,使用 UpdateLayeredWindow 更有效地使用内存,因为系统不需要存储重定向窗口映像所需的额外内存。 为提高窗口动画效果,请调用 UpdateLayeredWindow 以更改分层窗口的位置和大小。 请注意,已调用 SetLayeredWindowAttributes 后,后续的 UpdateLayeredWindow 调用将失败,直到清除分层样式位并再次设置。
分层窗口的命中测试基于窗口的形状和透明度。 这意味着窗口的区域是颜色键,或者其 alpha 值为零的区域将允许鼠标消息通过。 但是,如果分层窗口具有 WS_EX_TRANSPARENT 扩展窗口样式,则会忽略分层窗口的形状,并将鼠标事件传递到分层窗口下的其他窗口。
Message-Only Windows
消息窗口 使你能够发送和接收消息。 它不可见,没有 z 顺序,无法枚举,也不会接收广播消息。 窗口只是调度消息。
若要创建仅消息窗口,请在 createWindowEx函数的 hWndParent参数中指定 HWND_MESSAGE 常量或现有仅消息窗口的句柄。 还可以通过在 setParent函数的 hWndNewParent参数中指定HWND_MESSAGE,将现有窗口更改为仅消息窗口。
若要查找仅消息窗口,请在 FindWindowEx 函数的 hwndParent 参数中指定 HWND_MESSAGE。 此外,如果 hwndParent 和 hwndChildAfter 参数都 NULL,则 FindWindowEx 仅搜索消息窗口和顶级窗口。
窗口关系
窗口可以与用户或其他窗口关联很多。 窗口可以是拥有的窗口、前景窗口或背景窗口。 窗口还具有相对于其他窗口的 z 顺序。 有关详细信息,请参阅以下主题:
前台和后台窗口
每个进程可以有多个执行线程,每个线程都可以创建窗口。 创建用户当前正在使用的窗口的线程称为前台线程,该窗口称为 前台窗口。 所有其他线程都是后台线程,后台线程创建的窗口称为后台窗口 。
每个线程都有一个优先级,用于确定线程接收的 CPU 时间量。 尽管应用程序可以设置其线程的优先级级别,但前台线程的优先级通常略高于后台线程。 由于优先级较高,前台线程接收的 CPU 时间比后台线程多。 前台线程的正常基优先级为 9;后台线程的正常基优先级为 7。
用户通过单击窗口或使用 ALT+TAB 或 ALT+ESC 组合键设置前台窗口。 若要检索前台窗口的句柄,请使用 GetForegroundWindow 函数。 若要检查应用程序窗口是否为前台窗口,请将 GetForegroundWind ow 返回的句柄与应用程序窗口的句柄进行比较。
应用程序使用 SetForegroundWindow 函数设置前台窗口。
系统限制哪些进程可以设置前台窗口。 仅当以下值时,进程才能设置前台窗口:
- 以下所有条件均为 true:
- 调用 SetForegroundWindow 的过程属于桌面应用程序,而不是针对 Windows 8 或 8.1 设计的 UWP 应用或 Windows 应用商店应用。
- 前台进程尚未禁用对 SetForegroundWindow 调用 LockSetForegroundWindow 函数的调用。
- 前台锁定超时已过期(请参阅 SystemParametersInfo中的SPI_GETFOREGROUNDLOCKTIMEOUT)。
- 没有处于活动状态的菜单。
- 此外,至少满足以下条件之一:
- 调用进程是前台进程。
- 调用进程由前台进程启动。
- 当前没有前台窗口,因此没有前台进程。
- 调用进程收到最后一个输入事件。
- 正在调试前台进程或调用进程。
即使它满足这些条件,进程也有可能被拒绝设置前台窗口的权利。
可以设置前台窗口的进程可以通过调用 AllowSetForegroundWindow 函数或调用具有 BSF_ALLOWSFW 标志的 BroadcastSystemMessage 函数来设置前台窗口。 前台进程可以通过调用 LockSetForegroundWindow 函数来禁用 对 setForegroundWindow 的调用。
拥有的 Windows
重叠或弹出窗口可以由另一个重叠或弹出窗口拥有。 拥有将多个约束置于窗口上。
- 拥有的窗口始终高于其所有者的 z 顺序。
- 系统在所有者被销毁时自动销毁拥有的窗口。
- 当所有者最小化时,将隐藏拥有的窗口。
只有重叠窗口或弹出窗口才能成为所有者窗口;子窗口不能是所有者窗口。 应用程序通过在创建具有 WS_OVERLAPPED 或 WS_POPUP 样式的窗口时,将所有者的窗口句柄指定为 CreateWindowEx 的 hwndParent 参数来创建拥有的窗口。 hwndParent 参数必须标识重叠或弹出窗口。 如果 hwndParent 标识子窗口,则系统将所有权分配给子窗口的顶级父窗口。 创建拥有的窗口后,应用程序无法将窗口的所有权转移到另一个窗口。
默认情况下,对话框和消息框是拥有的窗口。 应用程序在调用创建对话框或消息框的函数时指定所有者窗口。
应用程序可以使用具有 GW_OWNER 标志的 GetWindow 函数来检索窗口所有者的句柄。
Z-Order
窗口 z 顺序 指示窗口在重叠窗口堆栈中的位置。 此窗口堆栈沿虚轴(z 轴)方向方向,从屏幕向外延伸。 z 顺序顶部的窗口与所有其他窗口重叠。 z 顺序底部的窗口与其他所有窗口重叠。
系统在单个列表中维护 z 顺序。 它根据窗口是最顶层的窗口、顶级窗口还是子窗口,将窗口添加到 z 顺序。 最顶层的窗口 与所有其他非顶部窗口重叠,无论它是活动窗口还是前台窗口。 最顶部的窗口具有 WS_EX_TOPMOST 样式。 所有最顶层的窗口都以 z 顺序显示在任何最顶层的窗口之前。 子窗口按 z 顺序将其父窗口分组。
当应用程序创建窗口时,系统会将其置于相同类型的窗口的 z 顺序顶部。 可以使用 BringWindowToTop 函数将窗口置于同一类型的窗口的 z 顺序顶部。 可以使用 SetWindowPos 和 DeferWindowPos 函数重新排列 z 顺序。
用户通过激活其他窗口来更改 z 顺序。 系统将活动窗口放置在相同类型的窗口 z 顺序的顶部。 当窗口位于 z 顺序的顶部时,其子窗口也是如此。 可以使用 GetTopWindow 函数搜索父窗口的所有子窗口,并将句柄返回到 z 顺序最高的子窗口。 GetNextWindow 函数按 z 顺序检索下一个或上一个窗口的句柄。
窗口显示状态
在任何给定时间,窗口可能处于活动状态或处于非活动状态;隐藏或可见;并最小化、最大化或还原。 这些品质统称为 窗口显示状态。 以下主题讨论窗口显示状态:
活动窗口
活动窗口 是用户当前正在使用的应用程序的顶级窗口。 为了允许用户轻松识别活动窗口,系统将其置于 z 顺序的顶部,并将标题栏的颜色和边框更改为系统定义的活动窗口颜色。 只有顶级窗口可以是活动窗口。 当用户使用子窗口时,系统将激活与子窗口关联的顶级父窗口。
系统中一次只有一个顶级窗口处于活动状态。 用户通过单击顶层窗口(或其子窗口之一)或使用 ALT+ESC 或 ALT+TAB 键组合激活顶级窗口。 应用程序通过调用 SetActiveWindow 函数来激活顶级窗口。 其他函数可能导致系统激活不同的顶级窗口,包括 SetWindowPos、DeferWindowPos、SetWindowPlacement,以及 DestroyWindow。 尽管应用程序可以随时激活不同的顶级窗口,但为了避免混淆用户,它只应在响应用户作时执行此作。 应用程序使用 GetActiveWindow 函数检索活动窗口的句柄。
当激活从一个应用程序的顶级窗口更改为另一个应用程序的顶级窗口时,系统会向两个应用程序发送 WM_ACTIVATEAPP 消息,并通知他们更改。 当激活更改到同一应用程序中的不同顶级窗口时,系统将这两个窗口发送一条 WM_ACTIVATE 消息。
已禁用的 Windows
可以禁用窗口。 禁用的窗口 不会从用户接收键盘或鼠标输入,但它可以从其他窗口、其他应用程序以及系统接收消息。 应用程序通常禁用窗口以防止用户使用该窗口。 例如,应用程序可能会禁用对话框中的推送按钮,以防止用户选择它。 应用程序可以随时启用禁用的窗口;启用窗口可还原正常输入。
默认情况下,创建时会启用窗口。 但是,应用程序可以指定 WS_DISABLED 样式来禁用新窗口。 应用程序通过使用 EnableWindow 函数启用或禁用现有窗口。 当启用窗口的状态即将更改时,系统会向窗口发送 WM_ENABLE 消息。 应用程序可以使用 IsWindowEnabled 函数来确定是否启用了窗口。
禁用子窗口时,系统将子窗口的鼠标输入消息传递到父窗口。 父级使用消息来确定是否启用子窗口。 有关详细信息,请参阅 鼠标输入。
一次只能有一个窗口接收键盘输入;据说该窗口具有键盘焦点。 如果应用程序使用 EnableWindow 函数禁用键盘焦点窗口,则窗口除了禁用外,还会丢失键盘焦点。 EnableWindow 然后将键盘焦点设置为 NULL,这意味着没有窗口具有焦点。 如果子窗口或其他后代窗口具有键盘焦点,则禁用父窗口时后代窗口将失去焦点。 有关详细信息,请参阅 键盘输入。
窗口可见性
窗口可以是可见的,也可以是隐藏的。 系统在屏幕上显示 可见窗口。 它通过不绘制隐藏窗口来隐藏 隐藏窗口。 如果窗口可见,用户可以向窗口提供输入并查看窗口的输出。 如果窗口处于隐藏状态,则会有效地禁用该窗口。 隐藏窗口可以处理来自系统或其他窗口的消息,但无法处理来自用户或显示输出的输入。 应用程序在创建窗口时设置窗口的可见性状态。 稍后,应用程序可以更改可见性状态。
为窗口设置 WS_VISIBLE 样式时,窗口可见。 默认情况下,CreateWindowEx 函数将创建隐藏窗口,除非应用程序指定 WS_VISIBLE 样式。 通常,应用程序在创建窗口后设置 WS_VISIBLE 样式,以保留用户隐藏创建过程的详细信息。 例如,当应用程序自定义窗口的外观时,应用程序可能会隐藏新窗口。 如果在 createWindowEx 中指定了 WS_VISIBLE 样式,则系统会在创建窗口后将 WM_SHOWWINDOW 消息发送到窗口,但在显示窗口之前。
应用程序可以使用 IsWindowVisible 函数来确定窗口是否可见。 应用程序可以使用 ShowWindow、SetWindowPos、DeferWindowPos或 SetWindowPlacement 或 setWindowLong 函数来显示或隐藏窗口。 这些函数通过设置或删除窗口的 WS_VISIBLE 样式来显示或隐藏窗口。 他们还将 WM_SHOWWINDOW 消息发送到窗口,然后再显示或隐藏它。
当所有者窗口最小化时,系统会自动隐藏关联的拥有窗口。 同样,还原所有者窗口时,系统会自动显示关联的拥有窗口。 在这两种情况下,系统将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 有时,应用程序可能需要隐藏拥有的窗口,而无需最小化或隐藏所有者。 在这种情况下,应用程序使用 ShowOwnedPopups 函数。 此函数设置或删除所有拥有窗口的 WS_VISIBLE 样式,并将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 隐藏所有者窗口不会影响拥有窗口的可见性状态。
当父窗口可见时,其关联的子窗口也可见。 同样,当父窗口处于隐藏状态时,其子窗口也处于隐藏状态。 最小化父窗口对子窗口的可见性状态没有影响;也就是说,子窗口与父窗口一起最小化,但不会更改 WS_VISIBLE 样式。
即使窗口具有 WS_VISIBLE 样式,用户可能无法在屏幕上看到窗口;其他窗口可能完全重叠,或者它可能已移动到屏幕边缘之外。 此外,可见的子窗口受其父子关系建立的剪辑规则的约束。 如果窗口的父窗口不可见,它也不会可见。 如果父窗口移动到屏幕边缘之外,则子窗口也会移动,因为相对于父窗口的左上角绘制了子窗口。 例如,用户可能会将包含子窗口的父窗口移动到屏幕边缘足够远的父窗口,用户可能无法看到子窗口,即使子窗口及其父窗口都具有 WS_VISIBLE 样式。
最小化、最大化和还原的 Windows
最大化窗口 是具有 WS_MAXIMIZE 样式的窗口。 默认情况下,系统会放大最大化窗口,使其填满屏幕,或者,如果子窗口是父窗口的工作区,则为该窗口。 虽然窗口的大小可以设置为最大化窗口的大小相同,但最大化窗口略有不同。 系统会自动将窗口的标题栏移动到屏幕顶部或父窗口工作区顶部。 此外,系统禁用窗口的大小调整边框和标题栏的窗口定位功能(以便用户无法通过拖动标题栏移动窗口)。
最小化窗口 是具有 WS_MINIMIZE 样式的窗口。 默认情况下,系统会将最小化窗口减少为其任务栏按钮的大小,并将最小化窗口移动到任务栏。 还原的窗口 是一个窗口,它已返回到其以前的大小和位置,即它在最小化或最大化之前的大小。
如果应用程序在 CreateWindowEx 函数中指定 WS_MAXIMIZE 或 WS_MINIMIZE 样式,则窗口最初是最大化或最小化的。 创建窗口后,应用程序可以使用 CloseWindow 函数将窗口最小化。 ArrangeIconicWindows 函数排列桌面上的图标,或者它在父窗口中排列父窗口的最小化子窗口。 OpenIcon 函数将最小化窗口还原到其以前的大小和位置。
ShowWindow 函数可以最小化、最大化或还原窗口。 它还可以设置窗口的可见性和激活状态。 SetWindowPlacement 函数包括与 ShowWindow相同的功能,但它可以替代窗口的默认最小化、最大化和还原位置。
IsZoomed 和 IsIconic 函数分别确定给定窗口是最大化还是最小化。 GetWindowPlacement 函数检索窗口的最小化、最大化和还原位置,并确定窗口的显示状态。
当系统收到用于最大化或还原最小化窗口的命令时,它会向窗口发送 WM_QUERYOPEN 消息。 如果窗口过程返回 FALSE,则系统将忽略最大化或还原命令。
系统会自动将最大化窗口的大小和位置设置为最大化窗口的系统定义默认值。 若要替代这些默认值,应用程序可以调用 SetWindowPlacement 函数,也可以处理系统即将最大化窗口时窗口收到的 WM_GETMINMAXINFO 消息。 WM_GETMINMAXINFO 包含指向 MINMAXINFO 结构的指针,其中包含系统用来设置最大化大小和位置的值。 替换这些值会替代默认值。
窗口大小和位置
窗口的大小和位置表示为边界矩形,以相对于屏幕或父窗口的坐标表示。 顶级窗口的坐标相对于屏幕左上角;子窗口的坐标相对于父窗口的左上角。 应用程序在创建窗口时指定窗口的初始大小和位置,但可以随时更改窗口的大小和位置。 有关详细信息,请参阅 填充形状。
本节包含以下主题:
默认大小和位置
应用程序可以通过在 CreateWindowEx中指定CW_USEDEFAULT来允许系统计算顶级窗口的初始大小或位置。 如果应用程序将窗口的坐标设置为CW_USEDEFAULT并且尚未创建其他顶级窗口,则系统会设置新窗口相对于屏幕左上角的位置;否则,它将设置相对于应用程序最近创建的顶级窗口的位置的位置。 如果宽度和高度参数设置为CW_USEDEFAULT,系统将计算新窗口的大小。 如果应用程序已创建其他顶级窗口,则系统将新窗口的大小基于应用程序最近创建的顶级窗口的大小。 创建子窗口或弹出窗口时指定CW_USEDEFAULT会导致系统将窗口的大小设置为默认最小窗口大小。
跟踪大小
系统维护 WS_THICKFRAME 样式窗口的最小和最大跟踪大小;具有此样式的窗口具有大小边框。 最小跟踪大小 是可以通过拖动窗口大小边框生成的最小窗口大小。 同样,最大跟踪大小 是可以通过拖动大小边框生成的最大窗口大小。
当系统创建窗口时,窗口的最小和最大跟踪大小设置为系统定义的默认值。 应用程序可以通过处理 WM_GETMINMAXINFO 消息来发现默认值并重写它们。 有关详细信息,请参阅 大小和位置消息。
系统命令
具有窗口菜单的应用程序可以通过发送系统命令来更改该窗口的大小和位置。 当用户从窗口菜单中选择命令时,将生成系统命令。 应用程序可以通过将 WM_SYSCOMMAND 消息发送到窗口来模拟用户作。 以下系统命令会影响窗口的大小和位置。
命令 | 描述 |
---|---|
SC_CLOSE | 关闭窗口。 此命令将 WM_CLOSE 消息发送到窗口。 窗口执行清理和销毁自身所需的任何步骤。 |
SC_MAXIMIZE | 最大化窗口。 |
SC_MINIMIZE | 最小化窗口。 |
SC_MOVE | 移动窗口。 |
SC_RESTORE | 将最小化或最大化的窗口还原到其以前的大小和位置。 |
SC_SIZE | 启动 size 命令。 若要更改窗口的大小,请使用鼠标或键盘。 |
大小和位置函数
创建窗口后,应用程序可以通过调用多个不同函数之一来设置窗口的大小或位置,包括 SetWindowPlacement、MoveWindow、SetWindowPos,以及 DeferWindowPos。 SetWindowPlacement 设置窗口的最小化位置、最大化位置、还原的大小和位置以及显示状态。 moveWindow 和 SetWindowPos 函数 相似;两者都设置单个应用程序窗口的大小或位置。 SetWindowPos 函数包括一组影响窗口显示状态的标志;MoveWindow 不包含这些标志。 使用 BeginDeferWindowPos、DeferWindowPos和 EndDeferWindowPos 函数同时设置多个窗口的位置,包括大小、位置、z 顺序中的位置和显示状态。
应用程序可以使用 GetWindowRect 函数检索窗口边界矩形的坐标。 GetWindowRect 使用窗口左上角和右下角的坐标填充 RECT 结构。 坐标相对于屏幕左上角,即使对于子窗口也是如此。 ScreenToClient 或 MapWindowPoints 函数将子窗口边界矩形的屏幕坐标映射到相对于父窗口工作区的坐标。
GetClientRect 函数检索窗口工作区的坐标。 GetClientRect 使用工作区左上角和右下角的坐标填充 RECT 结构,但坐标相对于工作区本身。 这意味着工作区左上角的坐标始终为 (0,0),右下角的坐标是工作区的宽度和高度。
CascadeWindows 函数级联桌面上的窗口或级联指定父窗口的子窗口。 TileWindows 函数平铺桌面上的窗口或平铺指定父窗口的子窗口。
大小和位置消息
系统将 WM_GETMINMAXINFO 消息发送到要更改其大小或位置的窗口。 例如,当用户从窗口菜单中单击 “移动”或“大小” 或单击大小调整边框或标题栏时,将发送该消息;当应用程序调用 SetWindowPos 移动或调整窗口大小时,也会发送该消息。 WM_GETMINMAXINFO 包含指向 MINMAXINFO 结构的指针,该结构包含窗口的默认最大化大小和位置,以及默认的最小和最大跟踪大小。 应用程序可以通过处理 WM_GETMINMAXINFO 并设置 MINMAXINFO的相应成员来替代默认值。 窗口必须具有 WS_THICKFRAME 或 WS_CAPTION 样式才能接收 WM_GETMINMAXINFO。 具有 WS_THICKFRAME 样式的窗口在窗口创建过程中以及移动或调整其大小时接收此消息。
系统将 WM_WINDOWPOSCHANGING 消息发送到一个窗口,窗口的大小、位置、z 顺序中的位置或显示状态即将更改。 此消息包含指向 WINDOWPOS 结构的指针,该结构指定窗口的新大小、位置、z 顺序和显示状态。 通过设置 WINDOWPOS的成员,应用程序可能会影响窗口的新大小、位置和外观。
更改窗口的大小、位置、位置在 z 顺序或显示状态后,系统将 WM_WINDOWPOSCHANGED 消息发送到窗口。 此消息包含指向 WINDOWPOS 的指针,该指针通知窗口其新大小、位置、z 顺序和显示状态。 设置通过 WM_WINDOWPOSCHANGED 传递的 WINDOWPOS 结构的成员对窗口没有影响。 必须处理 WM_SIZE 和 WM_MOVE 消息的窗口必须将 WM_WINDOWPOSCHANGED 传递给 DefWindowProc 函数;否则,系统不会向窗口发送 WM_SIZE 和 WM_MOVE 消息。
创建或调整窗口大小时,系统将 WM_NCCALCSIZE 消息发送到窗口。 系统使用该消息计算窗口的工作区大小以及相对于窗口左上角的工作区的位置。 窗口通常将此消息传递到默认窗口过程;但是,此消息在自定义窗口的非工作区或保留窗口大小的部分的应用程序中非常有用。 有关详细信息,请参阅 绘画和绘图。
窗口动画
通过使用 AnimateWindow 函数显示或隐藏窗口时,可以产生特殊效果。 以这种方式对窗口进行动画处理时,系统将滚动、滑动或淡化窗口,具体取决于在调用 AnimateWindow中指定的标志。
默认情况下,系统使用 滚动动画。 效果如此,窗口显示为打开(显示窗口)或滚动关闭(隐藏窗口)。 可以使用 dwFlags 参数指定窗口是水平滚动、垂直还是对角线。
指定 AW_SLIDE 标志时,系统将使用 幻灯片动画。 使用此效果,窗口将显示为幻灯片进入视图(显示窗口)或从视图中滑动(隐藏窗口)。 可以使用 dwFlags 参数指定窗口是水平、垂直还是对角线滑动。
指定 AW_BLEND 标志时,系统将使用 alpha 混合淡化。
还可以使用 AW_CENTER 标志使窗口显示为向内折叠或向外展开。
窗口布局和镜像
窗口布局定义在窗口或设备上下文(DC)中布局文本和 Windows 图形设备界面(GDI)对象的方式。 某些语言(如英语、法语和德语)需要从左到右(LTR)布局。 其他语言(如阿拉伯语和希伯来语)需要从右到左(RTL)布局。 窗口布局适用于文本,但也会影响窗口的其他 GDI 元素,包括位图、图标、原点的位置、按钮、级联树控件,以及水平坐标在向左还是向右移动时增加。 例如,应用程序设置 RTL 布局后,原点位于窗口或设备的右边缘,并且表示水平坐标的数字在向左移动时增加。 但是,并非所有对象都受窗口布局的影响。 例如,与窗口(如图元文件和打印机 DC)不关联的对话框、消息框和设备上下文的布局必须单独处理。 本主题稍后将提及这些内容的具体内容。
通过窗口函数,可以在 Windows 的阿拉伯语和希伯来语版本中指定或更改窗口布局。 请注意,对于具有样式 CS_OWNDC 或具有GM_ADVANCED图形模式的 DC,不支持更改为 RTL 布局(也称为镜像)。
默认情况下,窗口布局从左到右(LTR)。 若要设置 RTL 窗口布局,请使用样式 WS_EX_LAYOUTRTL调用 CreateWindowEx。 此外,默认情况下,子窗口(即使用 WS_CHILD 样式创建的窗口,并在调用 CreateWindow 或 CreateWindowEx中具有与父级相同的布局 hWnd 参数)。 若要禁用对所有子窗口的镜像继承,请在调用 createWindowEx 中指定WS_EX_NOINHERITLAYOUT。 请注意,镜像不是由拥有的窗口(没有 WS_CHILD 样式创建的窗口)或 CreateWindowEx 中父 hWnd 参数创建的窗口 设置为 NULL。 若要禁用单个窗口的镜像继承,请使用 GetWindowLong 和 SetWindowLong 处理 WM_NCCREATE 消息,以关闭 WS_EX_LAYOUTRTL 标志。 此处理除了需要任何其他处理之外。 以下代码片段演示如何执行此作。
SetWindowLong (hWnd,
GWL_EXSTYLE,
GetWindowLong(hWnd,GWL_EXSTYLE) & ~WS_EX_LAYOUTRTL))
可以通过调用 setProcessDefaultLayout(LAYOUT_RTL),将默认布局设置为 RTL。 调用后创建的所有窗口都将镜像,但现有窗口不受影响。 若要关闭默认镜像,请调用 SetProcessDefaultLayout(0)。
请注意,SetProcessDefaultLayout 仅镜像窗口的 DC。 若要镜像任何 DC,请调用 SetLayout(hdc, LAYOUT_RTL)。 有关详细信息,请参阅本主题后面的有关与窗口无关的镜像设备上下文的讨论。
默认情况下,镜像窗口中的位图和图标也会镜像。 但是,不应镜像所有这些内容。 例如,不应镜像具有文本、商业徽标或模拟时钟的用户。 若要禁用位图的镜像,请使用 dwLayout中设置的 LAYOUT_BITMAPORIENTATIONPRESERVED 位调用 SetLayout。 若要在 DC 中禁用镜像,请调用 SetLayout(hdc, 0)。
若要查询当前默认布局,请调用 GetProcessDefaultLayout。 成功返回后,pdwDefaultLayout 包含LAYOUT_RTL或 0。 若要查询设备上下文的布局设置,请调用 GetLayout。 成功返回后,GetLayout 返回 DWORD,该LAYOUT_RTL和LAYOUT_BITMAPORIENTATIONPRESERVED位的设置指示布局设置。
创建窗口后,可以使用 SetWindowLong 函数更改布局。 例如,当用户将现有窗口的用户界面语言从阿拉伯语或希伯来语更改为德语时,这是必需的。 但是,更改现有窗口的布局时,必须使窗口失效并更新窗口,以确保窗口的内容都在同一布局上绘制。 下面的代码示例来自根据需要更改窗口布局的示例代码:
// Using ANSI versions of GetWindowLong and SetWindowLong because Unicode
// is not needed for these calls
lExStyles = GetWindowLongA(hWnd, GWL_EXSTYLE);
// Check whether new layout is opposite the current layout
if (!!(pLState -> IsRTLLayout) != !!(lExStyles & WS_EX_LAYOUTRTL))
{
// the following lines will update the window layout
lExStyles ^= WS_EX_LAYOUTRTL; // toggle layout
SetWindowLongA(hWnd, GWL_EXSTYLE, lExStyles);
InvalidateRect(hWnd, NULL, TRUE); // to update layout in the client area
}
在镜像中,你应该以“near”和“far”而不是“left”和“right”来思考。 未能这样做可能会导致问题。 在屏幕坐标和客户端坐标之间进行映射时,会出现镜像窗口中的问题的一种常见编码做法。 例如,应用程序通常使用类似于以下内容的代码在窗口中放置控件:
// DO NOT USE THIS IF APPLICATION MIRRORS THE WINDOW
// get coordinates of the window in screen coordinates
GetWindowRect(hControl, (LPRECT) &rControlRect);
// map screen coordinates to client coordinates in dialog
ScreenToClient(hDialog, (LPPOINT) &rControlRect.left);
ScreenToClient(hDialog, (LPPOINT) &rControlRect.right);
这会导致镜像出现问题,因为矩形的左边缘将成为镜像窗口中的右边缘,反之亦然。 若要避免此问题,请将 ScreenToClient 调用替换为对 mapWindowPoints 的调用,如下所示:
// USE THIS FOR MIRRORING
GetWindowRect(hControl, (LPRECT) &rControlRect);
MapWindowPoints(NULL, hDialog, (LPPOINT) &rControlRect, 2)
此代码的工作原理是,在支持镜像的平台上,MapWindowPoints 修改为在镜像客户端窗口时交换左右点坐标。 有关详细信息,请参阅 MapWindowPoints的“备注”部分。
可能导致镜像窗口问题的另一种常见做法是使用屏幕坐标中的偏移量而不是客户端坐标将对象定位在客户端窗口中。 例如,以下代码使用屏幕坐标的差异作为客户端坐标中的 x 位置来定位对话框中的控件。
// OK if LTR layout and mapping mode of client is MM_TEXT,
// but WRONG for a mirrored dialog
RECT rdDialog;
RECT rcControl;
HWND hControl = GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hDlg, &rcDialog); // gets rect in screen coordinates
GetWindowRect(hControl, &rcControl);
MoveWindow(hControl,
rcControl.left - rcDialog.left, // uses x position in client coords
rcControl.top - rcDialog.top,
nWidth,
nHeight,
FALSE);
当对话框窗口具有从左到右(LTR)布局且客户端的映射模式MM_TEXT时,此代码是正常的,因为客户端坐标中的新 x 位置对应于控件的左边缘和屏幕坐标中的对话框的差异。 但是,在镜像对话框中,左侧和右侧是反向的,因此应按如下所示 MapWindowPoints:
RECT rcDialog;
RECT rcControl;
HWND hControl - GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hControl, &rcControl);
// MapWindowPoints works correctly in both mirrored and non-mirrored windows.
MapWindowPoints(NULL, hDlg, (LPPOINT) &rcControl, 2);
// Now rcControl is in client coordinates.
MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, FALSE)
镜像对话框和消息框
对话框和消息框不继承布局,因此必须显式设置布局。 若要镜像消息框,请使用 MB_RTLREADING 选项调用 MessageBox 或 MessageBoxEx。 若要从右向左布局对话框,请在对话框模板结构中使用扩展样式WS_EX_LAYOUTRTL DLGTEMPLATEEX。 属性表是对话框的特殊情况。 每个选项卡都被视为单独的对话框,因此需要在要镜像的每个选项卡中包括WS_EX_LAYOUTRTL样式。
镜像设备上下文与窗口不关联
未与窗口(如图元文件或打印机 DC)关联的 DC 不会继承布局,因此必须显式设置布局。 若要更改设备上下文布局,请使用 SetLayout 函数。
SetLayout 函数很少与窗口一起使用。 通常,窗口仅在处理 WM_PAINT 消息时接收关联的 DC。 有时,程序通过调用 GetDC为窗口创建 DC。 无论哪种方式,DC 的初始布局都由 BeginPaint 设置,或根据窗口的WS_EX_LAYOUTRTL标志 GetDC。
GetWindowOrgEx、GetWindowExtEx、GetViewportOrgEx 和 GetViewportExtEx 返回的值不受调用 SetLayout的影响。
当布局为 RTL 时,GetMapMode 将返回MM_ANISOTROPIC而不是MM_TEXT。 使用 MM_TEXT 调用 SetMapMode 将正常工作;仅 GetMapMode 的返回值受到影响。 同样,当映射模式MM_TEXT导致报告的映射模式更改为MM_ANISOTROPIC时,调用 SetLayout(hdc, LAYOUT_RTL)。
窗口销毁
通常,应用程序必须销毁它创建的所有窗口。 它通过使用 DestroyWindow 函数执行此作。 当窗口被销毁时,如果窗口可见,系统将隐藏该窗口,然后删除与该窗口关联的任何内部数据。 这会使窗口句柄失效,应用程序不能再使用该句柄。
应用程序会在创建应用程序后不久销毁它创建的许多窗口。 例如,一旦应用程序有足够的输入来继续其任务,应用程序通常会销毁对话框窗口。 应用程序最终会销毁应用程序的主窗口(终止前)。
在销毁窗口之前,应用程序应保存或删除与窗口关联的任何数据,并释放为窗口分配的任何系统资源。 如果应用程序未释放资源,系统将释放应用程序未释放的任何资源。
销毁窗口不会影响从中创建窗口的窗口类。 仍可使用该类创建新窗口,并且该类的任何现有窗口将继续运行。 销毁窗口也会破坏窗口的后代窗口。 DestroyWindow 函数先将 WM_DESTROY 消息发送到窗口,然后发送到其子窗口和子窗口。 这样,正在销毁的窗口的所有后代窗口也会被销毁。
当用户单击 关闭时,具有窗口菜单的窗口会收到 WM_CLOSE 消息。 通过处理此消息,应用程序可以在销毁窗口之前提示用户进行确认。 如果用户确认应销毁窗口,应用程序可以调用 DestroyWindow 函数来销毁窗口。
如果被销毁的窗口是活动窗口,则活动状态和焦点状态将传输到另一个窗口。 成为活动窗口的窗口是下一个窗口,由 Alt+ESC 键组合决定。 然后,新的活动窗口确定哪个窗口接收键盘焦点。