視窗
視窗是在使用者介面上的一個可見的範圍。視窗一般都是長方形的。它包含著各種控制項,是用作輸入和輸出的介面。雖然視窗一般用於圖形使用者介面,但又有時用於命令列介面的。而一個使用視窗為主要使用者介面的系統則稱為視窗系統。這個想法源於全錄帕洛阿爾托研究中心。
在視窗系統上,視窗一般被畫成一些二維的物件,被安置在桌面之上。大多數的視窗都可以隨使用者的意願而更改大小、移動、隱藏、回復和關閉。當兩個視窗重疊的時候,就如日常生活中一樣,其中一個要位於另一個之上,而下方的則會有些地方被上方個視窗所遮蓋。而視窗系統中管理這個操作的部份,則叫做視窗管理員。
視窗是數種圖形使用者介面中的一種重要的widget。當中有VMS的DEC Windows、GNU/Unix-like系統的X視窗系統、Microsoft Windows和Open Windows。
歷史
這個概念是由斯坦福大學研究所(由 道格拉斯·恩格爾巴特 領導)開發實現的。[1] 他們最早的系統支援多窗口,但沒明顯的方法顯示它們的分界線(比如窗口邊框、標題列等)。[2]
研究在 Xerox 公司的 Palo Alto 研究中心 / PARC (由 艾倫·凱領導)中繼續。他們使用了重疊的窗口。[3]
在1980年代,PARC 提出術語"WIMP",分別代表了窗口、圖示、選單、指標。
那時Apple與PARC進行了簡單的合作。Apple 根據 PARC 的介面開發了自己的新介面。它首次應用是在 Apple的 Lisa上,此後用於 Macintosh 電腦。與此同時,Microsoft在為"Mac"開發辦公軟體。之後他們在 Apple 系統的基礎上開發了自己的窗口系統。[2]
子類化與超類化
窗口子類化是對一個窗口實例通過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的子類化與超類化
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窗口類型
使用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函式,用於找到指定父窗口、指定子窗口的下一個子窗口。
Windows窗口類與窗口實例的額外儲存空間
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。
視窗管理員
常見的窗口系統:
參考文獻
- ^ Reimer, Jeremy. A History of the GUI (Part 1). Ars Technica. 2005 [2009-09-14]. (原始內容存檔於2013-07-07).
- ^ 2.0 2.1 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).