視窗

視窗是在使用者界面上的一個可見的範圍。視窗一般都是長方形的。它包含著各種控件，是用作輸入和輸出的界面。雖然視窗一般用於圖形使用者界面，但又有時用於命令行界面的。而一個使用視窗為主要用戶界面的系統則稱為視窗系統。這個想法源於施乐帕洛阿尔托研究中心。 在視窗系統上，視窗一般被畫成一些二維的物件，被安置在桌面之上。大多數的視窗都可以隨使用者的意願而更改大小、移動、隱藏、回復和關閉。當兩個視窗重疊的時候，就如日常生活中一樣，其中一個要位於另一個之上，而下方的則會有些地方被上方個視窗所遮蓋。而視窗系統中管理這個操作的部份，則叫做視窗管理員。 視窗是數種圖形使用者界面中的一種重要的widget。當中有VMS的DEC Windows、GNU/Unix-like系統的X Window系統、Microsoft Windows和Open Windows。 这个概念是由斯坦福大學研究所（英语：SRI International）(由 道格拉斯·恩格尔巴特 领导）开发实现的。 他们最早的系统支持多窗口，但没明显的方法显示它们的分界线（比如窗口边框、标题栏等）。 研究在 Xerox 公司的 Palo Alto 研究中心 / PARC （由 艾伦·凯领导）中继续。他们使用了重叠的窗口。 在1980年代，PARC 提出术语"WIMP"，分别代表了窗口、图标、菜单、指针。 那时Apple与PARC进行了简单的合作。Apple 根据 PARC 的界面开发了自己的新界面。它首次应用是在 Apple的 Lisa上，此后用于 Macintosh 电脑。与此同时，Microsoft在为"Mac"开发办公软件。之后他们在 Apple 系统的基础上开发了自己的窗口系统。 窗口子类化是对一个窗口实例通过GetWindowLong和SetWindowLong将窗口过程地址修改为一个新函数地址，新的窗口过程函数处理那些感兴趣的消息，将其它消息传递给原窗口过程。即实例子类化。在某控件已经创建的情况下，为了获得窗口消息，必须子类化它。子类化不需要创建一个完整的新窗口类，只需要拦截单个窗口。 窗口超类化是一个已存在的窗口类（WNDCLASS或WNDCLASSEX），改变窗口类的特征。仅影响窗口类新创建的窗口实例，不会影响窗口过程地址修改前已经创建的窗口。超类化只能改变用户自创的窗口的特性，不能用于系统创建的窗口(如对话框上的标准按钮）。超类话过程示例如下： WNDCLASSEX wc; wc.cbsize=sizeof(wc); GetClassInfoEx(hinst,"XXXX",&wc); //hinst---定义窗口类XXXX的模块的句柄，如为系统定义的窗口类(如: Edit,Button)则hinst=NULL wc.lpszClassName="YYYY";//必须改变窗口类的名字 wc.hbrBackground=CreateSolidBrush(RGB(0,0,0)); wc.lpfnWndProc=NewWndProc;//改变窗口过程 RegisterClassEx(&wc); MFC的CWindowImplBaseT::SubclassWindow用于动态地把一个窗口实例绑定到当前的CWnd对象，消息循环首先调用CWnd对象的窗口过程，传递给基类的消息将被默认的窗口过程处理。CWnd::SubclassDlgItem用于把对话框的一个控件子类化绑定到一个基于CWnd的类对象。CWindowImplBaseT::UnsubclassWindow恢复一个被子类化的窗口。 模板类CWindowImplBaseT提供一个数据成员WNDPROC m_pfnSuperWindowProc并且初始化为 ::DefWindowProc.然而在窗口超类化处理时它存储了已注册窗口类的窗口过程，在窗口子类化时它保存窗口实例句柄原有的窗口过程。 CWnd::CreateEx在创建窗口前调用SetWindowsHookEx函数安装了一个钩子函数_AfxCbtFilterHook。窗口刚创建好，钩子函数_AfxCbtFilterHook就被调用。_AfxCbtFilterHook调用SetWindowLong将窗口过程替换为AfxWndProcBase，并将SetWindowLong返回的原窗口地址保存到成员变量oldWndProc。可见，通过CWnd::CreateEx创建的所有窗口都会被子类化。在DefWindowProc函数中，消息会传给子类化时保存的原窗口地址oldWndProc。 ATL提供DECLARE_WND_SUPERCLASS来支持超类化。 使用Windows API函数CreateWindowEx，可以指定创建的窗口的类型： 层叠窗口 Overlapped Window：一种顶层（top-level)窗口，作为应用程序的主窗口。有标题条、边界与客户区，可以有菜单、最大最小化按钮、滚动条、可改变窗体尺寸的边界（sizing border）。使用CreateWindowEx函数指定WS_OVERLAPPED 或 WS_OVERLAPPEDWINDOW风格。 弹出窗口 Pop-up Window：另一种顶层窗口，用于对话框、MessageBox，以及其他出现在应用程序主窗口之外的临时窗口。标题条可选，除此以外与层叠窗口相同。使用CreateWindowEx函数，指定WS_POPUP风格。如果需要标题条，指出WS_CAPTION风格。使用WS_POPUPWINDOW风格有border与window菜单。弹出窗口可以拥有其他顶层窗口；顶层窗口也可以拥有弹出窗口；前述二者可以同时成立。弹出窗口总是有WS_CLIPSIBLINGS风格。弹出窗口的尺寸或定位参数不能使用CW_USEDEFAULT，否则尺寸、定位被设为0。 顶层窗口 top-level window：就是“非Child window”。其parent恒定是Desktop。总是有WS_CLIPSIBLINGS风格。 子窗口 Child Window：出现在父窗口（parent window）的客户区中，超出的部分不可见。子窗口一定有Parent，一定没有Owner。非子窗口的Parent一定是桌面，它们不一定有Owner。典型用于把父窗口的客户区分割成一些子区域。使用CreateWindowEx函数指定WS_CHILD风格。父窗口可以是Overlapped、Pop-up、其他子窗口。子窗口的唯一必要特性是有客户区；不能有菜单；其他特性可选。定位子窗口时总是用父窗口客户区左上角坐标系。Destroyed、Hidden、Moved、Shown等窗口操作与父窗口一致。父窗口如果指定了WS_CLIPCHILDREN风格，则父窗口不能在子窗口上绘制；否则重绘父窗口会覆盖子窗口的内容。兄弟子窗口（sibling window）可以在彼此重叠的客户区上绘制，除非指定了WS_CLIPSIBLINGS风格，则不论子窗口之间z-order，与其他子窗口重叠区域不被绘制。不能使用CW_USEDEFAULT，否则将没有尺寸、没有定位位置。子窗口的输入消息直接发给子窗口，并不传递给父窗口。但子窗口被函数EnableWindow 禁止时，系统把子窗口的输入消息发给父窗口。在子窗口上点击鼠标，子窗口收到WM_LMOUSEBUTTONDOWN消息；如果主窗口没有键盘输入焦点（focus），则主窗口获得输入焦点（WM_SETFOCUS）；如果兄弟子窗口有输入焦点，则当前子窗口获得输入焦点。子窗口有独一无二整数标识符。应用程序通过空间的整数标识符给它发消息。控件给父窗口发送notification消息时，用整数标识符来表示自身。 Layered Window：带有WS_EX_LAYERED风格的窗口就是分层窗口，用于实现异形窗口和窗口整体透明。异形窗口要把控件的透明色和窗口后面的图像进行融合，需要用UpdateLayeredWindow函数来绘制。窗口整体透明，需要用SetLayeredWindowAttribute函数设置透明度。调用UpdateLayeredWindow函数后，WM_PAINT消息将失效；更新窗口需要自行调用UpdateLayeredWindow函数。子窗口无法应用WS_EX_LAYERED风格。子窗口如果需要是异形或者整体透明，只能去掉WS_CHILD风格，作为popup窗口，然后MOVE到一定位置，在父窗口移动的时候跟随移动。 Message-Only Window，允许发送/接收Windows消息，不可见，没有z-order，不能被枚举，不接收广播消息。CreateWindowEx函数的参数hWndParent为HWND_MESSAGE常量或者一个已存在的message-only窗口的句柄。对已经存在的窗口，通过函数SetParent的参数hWndNewParent为HWND_MESSAGE，把该窗体修改为message-only。为搜到message-only窗口，调用函数FindWindowEx指定参数hwndParent为HWND_MESSAGE。此外，函数FindWindowEx搜索message-only窗口与顶层层口，如果参数hwndParent与hwndChildAfter都为NULL。 Owner/Owned Window：Overlapped Window/Pop-up Window可以被其他Overlapped Window/Pop-up Window拥有。子窗口一定没有Owner。这种关系具有如下特性：在z-order上，Owned总是在Owner之上。当Owner被摧毁时，系统自动摧毁它的所有Owned。当Owner极小化时，Owned自动隐藏（没有了WS_VISIBLE），不显示在任务栏上；但Owned窗口自身拥有的窗口不会自动隐藏，即没有传递性。Owner隐藏（SW_HIDE），不会影响其拥有的窗口。CreateWindowEx函数创建具有WS_OVERLAPPED或WS_POPUP风格的窗口时，通过参数hwndParent指定Owner。对话框与消息框默认都是Owned。通过GetWindow函数指出GW_OWNER标志获取窗口的Owner的句柄。 Owner Window：Owner window不能是Child window。 Child window一定没有Owner，因此Owner window是对popup和Overlapped而言。popup和Overlapped不一定有Owner。Owner为NULL的非Child窗口可以在任务栏上出现它们的按钮。 Owned Window top most window：具有“WS_EX_TOPMOST”属性。总是显示在其它非top most窗口的上面。同时是top most的窗口是“公平竞争”的关系。owner为top most，则其全部owned都变成了top most。child不能是top most窗口。 GetParent函数，对于子窗口返回其父窗口句柄；对于弹出窗口，返回拥有者窗口句柄；对于层叠窗口，返回0. GetWindow函数，指出GW_OWNER参数，则可以返回弹出窗口或层叠窗口的拥有者窗口句柄。 FindWindowEx函数，用于找到指定父窗口、指定子窗口的下一个子窗口。 WNDCLASS中cbClsExtra和cbWndExtra，分别用于指定附加于窗口类与窗口实例的额外存储空间字节大小，不能超过40字节。分别类似于C++类中的static变量与类似于C++类中的成员变量。 GetClassLong(hwnd,GCL_CBCLSEXTRA)获取的是类附加空间的大小，即cbClsExtra的值。GetClassLong(hwnd,GCL_CBWNDEXTRA)获取的是窗口附加空间的大小，即cbWndExtra的值。 使用方法： 访问类附加空间的数据GetClassLong(hwnd,index)，index表示访问附加空间以0开始的索引。 设置类附加空间的数据SetClassLong(hwnd,index,value)，index表示访问附加空间以0开始的索引，value表示要设置的值。 访问窗口附加空间的数据GetWindowLong(hwnd,index)，index表示访问附加空间以0开始的索引。 设置窗口附加空间的数据SetWindowLong(hwnd,index,value)，index表示访问附加空间以0开始的索引，value表示要设置的值。 其它相关Windows API有：GetWindowWord、GetWindowLongPtr、SetWindowWord、SetWindowLongPtr。 视窗系统中管理视窗的部分被成为"窗口管理器" 常见的窗口系统： X Window系統 Microsoft Windows Reimer, Jeremy. A History of the GUI (Part 1). Ars Technica. 2005 [2009-09-14]. （原始内容存档于2013-07-07）.  Reimer, Jeremy. A History of the GUI (Part 2). Ars Technica. 2005 [2009-09-14]. （原始内容存档于2013-07-07）.  引用错误：带有name属性“ArsTechnica2”的<ref>标签用不同内容定义了多次 Milestones: 1975 - Graphical User Interface (GUI). Palo Alto Research Center Incorporated. [2009-09-14]. （原始内容存档于2009-09-04）.  Barger, Jorn. Linux desktops (GUIs, widgets, window managers, etc). 2002 [2009-09-14]. （原始内容存档于2009-08-08）.