原文链接:http://www.cnblogs.com/dhatbj/p/3288152.html

说明:这是本人2008年写的一篇旧文,从未公开发表过。其中除了一小段描述Window Mobile平台的内容已过时,大部分内容对于从事Win32开发的程序员还是很有参考价值的,也是对自己从事Windows开发工作的一个总结,欢迎指正。转载请注明:http://www.cnblogs.com/dhatbj/原创。

范围(Scope

讨论Windows操作系统中窗口之间的关系(relationship between windows),除特别指明的部分之外,适用于各版本桌面平台和Windows Mobile平台。

概述(Summary

窗口(Window)是Windows操作系统中用来显示信息和接受用户输入的基本单元(Block)。负责管理窗口相关功能的操作系统部件被称为窗口管理器(Window Manager)。Windows操作系统初始化时会生成一个窗口,叫做桌面窗口(Desktop Window),调用GetDesktopWindow函数可获得它的句柄。桌面窗口会覆盖整个屏幕,所有其它窗口都在其之上显示。

 

窗口类型(Window Type

Windows中有3种类型的窗口:层叠窗口(Overlapped Window)、弹出窗口(Popup Window)、子窗口(Child Window),在生成窗口(调用CreateWindowEx)时分别以WS_OVERLAPPED、WS_POPUP或WS_CHILD窗口风格(Style)来表示。层叠窗口是窗口的缺省类型,如果不指定任何窗口类型则生成的是层叠窗口。

弹出窗口通常用于对话框。它隐含带有WS_CLIPSIBLINGS窗口风格(后面会详细描述)。

层叠窗口通常被用作应用程序的主窗口,也隐含带有WS_CLIPSIBLINGS窗口风格。在桌面平台上,层叠窗口还隐含带有WS_CAPTION窗口风格。带有标题栏的窗口都隐含带有边框(Border),至于原因嘛,想像一下“光秃秃的标题栏”+“没有边框的窗口”会是个什么样子。在Mobile平台上,层叠窗口与弹出窗口的界限已经很模糊了。

  层叠窗口和弹出窗口统称为顶层窗口(top-level windows)。

剩下的一类是子窗口,例如常见的Button,Edit Box,List Box等窗口控件。

WS_OVERLAPPED的值

在桌面平台上,WS_OVERLAPPED定义为0,这与窗口的缺省类型为层叠窗口的事实相符;而在Windows Mobile平台上,WS_OVERLAPPED被定义为WS_BORDER | WS_CAPTION,这是怎么回事呢?我想这是微软为了保持桌面平台与Mobile平台软件的外观兼容性而使用的一个技巧,因为Mobile平台上的层叠窗口缺省是不带WS_CAPTION风格的,微软的意思应该是:(WS_OVERLAPPED in PC)=(WS_OVERLAPPED in Mobile)| WS_BORDER | WS_CAPTION,在字面上就会写成:

#define WS_OVERLAPPED         WS_BORDER | WS_CAPTION

这样定义可以方便桌面平台上的代码移植到Mobile平台。但开发原生的Windows Mobile代码时就要注意了,由于Mobile上的典型窗口是不带标题栏的(Mobile界面最上方的Title Bar并不属于窗口的一部分),我们在生成层叠窗口时不应使用WS_OVERLAPPED标志(这一标志的实际意义是:PC style overlapped window)— 不指定任何窗口类型就好。

窗口层次结构(Window Hierarchy

窗口管理器以一个树状结构组织和管理系统内所有窗口,如图:

图1.树状的窗口组织图

树形结构的根是桌面窗口,其下属第一层窗口是顶层窗口(层叠窗口+弹出窗口,见上一小节)。顶层窗口之下的所有层里只包含子窗口。从桌面窗口出发,通过一系列相关API函数的调用,可以遍历系统中的所有窗口。

窗口的从属关系 

包括父/子(parent-child)关系、拥有/被拥有(owner-owned)关系及兄弟(siblings)关系。

  父/子(parent-child)关系 

类型为Child Window的窗口必须有一个父窗口,父窗口的类型可以是3种类型中的任意一种。子窗口的位置坐标都是相对于父窗口客户区的左上角(upper-left corner)计算的。子窗口会把它的notify消息发送到父窗口。父/子关系对窗口可见性的影响为:子窗口只能显示在它的父窗口的客户区中,超出父窗口客户区的部分将被裁减掉;父窗口被隐藏时,它的所有子窗口也被隐藏;最小化父窗口不影响子窗口的可见状态,子窗口会随着父窗口被最小化,但是它的WS_VISIBLE属性不变。父窗口被销毁的时候,它的所有子窗口都会被销毁。

窗口生成时通过CreateWindowEx函数的hWndParent参数可指定其父窗口,或在窗口产生后通过SetParent函数更改。通过GetParent函数可获取父窗口句柄。父窗口要查询其子窗口可使用GetWindow函数(指定GW_CHILD标志),该函数返回第一个子窗口的句柄。

桌面窗口与所有顶层窗口也是父/子关系,但又有其特殊性:对桌面窗口调用GetWindow函数(指定GW_CHILD标志)可得到第一个顶层窗口的句柄,但对某一个顶层窗口调用GetParent却会返回NULL,不会返回桌面窗口的句柄。由此可见,父/子关系中的“子”一方未必一定是子窗口(Child Window类型)。

  拥有/被拥有(owner-owned)关系

顶层窗口之间可以存在owner-owned关系。owner-owned关系对窗口可见性的影响为:owned窗口永远显示在owner窗口的前面;当owner窗口最小化的时候,它所拥有的窗口都会被隐藏;隐藏owner窗口不影响它所拥有的窗口的可见状态。根据最后这一点,如果窗口A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候,窗口B被隐藏,但是窗口 C还是可见的。当owner窗口被销毁的时候,它所拥有的窗口都会被销毁。

Owner窗口在owned窗口生成时通过CreateWindowEx函数的hWndParent参数指定,如果该参数传入的是一个子窗口,窗口管理器将找到容纳该子窗口的顶层窗口,以该顶层窗口作为owner窗口。Owner窗口一旦指定不能更改。通过GetWindow函数(指定GW_OWNER标志)可获取owner窗口的句柄(如果存在的话)。

  兄弟(siblings)关系 

同一个父窗口的所有直属子窗口之间是兄弟关系,也就是相互平等,没有主从之分。窗口管理器用链表(linked list)来管理每个父窗口的直属子窗口(见图1),这个链表叫子窗口链(child window list)。

调用GetWindow函数时使用GW_HWNDPREV或GW_HWNDNEXT标志可访问子窗口链中的前一个或后一个窗口;使用GW_HWNDFIRST或GW_HWNDLAST标志可访问子窗口链中的第一个或最后一个窗口。

Z-Order 

窗口在子窗口链中的先后顺序也就是窗口在屏幕上显示时的前后顺序,在子窗口链里位置越靠前的窗口显示时也越靠前,这个前后顺序就是Z-Order。Z-Order在前的顶层窗口会遮挡Z-Order在后的顶层窗口;屏幕上的一块区域需要刷新(Update)时,同一个子窗口链中Z-Order在前的窗口先刷新,Z-Order在后的窗口后刷新。有父/子关系的窗口是父窗口先刷新,子窗口后刷新,

  顶层窗口生成时,窗口管理器会把它加到(桌面窗口的)子窗口链的最前面,也就是Z-Order的最前面,使整个窗口都可见。子窗口的Z-Order要高于它的父窗口,因此会显示在父窗口前面,但任何一个子窗口的Z-Order都不会超过其父窗口的Z-Order更靠前的兄弟窗口。改变窗口的Z-Order可使用SetWindowPos函数。

子窗口生成时,与顶层窗口的情况有所不同,窗口管理器会把它加到父窗口的子窗口链的最后面。这似乎是反直觉的,为什么会这样呢?窗口管理器这样做是有原因的,其主要目的是让后生成的窗口能显示在前面(兄弟窗口间有重叠的情况下),并且子窗口间的Tab-Order与窗口的生成顺序相同,这样的效果才是符合直觉的。子窗口大多数情况下都共用其父窗口的显示DC(Device Context),所以在刷新时是可以在其兄弟窗口的客户区上绘画(draw)的,这就造成了Z-Order在后的子窗口因为刷新顺序在后,绘画能覆盖Z-Order在前的窗口,显示效果反而在前的现象,如下图所示:

图2.子窗口相互覆盖示意图(无WS_CLIPSIBLINGS风格)

如果想使Z-Order在前的子窗口显示时也在前(覆盖Z-Order在后的子窗口),需要使用WS_CLIPSIBLINGS窗口风格(后面详述)。

Topmost窗口

也就是具有WS_EX_TOPMOST扩展风格的窗口,仅适用于顶层窗口,子窗口无法使用。根据有无WS_EX_TOPMOST风格将所有顶层窗口分成了两个级别。有WS_EX_TOPMOST风格的顶层窗口Z-Order在前,普通顶层窗口Z-Order在后。普通顶层窗口要成为Topmost窗口可以调用SetWindowPos函数指定hwndInsertAfter参数为HWND_TOPMOST,指定hwndInsertAfter参数为HWND_NOTOPMOST调用SetWindowPos则使Topmost窗口成为普通顶层窗口。

Tab-Order

兄弟窗口间的Tab-Order实际上是由Z-Order决定的(与Z-Order的顺序相同),因此如果想在程序运行过程中动态改变Tab-Order,可通过改变兄弟窗口间的Z-Order(使用SetWindowPos)来实现。反过来,在VC++的对话框编辑器里,如果控件的位置有重叠,通过调整Tab-Order也能调整控件间的遮挡关系。

WS_CLIPCHILDRENWS_CLIPSIBLINGS

       在窗口之间有重叠的情况下,这两个窗口风格会影响窗口刷新区域(Update Region)的计算方法。“Clip”一词是指从刷新区域中“剪切掉”被Z-Order在前的子窗口或兄弟窗口覆盖的区域,如图3所示:

图3. 窗口之间有重叠时的刷新区域

窗口B与窗口A是兄弟关系,B的Z-Order高于A。整个屏幕刷新时,B先刷新,A后刷新。如果A没有WS_CLIPSIBLINGS风格,则A的整个客户区都被刷新,A将重新刷新两窗口相交的C区域,造成A覆盖B的效果;但如果A有WS_CLIPSIBLINGS风格,则只有图中绿色的部分会被刷新,A就不会覆盖B。简单的说,WS_CLIPSIBLINGS可以控制Z-Order在前的窗口显示在前还是Z-Order在后的窗口显示在前。由于窗口管理器强制顶层窗口都有WS_CLIPSIBLINGS风格,所以顶层窗口总是Z-Order在前的显示在前(能覆盖Z-Order在后的窗口)。子窗口缺省不带WS_CLIPSIBLINGS风格,所以是Z-Order在后的窗口能覆盖Z-Order在前的兄弟窗口(如图2)。要想使子窗口的遮挡效果与Z-Order一致,可以把相关的子窗口都加上WS_CLIPSIBLINGS风格,之后的外观如下图:

图4.子窗口相互覆盖示意图(有WS_CLIPSIBLINGS风格)

由于父窗口总是先于子窗口执行刷新动作,所以无论是否使用WS_CLIPCHILDREN风格,父窗口都不会遮盖子窗口。WS_CLIPCHILDREN的作用主要是避免窗口重叠区域的重复刷新,有可能加快窗口显示速度以及减轻刷新时的“闪烁”问题。

窗口生成时使用缺省位置和大小 

       CreateWindowEx生成的窗口如果是层叠窗口,程序可以不指定窗口的初始位置和大小,而是由窗口管理器决定。

要让窗口管理器设置窗口的初始位置,需要使用一个特殊值CW_USEDEFAULT作为CreateWindowEx的参数“x”的值,参数“y”将被忽略,不起作用。对于桌面平台,如果窗口风格包含WS_VISIBLE,CreateWindowEx 函数内部将把“y”的值作为第二个参数(nCmdShow)传递给ShowWindow函数,这时的“y”作为一个隐藏参数使用。

要让窗口管理器设置窗口的初始大小,需要使用CW_USEDEFAULT作为CreateWindowEx的参数nWidth的值,参数nHeight将被忽略。

CW_USEDEFAULT只能用于层叠窗口,对于弹出窗口或子窗口,如果给“x”参数传递了CW_USEDEFAULT,则窗口位置不确定;如果给nWidth参数传递了CW_USEDEFAULT,则窗口大小不确定。

参考资料

1.    Win32 Window Hierarchy and Styles,Kyle Marsh,1993

http://msdn.microsoft.com/en-us/library/ms997562.aspx

【转】窗口之间的主从关系与Z-Order的更多相关文章

  1. [转载]窗口之间的主从关系与Z-Order

    窗口之间的主从关系与Z-Order 原文地址:http://www.cnblogs.com/dhatbj/p/3288152.html说明:这是本人2008年写的一篇旧文,从未公开发表过.其中除了一小 ...

  2. 窗口之间的主从关系与Z-Order

    说明:这是本人2008年写的一篇旧文,从未公开发表过.其中除了一小段描述Window Mobile平台的内容已过时,大部分内容对于从事Win32开发的程序员还是很有参考价值的,也是对自己从事Windo ...

  3. 简析Window、Activity、DecorView以及ViewRoot之间的错综关系

    一.职能简介 Activity Activity并不负责视图控制,它只是控制生命周期和处理事件.真正控制视图的是Window.一个Activity包含了一个Window,Window才是真正代表一个窗 ...

  4. Python+Selenium练习篇之19-多窗口之间切换

    本文来介绍如何处理driver在多窗口之间切换,想一下这样的场景,在页面A点击一个连接,会触发在新Tab或者新窗口打开页面B,由于之前的driver实例对象在页面A,但是你接下来的脚本是操作页面B的元 ...

  5. 高强度学习训练第六天总结:Redis主从关系总结

    Redis主从复制机制 1.读写分离的好处 性能优化:主服务器专注于写操作,可以更适合写入数据的模式工作:同样,从服务器专注于读操作,可以用更适合读取数据的模式工作. 强化数据安全,避免单点故障:由于 ...

  6. Host–Parasite(主从关系): Graph LSTM-in-LSTM for Group Activity Recognition

    This article aims to tackle the problem of group activity recognition in the multiple-person scene. ...

  7. Angular2入门系列教程3-多个组件,主从关系

    上一篇 Angular2项目初体验-编写自己的第一个组件 好了,前面简单介绍了Angular2的基本开发,并且写了一个非常简单的组件,这篇文章我们将要学会编写多个组件并且有主从关系 现在,假设我们要做 ...

  8. 父窗口,子窗口之间的JS"通信"方法

    今天需要在iframe内做一个弹窗,但使用弹窗组件的为子窗口,所以弹窗只在子窗口中显示掩膜层和定位,这样不符合需求. 后来晓勇哥指点,了解到一个以前一直没关注到的东西,每个窗口的全局变量,其实都存在对 ...

  9. 实现如下类之间的继承关系,并编写Music类来测试这些类。

    实现如下类之间的继承关系,并编写Music类来测试这些类. package com.hanqi.test; public class Instrument { //输出弹奏乐器 public void ...

随机推荐

  1. JavaScript算法(冒泡排序、选择排序与插入排序)

    冒泡排序.选择排序与插入排序复杂度都是二次方级别的,放在一起说吧. 介绍一些学习这三个排序方法的比较好的资料.冒泡排序看<学习JavaScript数据结构与算法>介绍的冒泡排序,选择排序看 ...

  2. Objective-C 装饰模式--简单介绍和使用

    装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 比如游戏机有一个GamePad类, 现在要增加一个作弊功能(例如100 ...

  3. Java 线程 — ThreadPoolExecutor

    线程池 线程池处理流程 核心线程池:创建新线程执行任务,需要获取全局锁 队列:将新来的任务加入队列 线程池:大于corePoolSize,并且队列已满,小于maxPoolSize,创建新的worker ...

  4. 【已解决】WinPhone模拟器报错:模拟器没法确定来宾虚拟机通信的主机ID地址。某些功能已被禁用

    先看警告 再看错误信息 计算机管理打不开就==>Win+R ==>compmgmt.msc 发现,dnt在管理员权限组里面,也在Hyper-V权限组里面 看看Hyper-V的驱动有木有被禁 ...

  5. FPGA Timing笔记

    很多FPGA工程师都会遇到timing的问题,如何让FPGA跑到更快的处理频率是永久话题.决定FPGA的timing关键是什么?如何才能跑到更快的频率呢? A. 第一步需要了解FPGA的timing路 ...

  6. SQL 联合查询 + XML解析

    (select a.EBILLNO, a.EMPNAME, a.APPLYDATE, b.HS_NAME, ), ),'') as SUMMARY, cast(c.XmlData as XML).va ...

  7. Razor Engine,实现代码生成器的又一件利器

    Razor Engine,之前仅仅是ASP.NET MVC的一种View引擎,目前已经完全成为一种可以独立使用的模版引擎,并且已经成为了CodePlex上一个开源的项目(http://razoreng ...

  8. golang操作文件

    1.读取文件信息: /* 读取文件信息 */ func readFile(path string) string { fi, err := os.Open(path) if err != nil { ...

  9. js实现可拖拽的div

    前言 下午忙里偷闲想写一个可拖拽的例子,留在脑海里一直都是三个事件mouseDown,mouseUp,mouseMove, 但从没有动手实践过,今天想起了自己实践了并学习了张鑫旭的demo实现. 学习 ...

  10. 帮公司人事MM做了个工资条拆分工具

    引言 偶尔一次午饭时人事说加班加到8点多,纯手工复制粘贴Excel的内容,公司大概150多人吧,每次发工资时都需要这样手动处理,将一个Excel拆分成150多个Excel,再把里面的内容粘过去,如此循 ...