这篇文章翻译至MSDN2005,给自己学习,也给所有觉得它有用的人,文中难免有翻译不到位或者错误的地方,望高手指正。译者:欧昊川(转载麻烦注明出处及译者)
这个概述讨论了窗口的一些特性,如窗口类型、状态、大小及位置。
1.1 层叠窗口(Overlapped Windows)
层叠窗口是一个具有标题栏、边框和客户区的顶层窗口;也就是说它适合做为应用程序主窗口。它也可以具有一个系统菜单,最小和最大化按钮,以及滚动条。一个层叠窗口被典型地用于包含所有上述组件的应用程序主窗口。
通过在CreateWindowEx中指定WS_OVERLAPPED或者WS_OVERLAPPEDWINDOW样式,一个应用程序就能创建一个层叠窗口。如果你使用第一个样式,那么创建的窗口就具有一个标题栏和边框;如果你使用第二个,那么窗口就具有一个标题栏,可以调整大小的边框,系统菜单,以及最大最小化按钮。
1.2 弹出窗口(Pop-up Windows)
弹出窗口是一个特殊的层叠窗口,它被用于显示在应用程序主窗口之外的对话框,消息框以及其他临时窗口。标题栏对弹出窗口来说是可选的;除此之外,弹出窗口跟具有WS_OVERLAPPED样式的层叠窗口一样。
你可以通过在CreateWindowEx中指定WS_POPUP样式来创建一个弹出窗口。如果要使用标题栏,就加入WS_CAPTION样式。使用WS_POPUPWINDOW样式来创建一个含有边框和系统菜单的弹出窗口。WS_CAPTION样式必须与WS_POPUPWINDOW样式一起使用才能使系统菜单可见。
1.3 子窗口(Child Windows)
子窗口具有WS_CHILD样式并且它被限制在其父窗口的客户区中。应用程序典型地使用子窗口来把其父窗口的客户区划分成几个功能区域。你可以通过在CreateWindowEx中指定WS_CHILD样式来创建子窗口。
子窗口必须具有一个父窗口。父窗口可以是一个层叠窗口,弹出窗口,或者另外一个子窗口。你可以在CreateWindowEx中指定父窗口。如果你在CreateWindowEx中指定了WS_CHILD样式但是没有指定父窗口,那么系统不会创建这个子窗口。
子窗口只具有一个客户区而没有其他特性,除非这些特性被明确的请求。应用程序可以为子窗口添加标题栏,系统菜单,最小化最大化按钮,边框,以及滚动条。但是子窗口不能具有自定义菜单。如果应用程序指定了一个自定义菜单句柄,那么无论是在它注册这个子窗口类还是创建这个子窗口时,这个菜单句柄都被忽略。如果没有指定边框样式,系统将创建一个无边框窗口。应用程序可以使用无边框的子窗口来划分父窗口的客户区如果想保持这种划分对用户是不可见的话。
1.4 窗口布置(Positioning)
系统总是相对于父窗口客户区的左上角来放置子窗口。子窗口的任何部分都不会出现在其父窗口的边框之外。如果应用程序创建一个比父窗口大的子窗口,或者移动子窗口使得一个或者所有子窗口超出了父窗口的边框,那么系统会裁剪子窗口,即在父窗口边框之外的部分不被显示。对父窗口产生影响的行为同样会影响子窗口,这些行为如下:
1.5 窗口裁剪(Clipping)
系统不会自动的从父窗口的客户区裁剪出子窗口区域。这意味着父窗口可以直接在子窗口所在区域上绘图,如果它在属于子窗口区域的地方完成了任何绘制的话。然而,如果父窗口指定了WS_CLIPCHILDREN样式,那么系统就会从父窗口的客户区对子窗区域进行裁剪。如果子窗口区域被裁剪出来了,父窗口就不能在它上面绘制了。(注:在这里,“裁剪”是指从父窗口的客户区划分出一块子窗口区域,下同。)
在同一个客户区内,一个子窗口能叠放在另一个子窗口上。与一个或者多个子窗口共享一个父窗口的子窗口被互称为兄弟窗口。兄弟窗口可以相互在对方的客户区内绘图,除非某个子窗口具有WS_CLIPSIBLING样式。如果子窗口有这个样式,任何在其内部的其他兄弟窗口的部分将被裁剪。
如果一个窗口即有WS_CLIPCHILDREN又有WS_CLIPSIBLING样式的话,轻微的性能下降将会发生。每一个窗口都占据着系统资源,因此应用程序不应该不加选择的使用子窗口。为了达到最佳性能,需要逻辑划分主窗口区域的应用程序应该在它的窗口过程中完成划分,而不是使用子窗口。
1.6 与父窗口的关系(Relationship to Parent Window)
应用程序可以使用SetParent函数改变已经存在的子窗口的父窗口。在这种情况下,系统把子窗口从原先的父窗口客户区中移除并把它移到新的父窗口客户区中。如果SetParent函数指定了一个NULL句柄,桌面窗口将成为该子窗口的父窗口。在这种情况下,子窗口被绘制在桌面上,在任何窗口的边框之外。GetParent函数返回一个子窗口的父窗口句柄。
父窗口放弃其客户区的一部分来给子窗口使用(注:“放弃”与裁剪的意思相同),子窗口会从这个区域收到所有的输入。
一个子窗口只有一个父窗口,但是一个父窗口能含有多个子窗口。每个子窗口又可以包含多个子窗口。在这个窗口链中,每个子窗口被称为最初的父窗口的子孙窗口。应用程序使用IsChild函数来发现一个给定的窗口是否是一个给定的父窗口的子窗口或者子孙窗口。
EnumChildWindows函数列举一个父窗口的子窗口。然后,该函数将返回的每个子窗口的句柄传递到应用程序定义的一个回调函数中。给定的父窗口的子孙窗口也会被列举。
1.7 消息(Messages)
系统直接将子窗口的输入消息传递给子窗口;该输入消息并不会传递到父窗口。唯一的例外是如果子窗口被EnableWindow函数禁用。在这种情况下,系统将任何要传递给子窗口的输入消息传递到父窗口中。这允许父窗口检查输入消息并启用子窗口,如果需要的话。
一个子窗口具有一个唯一的整数标识符,当子窗口与控件窗口一同工作时,这个标识符是重要的。应用程序通过给控件发送消息来指挥它的行为。应用程序使用控件窗口的标识符来标识发送给该控件的消息。另外,控件发送通知消息给它的父窗口。一个通知消息包含控件窗口标识符,父窗口使用它来分辨是哪个控件发送的消息。应用程序通过设置CreateWindowEx函数中的hMenu参数为一个非菜单句柄的值来为其他类型的子窗口指定子窗口标识符。
1.8 分层窗口(Layered Windows)
使用分层窗口可以显著的改善具有复杂形状、动画效果或者使用alpha混合效果的窗口的性能和视觉效果。系统将分层窗口以及处于后台的应用程序窗口自动组合起来并自动重绘。因此,分层窗口可以平稳的实施而不会产生复杂窗口区域的典型的闪烁情况。另外,分层窗口可以部分透明,也就是alpha混合。
要创建一个分层窗口,在调用CreateWindowEx函数时指定WS_EX_LAYERED这个窗口扩展样式,或者在窗口创建之后调用SetWindowLong函数来设置WS_EX_LAYERED扩展样式。在调用CreateWindowEx函数后,直到SetLayeredWindowAttributes或者UpdateLayeredWindow函数被调用后,分层窗口才会被显示出来。
要设置给定分层窗口的不透明度或者透明关键色,可以调用SetLayeredWindowAttributes。调用之后,在窗口被显示或调整大小时,系统仍然会要求窗口重绘。然而,因为系统存储了分层窗口的图像,所以在分层窗口的一部分被做为相关窗口在桌面上移动后的结果显示时,系统不会要求窗口重绘。原有的应用程序如果想为窗口增加透明效果时,不需要重新构建重绘代码。因为系统重定向调用了SetLayeredWindowAttributes函数的窗口的绘制过程到脱离屏幕的内存中(注:off-screen memory译成“脱离屏幕的内存”不知道是否妥当),并且重建它来达到预期的效果。
为了使动画效果更快和更有效率或者如果每个像素都需要alpha值,可以调用UpdateLayeredWindow函数。该函数应该主要被使用在当应用程序必须直接地支持分层窗口的形状和内容的,而不使用系统通过SetLayeredWindowAttributes函数提供的重定位机制(注:redirection mechanism)的时候。另外,直接利用UpdateLayeredWindow函数能更有效的使用内存,因为系统不需要额外的被用来存储重定向窗口的图像的内存。为了在动画显示窗口时达到最大的效率,调用UpdateLayeredWindow函数来改变分层窗口的位置和大小。请注意在调用SetLayeredWindowAttributes函数之后,接着调用UpdateLayeredWindow函数将失败,直到分层样式(注:WS_EX_LAYERED)被清除并重新设置之后。
分层窗口的鼠标点击测试(注:Hit testing)基于窗口的形状和透明度。这意味着那些用不同的颜色做标记和alpha值为0的窗口区域将使鼠标消息穿过(注:即不会收到鼠标消息)。然而,如果分层窗口具有WS_EX_TRANSPARENT窗口扩展样式的话,分层窗口的形状将被忽略并且鼠标事件将传递到分层窗口下面的其他窗口中。
1.9 只处理消息的窗口(Message-Only Windows)(注:以下简称:MO窗口)
MO窗口使你能发送和接收消息。它是不可见的,没有Z序,不能枚举,不能接收广播消息。该窗口仅简单的分派消息。
要创建MO窗口,指定CreateWindowEx函数的hWndParent参数为HWND_MESSAGE常量或者一个指向已存在的MO窗口句柄。你也可以通过指定SetParent函数的hWndNewParent参数为HWND_MESSAGE常量来改变一个已存在的窗口成MO窗口。
要找到MO窗口,可以指定FindWindowEx函数的hwndParent参数为HWND_MESSAGE常量。另外,如果FindWindowEx函数的hwndParent参数和hwndChildAfter参数都为空,那么该函数既寻找MO窗口也寻找顶层窗口。