当所有者窗口最小化时,系统会自动隐藏关联的拥有窗口。 同样,还原所有者窗口时,系统会自动显示关联的拥有窗口。 在这两种情况下,系统会先将
WM_SHOWWINDOW
消息发送到拥有的窗口,然后再隐藏或显示它们。 有时,应用程序可能需要隐藏拥有的窗口,而无需最小化或隐藏所有者。 在这种情况下,应用程序使用
ShowOwnedPopups
函数。 此函数设置或删除所有拥有窗口
的WS_VISIBLE
样式,并将
WM_SHOWWINDOW
消息发送到拥有的窗口,然后再隐藏或显示它们。 隐藏所有者窗口不会影响拥有窗口的可见性状态。
当父窗口可见时,其关联的子窗口也可见。 同样,当父窗口处于隐藏状态时,其子窗口也会被隐藏。 最小化父窗口不会影响子窗口的可见性状态;也就是说,子窗口与父窗口一起最小化,但
WS_VISIBLE
样式不会更改。
即使窗口具有
WS_VISIBLE
样式,用户也可能无法在屏幕上看到该窗口;其他窗口可能完全重叠,或者它可能已移动到屏幕边缘之外。 此外,可见子窗口受其父子关系所建立的剪裁规则的约束。 如果窗口的父窗口不可见,它也将不可见。 如果父窗口移动超出屏幕边缘,子窗口也会移动,因为子窗口相对于父窗口的左上角绘制。 例如,用户可能会将包含子窗口的父窗口移动到离屏幕边缘足够远的位置,用户可能无法看到子窗口,即使子窗口及其父窗口都具有
WS_VISIBLE
样式。
最小化、最大化和还原的窗口
最大化窗口
是具有
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
消息来模拟用户操作。 以下系统命令会影响窗口的大小和位置。
Command
大小和位置函数
创建窗口后,应用程序可以通过调用多个不同函数之一(包括
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
标志使窗口看起来向内折叠或向外展开。
窗口布局和镜像
窗口布局定义文本和 Windows 图形设备接口 (GDI) 对象如何在 DC) (窗口或设备上下文中布局。 某些语言(如英语、法语和德语)需要从左到右 (LTR) 布局。 其他语言(如阿拉伯语和希伯来语)需要从右到左 (RTL) 布局。 窗口布局适用于文本,但也会影响窗口的其他 GDI 元素,包括位图、图标、原点的位置、按钮、级联树控件,以及水平坐标是随向左还是向右增加。 例如,在应用程序设置 RTL 布局后,原点将定位在窗口或设备的右边缘,表示水平坐标的数量随着向左移动而增加。 但是,并非所有对象都受窗口布局的影响。 例如,对话框、消息框和设备上下文(如图元文件和打印机 DC)的布局必须单独处理。 本主题稍后将提及这些内容的具体内容。
使用窗口函数可以指定或更改 Windows 的阿拉伯语和希伯来语版本的窗口布局。 请注意,对于具有CS_OWNDC样式的窗口或具有GM_ADVANCED图形模式的 DC,不支持更改为 RTL 布局
(
也称为镜像) 。
默认情况下,窗口布局为从左到右 (LTR) 。 若要设置 RTL 窗口布局,请使用样式
WS_EX_LAYOUTRTL
调用
CreateWindowEx
。 此外,默认情况下,子窗口 (,即在调用
CreateWindow 或 CreateWindowEx
时使用
WS_CHILD
样式和有效的父
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) 。 有关详细信息,请参阅本主题后面的有关镜像设备上下文(不与 Windows 关联的)的讨论。
默认情况下,镜像窗口中的位图和图标也会镜像。 但是,并非所有这些都应当镜像。 例如,不应镜像具有文本、企业徽标或模拟时钟的那些。 若要禁用位图的镜像,请使用 dwLayout 中设置LAYOUT_BITMAPORIENTATIONPRESERVED位调用 SetLayout。 若要在 DC 中禁用镜像,请调用 SetLayout (hdc, 0) 。
若要查询当前默认布局,请调用 GetProcessDefaultLayout。 成功返回后, pdwDefaultLayout 包含LAYOUT_RTL或 0。 若要查询设备上下文的布局设置,请调用 GetLayout。 成功返回后, GetLayout 将返回一个 DWORD,该 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
在镜像中,应考虑“近”和“远”,而不是“左”和“右”。 否则可能会导致问题。 在屏幕坐标和客户端坐标之间进行映射时,会导致镜像窗口中出现问题的一种常见编码做法。 例如,应用程序通常使用类似于下面的代码在窗口中放置控件:
// 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。 若要从右到左布局对话框,请使用对话框模板结构 DLGTEMPLATEEX 中的扩展样式WS_EX_LAYOUTRTL。 属性表是对话框的一种特殊情况。 每个选项卡被视为一个单独的对话框,因此需要在要镜像的每个选项卡中包括WS_EX_LAYOUTRTL样式。
镜像不与窗口关联的设备上下文
与窗口无关的 DC(例如图元文件或打印机 DC)不会继承布局,因此必须显式设置布局。 若要更改设备上下文布局,请使用 SetLayout 函数。
SetLayout 函数很少用于窗口。 通常,Windows 仅在处理 WM_PAINT 消息时接收关联的 DC。 有时,程序通过调用 GetDC 为窗口创建 DC。 无论采用哪种方式,DC 的初始布局都由 BeginPaint 或 GetDC 根据窗口的WS_EX_LAYOUTRTL标志设置。
GetWindowOrgEx、GetWindowExtEx、GetViewportOrgEx 和 GetViewportExtEx 返回的值不受调用 SetLayout 的影响。
当布局为 RTL 时, GetMapMode 将返回MM_ANISOTROPIC而不是MM_TEXT。 使用 MM_TEXT 调用 SetMapMode 将正常工作;仅 GetMapMode 中的返回值受到影响。 同样, (hdc 调用 SetLayout 时,LAYOUT_RTL) MM_TEXT映射模式会导致报告的映射模式更改为MM_ANISOTROPIC。
通常,应用程序必须销毁它创建的所有窗口。 它通过使用 DestroyWindow 函数执行此操作。 当窗口被销毁时,系统会隐藏窗口(如果它可见),然后删除与该窗口关联的任何内部数据。 这会使窗口句柄失效,而应用程序不能再使用窗口句柄。
应用程序在创建后不久会销毁它创建的许多窗口。 例如,应用程序通常会在应用程序具有足够的用户输入以继续其任务后立即销毁对话框窗口。 应用程序最终会销毁应用程序 (的主窗口,然后终止) 。
在销毁窗口之前,应用程序应保存或删除与窗口关联的任何数据,并释放为窗口分配的任何系统资源。 如果应用程序未释放资源,系统将释放应用程序未释放的任何资源。
销毁窗口不会影响从中创建窗口的窗口类。 仍可使用该类创建新窗口,并且该类的任何现有窗口将继续运行。 销毁窗口也会销毁窗口的后代窗口。 DestroyWindow 函数首先将WM_DESTROY消息发送到窗口,然后发送到其子窗口和子窗口。 这样,被销毁的窗口的所有后代窗口也会被销毁。
当用户单击“关闭”时,带有窗口菜单的窗口会收到WM_CLOSE消息。 通过处理此消息,应用程序可以在销毁窗口之前提示用户进行确认。 如果用户确认应销毁窗口,应用程序可以调用 DestroyWindow 函数来销毁窗口。
如果正在销毁的窗口是活动窗口,则活动状态和焦点状态都转移到另一个窗口。 成为活动窗口的窗口是下一个窗口,由 Alt+ESC 组合键决定。 然后,新的活动窗口确定哪个窗口接收键盘焦点。