MFC子窗口和父窗口(SetParent,SetOwner)_Qsir的博客-程序员宅基地

技术标签: 父窗口  子窗口  Windows  框架  

一、概念和区别

在windows系统中,每个窗口对象都对应有一个数据结构,形成一个list链表。系统的窗口管理器通过这个list来获取窗口信息和管理每个窗口。这个数据结构中有四个数据用来构建list,即child、sibling、parent、owner四个域。
所以我们可以看到,窗口之间的关系有两种:owner-owned 关系和 parent-child关系。前者称之为拥有/被拥有关系,后者称之为父/子关系。在这篇文字中,我把owner窗口称之所有者窗口。换句话说,一个窗口在有一个父窗口(parent)的同时,还可能被不同的窗口拥有(owner),也可以有自己的子窗口(child)。在MFC 的CWnd类中,所有者窗口保存在m_hWndOwner成员变量中,父窗口则保存在m_hParent中,但是这两个值并不一定和窗口对象数据结构中的值相对应。

窗口之间的关系,决定了窗口的外在表现。比如显示、销毁等。

如果一个窗口数据的owner域非NULL,则它和该窗口建立了owner-owned 关系,拥有关系决定了:
(1)被拥有的窗口永远显示在拥有它的那个窗口的前面;
(2)当所有者窗口最小化的时候,它所拥有的窗口都会被隐藏;
(3)当所有者窗口被销毁的时候,它所拥有的窗口都会被销毁。
需要注意的是,隐藏所有者窗口并不会影响它所拥有的窗口的可见状态。比如:如果窗口 A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候,窗口B被隐藏,但是窗口 C还是可见。


如果一个窗口的parent域非NULL,则它和该窗口之间就建立了parent-child关系。父子决定了:
(1)窗口在屏幕上面的显示位置。父窗口提供了用来定位子窗口的坐标系统,一个子窗口只能显示在它的父窗口的客户区中,之外的部分将被裁减。这个裁减法则决定了如果父窗口不可见,则子窗口肯定不可见。如果父窗口移动到了屏幕之外,子窗口也一样。
(2)当父窗口被隐藏时,它的所有子窗口也被隐藏。
(3)父窗口被销毁的时候,它所拥有的子窗口都会被销毁。
注意!最小化父窗口不会影响子窗口的可见状态,子窗口会随着父窗口被最小化,但是它的WS_VISIBLE属性不会变。

Windows系统为什么要使用两种关系呢?这是为了更加灵活的管理窗口。举个例子:组合框(combobox)的下拉列表框(list box)可以超出组合框的父窗口的客户区,这样有利于显示,因此系统创建该list box的时候,是作为控制台窗口(desktop window)的子窗口,它的父窗口hWndParent是NULL,这样,list box的显示区域是限制在整个屏幕内,但是该list box的所有者却是组合框的第一个非子窗口祖先(比如对话框),当它的所有者窗口销毁后,该 list box自动销毁。

另外,窗口之间消息的传递也和窗口关系有关,通常,一个窗口会把自己的通知消息发送给它的父窗口,但不全是这样,比如,CToolBar发送通知消息给它的所有者窗口而不是父窗口。这样以来,就可以允许工具条作为一个窗口(比如一个 OLE 容器程序窗口)的子窗口的同时,能够给另一个窗口(比如in-place框架窗口)发送消息。至于某类窗口到底是把消息发送给谁,是父窗口还是所有者窗口,microsoft并没有明示。还有,在现场(in-place)编辑的情况下,当一个 server 窗口激活或者失效的时候,框架窗口所拥有的子窗口自动隐藏或者显示,这也是通过直接调用SetOwner函数实现的。


二、窗口类型的说明和限制

(1)控制台窗口(desktop window)。这是系统最早创建的窗口。可以认为它是所有 WS_OVERLAPPED 类型窗口的所有者和父窗口。Kyle Marsh在他的文章“Win32 Window Hierarchy and Styles”中指出,当系统初始化的时候,它首先创建控制台窗口,大小覆盖整个屏幕。所有其它窗口都在这个控制台窗口上面显示。窗口管理器所用的窗口list中第一个就是这个控制台。它的下一层窗口叫做顶级窗口(top-level),顶级窗口是指所有非child、没有父窗口,或者父窗口是desktop的窗口,它们没有WS_CHILD属性。

(2)WS_OVERLAPPED类型的窗口可以显示在屏幕的任何地方。它们的所有者窗口是控制台。

Overlapped 类型的窗口属于顶级窗口,一般作为应用程序的主窗口。不论是否给出了WS_CAPTION、WS_BORDER属性,这类窗口创建后都有标题栏和边框。Overlapped窗口可以拥有其它顶级窗口或者被其它顶级窗口所拥有。所有overlapped窗口都有WS_CLIPSIBLINGS属性。系统可以自动设置 overlapped窗口的大小和初始位置。

当系统 shuts down的时候,它将销毁所有overlapped类型的窗口。

(3)WS_POPUP类型的窗口可以显示在屏幕任何地方,它们一般没有父窗口,但是如果明确调用SetParent,这类窗口也可以有父窗口。

WS_POPUP类型的窗口的所有者是在CreateWindow函数中通过设置hWndParent参数给定的,如果hWndParent不是子窗口,则该窗口就成为这个新的弹出式窗口的owner,否则,系统从hWndParent的父窗口向上找,直到找到第一个非子窗口,把它作为该弹出窗口的owner。当owner窗口销毁的时候,系统自动销毁这个弹出窗口。

Pop-up类型的窗口也属于顶级窗口,它和 overlapped 窗口的主要区别是弹出式窗口不需要有标题栏,也不必有边框。弹出式可以拥有其它顶级窗口或者被拥有。所有弹出式窗口也都有 WS_CLIPSIBLINGS属性。

(4)所有者窗口(owner)只能是 overlapped 或者 pop-up 类型的窗口,子窗口不能是所有者窗口,也就是说子窗口不能拥有其它窗口。

overlapped 或者 pop-up 类型的窗口在拥有其它窗口的同时,也可以被拥有。

在使用CreateWindowEx创建 WS_OVERLAPPED 或者 WS_POPUP类型的窗口时,可以在 hwndParent 参数中给出它的所有者窗口的句柄。如果 hwndParent 给出的是一个child 类型的窗口句柄,则系统自动将新创建窗口的所有权交给该子窗口的顶级父窗口。在这种情况下,参数hwndParent被保存在新建窗口的parent域中,而它的所有者窗口句柄则保存在owner域中。

(5)缺省情况下,对话框和消息框属于 owned 窗口,除非在创建它们的时候明确给出了WS_CHILD属性,(比如对话框中嵌入对话框的情形)
否则由系统负责给它们指定owner窗口。需要注意的是,一旦创建了owned类型的窗口,就无法再改变其所有关系,因为WIN32没有没有提供改变窗口所有者的方法。

而且在Win32中,由于有多线程的存在,所以要注意保证父子窗口或者owner/owned 窗口要同属于一个线程。

(6)对于 WS_CHILD类型的窗口,它的父窗口就是它的所有者窗口。一个子窗口的父窗口也是在CreateWindow函数中用hWndParent参数指定的。子窗口只能在父窗口的客户区中显示,并随父窗口一起销毁。
子窗口必须有一个父窗口,这是它和overlapped 以及 pop-up 窗口之间的主要区别。父窗口可以是顶级窗口,也可以是其它子窗口。


三、几个相关函数的说明

(1)获取/设置所有者窗口

win32 API提供了函数GetWindow函数(GW_OWNER 标志)来获取一个窗口的所有者窗口句柄。
GetWindow(hWnd, GW_OWNER)永远返回窗口的所有者(owner)。对于子窗口,函数返回 NULL,因为它们的父窗口就相当于所有者(注意,是“相当于”)。因为Windows系统没有维护子窗口的所有者信息。

MFC中则是通过如下函数得到所有者窗口指针:
_AFXWIN_INLINE CWnd* CWnd::GetOwner() const
{ return m_hWndOwner != NULL ? CWnd::FromHandle(m_hWndOwner) : GetParent(); }
从上述代码我们可以看出,它返回的值和GetWindow返回的有所区别,如果当前窗口没有owner,那么将返回它的父窗口指针。

但是Windows没有提供改变窗口所有者的方法。MFC中则提供了改变所有者的方法:
_AFXWIN_INLINE void CWnd::SetOwner(CWnd* pOwnerWnd)
{ m_hWndOwner = pOwnerWnd != NULL ? pOwnerWnd->m_hWnd : NULL; }

另外,mfc还提供了CWnd::GetSafeOwner( CWnd* pParent, HWND* pWndTop );函数,可以用来得到参数pParent的第一个非child属性的父窗口指针。如果这个参数是NULL,则返回当前线程的主窗口(通过AfxGetMainWnd得到)。框架经常使用这个函数查找对话框或者属性页的所有者窗口。

(2)获取/设置父窗口

WIN32 API给出了函数GetParent和SetParent。而mfc也是完全封装了这两个函数:

_AFXWIN_INLINE CWnd* CWnd::SetParent(CWnd* pWndNewParent)
{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::SetParent(m_hWnd,
pWndNewParent->GetSafeHwnd())); }

_AFXWIN_INLINE CWnd* CWnd::GetParent() const
{ ASSERT(::IsWindow(m_hWnd)); return CWnd::FromHandle(::GetParent(m_hWnd)); }

对于SetParent,msdn里面说明了父子窗口必须是同一个进程的。但是由于窗口句柄是系统全局唯一的,不属于同一个进程的情况下,也可以成功调用,但是后果未知。
GetParent的返回值比较复杂,对于overlapped类型的窗口,它返回0,对于WS_CHILD类型,它返回其父窗口,对于WS_POPUP类型,它返回其所有者窗口,如果想得到创建它时所传递进去的那个hwndParent参数,应该用GetWindowWord(GWW_HWNDPARENT)函数。

(3)GetWindowWord(hWnd, GWW_HWNDPARENT)返回一个窗口的父窗口,如果没有,则返回其所有者。

(4)上面谈到,当一个owner窗口被最小化后,系统自动隐藏它所拥有的窗口。当owner窗口被恢复的时候,系统自动显示它所拥有的窗口。在这两种情况下,系统都会发送(send)WM_SHOWWINDOW消息给被拥有的窗口。某些时候,我们可能需要隐藏 owned窗口,但并不想最小化其所有者窗口,这时候,可以通过ShowOwnedPopups函数来实现,该函数设置或者删除当前窗口所拥有的窗口的WS_VISIBLE属性,然后发送WM_SHOWWINDOW消息更新窗口显示。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Qsir/article/details/75622763

智能推荐

数据结构期末复习(五)_设有散列函数h和键值k1,k2,若k1≠k2 ,而h(k1)= =h(k2),则称这种现象为-程序员宅基地

1.选择题算法分析的两个主要方面是(A)。A.空间复杂性和时间复杂性 B.正确性和简明性C.可读性和文档性 D.数据复杂性和程序复杂性以下(C)是稀疏矩阵的压缩存储方法。A.一维数组 B.二维数组 C.三元组 D.广义表二叉树的叶结点个数与度为2的结点的个数的关系是(C)。A.无关 B.相等 C.多一个 D.少一个在一个图中,所_设有散列函数h和键值k1,k2,若k1≠k2 ,而h(k1)= =h(k2),则称这种现象为

ucGUI移植-程序员宅基地

本文使用的是单独移植ucGUI到STM32开发板来制作不涉及uCOS系统移植。在进行ucGUI移植前先简单介绍本次用到的ucGUI;众所周知,ucGUI是一种嵌入式系统中的图形设计软件,我们可以利用它来制作操作界面、图像等用于在显示器上显示;ucGUI以其高效的独立于处理器和ili9320(本设计的LCD控制器)的图形接口、优秀的灰度颜色管理和强大的窗口管理体系,在图形设计领域占据了一席之地。_ucgui移植

ModuleNotFoundError: No module named &#180sklearn.utils.linear_assignment_&#180_modulenotfounderror: no module named 'sklearn.util-程序员宅基地

问题从 sklearn.utils.linear_assignment_ 导入包 linear_assignmentfrom sklearn.utils.linear_assignment_ import linear_assignment报错No module named 'sklearn.utils.linear_assignment_'解决1.使用scipy.optimize.linear_sum_assignment 替代通过百度、谷歌都没有弄明白,基本上都是说sklearn没有安_modulenotfounderror: no module named 'sklearn.utils.linear_assignment_

SaltStack版本升级管理参考手册_centos7 查询salt minion版本-程序员宅基地

1、CentOS6系统下将salt-minion升级至最新版本的操作方法(截止2018.1,最新版本是2017.2)yum -y install https://repo.saltstack.com/yum/redhat/salt-repo-latest-2.el6.noarch.rpmyum -y update salt-minionps -ef|grep salt-minion_centos7 查询salt minion版本

springboot配置shiro多项目实现session共享的详细步骤_不同的springboot应用之间如何传递serializable sessionid-程序员宅基地

springboot配置shiro多项目实现session共享的详细步骤公司需要这样的需求:有两个项目master 主项目、suiteone 项目,两个项目各自由shiro 安全框架管理,当不能登录时,都无法访问,但当登录了其中一个,再访问另一个的时候不再需要登录即可访问。如果想看为什么需要共享session ,可以去看我这篇文章。[shiro框架—关于多项目之间验证为什么需要共享session]关于实现多项目共享session的逻辑介绍其实在上边的链接里我已经说明了,但是怕大家不去看,所以我就又_不同的springboot应用之间如何传递serializable sessionid

曲率圆与曲率半径_曲率圆半径怎么知道-程序员宅基地

曲率计算公式曲率圆与曲率半径_曲率圆半径怎么知道

随便推点

IDEA中配置maven出现Cannot resolve plugin org.apache.maven.plugins:maven-clean-plugin的解决方法-程序员宅基地

//出现这种错误只要在pom.xml文件中添加如下代码,同步maven后即可解决问题<profiles> <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true..._cannot resolve plugin org.apache.maven.plugins:maven-clean-plugin:3.2.0

去哪儿VS携程产品分析-程序员宅基地

一、背景介绍2014年,在线旅游市场交易规模达到3077亿元,同比增长38.9%;2014年出境游人次突破1亿人次,同比增长11%;2014年在线出境游市场规模达到224,同比增长72%(艾瑞检测数据)。相比较机票的渗透率来说,酒店尤其是境外酒店在线渗透率仍然具有较大提升空间。本文将就携程和去哪儿境外酒店的整个预定流程,包括搜索、查询、预定、支付整体流程中每个环节做出分析,由于关注点不同,可...

mybatis一级缓存和二级缓存详细介绍_mybatis一节缓存与二阶缓存介绍-程序员宅基地

mybatis一级缓存和二级缓存详细介绍今天整理啦mybatis一级缓存和二级缓存下面为大家介绍一下一·mybatis一级缓存(sqlSession级别的一个map)1.一级缓存是一直开启的,是本地缓存,是sqlsession级别的缓存,与数据库同义词会话期间查询到的数据回放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。2.一级缓存失效情况(没有使用到当..._mybatis一节缓存与二阶缓存介绍

前后端分离模式下前端与后端数据交互-程序员宅基地

下面举的例子就是使用jQuery Ajax和Python Flask进行前后端交互时,前端提交表单数据到后端,后端返回JSON数据给前端。前端GET提交表单数据:# GET请求var data = { "name": "test", "test": "test",};$.ajax({ type: 'GET', url: /your/url/, ...

微信小程序开发笔记(四)--- 绑定点击事件、input绑定触发事件(两种)和input取值_微信小程序 input 点击触发-程序员宅基地

前言本文为微信小程序开发笔记,现学现卖,正在学习中。如有发现问题或者发现有什么不足的地方,请大佬们多多指教!!!(开发自己的微信小程序中……)-目录 -绑定点击事件 -input绑定触发事件(两种)和input取值绑定点击事件 <view> <view> {{text}}</view> <button bindtap="changeText">按钮</button> </view>_微信小程序 input 点击触发

ubuntu16.04下如何配置terminator_ubuntu16.04修改terminator的主题_mahui85的博客-程序员宅基地

Terminator 安装Terminator 的安装非常方便,在 Ubuntu 中只需要用 apt 的包管理工具就能轻松地进行安装:$ sudo apt-get install terminatorUbuntu terminator 无法打开解决方案在使用Ubuntu的过程中,突然发现Ctrl + Alt + T无法打开terminator,但是Ubuntu本身的终端是可以打开的。重启电脑,重装terminator,都无效。后来发现是自己把ubuntu默认的python版本由2.x改为_ubuntu16.04修改terminator的主题