原文链接: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. Java学习笔记(03)

    一.回顾运算符: 一.控制语句 1.1 顺序结构 (最常见的) 特点:代码从上往下依次执行

  2. Mysql命令show global status求根溯源

    近来,发现好多公司对mysql的性能监控是通过show global status实现的,因此对于这个命令想要探究一番,看他是否是实时更新的. 在此之前,我们必须搞明白mysql对于这个命令的执行过程 ...

  3. OpenCascade Chinese Text Rendering

    OpenCascade Chinese Text Rendering eryar@163.com Abstract. OpenCascade uses advanced text rendering ...

  4. WindowsError的错误代码详解

    0操作成功完成. 1功能错误. 2系统找不到指定的文件. 3系统找不到指定的路径. 4系统无法打开文件. 5拒绝访问. 6句柄无效. 7存储控制块被损坏. 8存储空间不足,无法处理此命令. 9存储控制 ...

  5. 【开源】OSharp框架解说系列(5.1):EntityFramework数据层设计

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  6. 通过3个Hello World应用来了解ASP.NET 5应用是如何运行的(2)

    对于上面创建的这个Hello World应用来说,程序入口点由应用自身来提供,所以应用本身具有自我执行的能力.从应用托管(Host)的角度来讲,这样的应用同时负责对自身的托管.将应用与托管环境独立起来 ...

  7. EF7 - What Does “Code First Only” Really Mean

    这篇文章很有价值,但翻译了一段,实在翻译不下去了,没办法,只能转载了. 英文地址:http://blogs.msdn.com/b/adonet/archive/2014/10/21/ef7-what- ...

  8. Sublime写MarkDown实时预览

    [TOC] Sublime写MarkDown实时预览 Sublime作为神器,实至名归. 首先 1.安装Sublime,并安装Package Control,这里不多说. 2.安装MarkDown P ...

  9. 相克军_Oracle体系_随堂笔记006-日志原理

    简单来说,学习Oracle数据库就两个目标: 保证数据库数据的一致性: 提高数据库的性能(这个和日志没关系).   日志的功能:     只是保证数据库数据的一致性:   1.Oracle日志原理   ...

  10. 解析C#类中的构造函数

    <解析C#类中的构造函数> 一.  C#中的构造函数概述: C#中类包含数据成员和函数成员.函数成员提供了操作类中数据的某些功能,包括方法.属性.构造器和终结器.运算符和索引器. 构造函数 ...