?? 19. 多重文件界面.txt
字號(hào):
多重文件界面
智能中國——游戲組 整理編譯
--------------------------------------------------------------------------------
多重文件接口(MDI)是Microsoft Windows文件處理應(yīng)用程序的一種規(guī)范,該規(guī)范描述了窗口結(jié)構(gòu)和允許使用者在單個(gè)應(yīng)用程序中使用多個(gè)文件的使用者接口(如文書處理程序中的文字文件和電子表格程序中的電子表格)。簡單地說,就像Windows在一個(gè)屏幕上維護(hù)多個(gè)應(yīng)用程序窗口一樣,MDI應(yīng)用程序在一個(gè)顯示區(qū)域內(nèi)維護(hù)多個(gè)文件窗口。Windows中的第一個(gè)MDI應(yīng)用程序是Windows下的Microsoft Excel的第一個(gè)版本。緊接著又出現(xiàn)了許多其它的應(yīng)用程序。
MDI 概念
盡管MDI規(guī)范隨著Windows 2.0的推出已經(jīng)很普及,但在那時(shí),MDI應(yīng)用程序?qū)懫饋砗芾щy,并且需要一些非常復(fù)雜的程序設(shè)計(jì)工作。從Windows 3.0起,其中許多工作就都由Windows為您做好了。Windows 95中增強(qiáng)的支持也已經(jīng)被添加進(jìn)Windows 98和Microsoft Windows NT中。
MDI 的組成
MDI程序的主應(yīng)用程序窗口是很普通的:它有一個(gè)標(biāo)題列、一個(gè)菜單、一個(gè)縮放邊框、一個(gè)系統(tǒng)菜單圖標(biāo)和最大化/最小化/關(guān)閉按鈕。顯示區(qū)域經(jīng)常被稱為「工作空間」,它不直接用于顯示程序輸出。這個(gè)工作空間包括零個(gè)或多個(gè)子窗口,每個(gè)窗口都顯示一個(gè)文件。
這些子窗口看起來與通常的應(yīng)用程序窗口以及MDI程序的主窗口很相似。它們有一個(gè)標(biāo)題列、一個(gè)縮放邊框、一個(gè)系統(tǒng)菜單圖標(biāo)和最大化/最小化/關(guān)閉按鈕,可能還包括滾動(dòng)條。但是文件窗口沒有菜單,主應(yīng)用程序窗口上的菜單適用于文件窗口。
在任何時(shí)候都只能有一個(gè)文件窗口是活動(dòng)的(加亮標(biāo)題列來表示),它出現(xiàn)在其它所有文件窗口之前。所有文件窗口都由工作空間區(qū)域加以剪裁,而不會(huì)出現(xiàn)在應(yīng)用程序窗口之外。
初看起來,對Windows程序?qū)懽髡邅碚f,MDI似乎是相當(dāng)簡單。需要程序?qū)懽髡咦龅墓ぷ骱孟窬褪菫槊總€(gè)文件建立一個(gè)WS_CHILD窗口,并使程序的主應(yīng)用程序窗口成為文件窗口的父窗口。但對現(xiàn)有的MDI應(yīng)用程序稍加研究,就會(huì)發(fā)現(xiàn)一些導(dǎo)致程序?qū)懽骼щy的復(fù)雜問題。例如:
MDI文件窗口可以最小化。它的圖示出現(xiàn)在工作空間的底部。一般來說,MDI應(yīng)用程序可以將不同的圖示分別用于主應(yīng)用程序窗口和每一類文件應(yīng)用。
MDI文件窗口可以最大化。在這種情況下,文件窗口的標(biāo)題列(一般用來顯示窗口中文件的文件名稱)消失,文件名稱出現(xiàn)在應(yīng)用程序窗口標(biāo)題列的應(yīng)用程序名稱之后,文件窗口的系統(tǒng)菜單圖標(biāo)成為應(yīng)用程序窗口的頂層菜單中的第一項(xiàng)。關(guān)閉文件窗口按鈕變成頂層菜單中的最后一項(xiàng),且出現(xiàn)在最右邊。
用以關(guān)閉文件窗口的系統(tǒng)鍵盤快捷鍵與關(guān)閉主窗口的系統(tǒng)鍵盤快捷鍵一樣,只是Ctrl鍵代替了Alt鍵。這也就是說,Alt+F4用于關(guān)閉應(yīng)用程序窗口,而Ctrl+F4用于關(guān)閉文件窗口。此外,Ctrl+F6可以在活動(dòng)MDI應(yīng)用程序的子文件窗口之間切換。與平時(shí)一樣,Alt+空格鍵啟動(dòng)主窗口的系統(tǒng)菜單,Alt+-(減號(hào))啟動(dòng)活動(dòng)子文件窗口的系統(tǒng)菜單。
當(dāng)使用光標(biāo)鍵在菜單項(xiàng)間移動(dòng)時(shí),控件權(quán)通常從系統(tǒng)菜單轉(zhuǎn)到菜單列中的第一項(xiàng)。在MDI應(yīng)用程序中,控件權(quán)是從應(yīng)用程序系統(tǒng)菜單轉(zhuǎn)到活動(dòng)文件系統(tǒng)菜單,然后再轉(zhuǎn)到菜單列中的第一項(xiàng)。
如果應(yīng)用程序能夠支持若干種型態(tài)的子窗口(如Microsoft Excel中的工作表和圖表文件),那么菜單應(yīng)能反映出與這種型態(tài)的文件有關(guān)的操作。這就要求當(dāng)不同的文字窗口變成活動(dòng)窗口時(shí),程序能更換菜單。此外,當(dāng)沒有文件窗口存在時(shí),菜單應(yīng)該被縮減到只剩下與打開新文件有關(guān)的操作。
頂層菜單上有一個(gè)叫做「窗口(Window)」的菜單項(xiàng)。按照習(xí)慣,這是頂層菜單上「Help」之前的那一項(xiàng),即倒數(shù)第二項(xiàng)。「窗口」子菜單上通常包含在工作空間內(nèi)安排文件窗口的選項(xiàng)。文件窗口可以從左上方開始「平鋪」或「層迭」。在前一種方式下,可以完整地看到每一個(gè)文件窗口。這個(gè)子菜單同時(shí)也包含所有文件窗口的列表。從中選擇一個(gè)文件窗口,就可以把此文件窗口移到前景。
Windows 98支持MDI的所有這些方面。當(dāng)然,需要您做一些工作(如下面的范例程序所示),但是,這遠(yuǎn)不是要您程序?qū)懽鱽碇苯又С炙羞@些功能。
MDI支援
探討Windows的MDI支持時(shí)需要發(fā)表一些新術(shù)語。主應(yīng)用程序窗口稱為「框架窗口」,就像傳統(tǒng)的Windows程序一樣,它是WS_OVERLAPPEDWINDOW樣式的窗口。
MDI應(yīng)用程序還根據(jù)預(yù)先定義的窗口類別MDICLIENT建立「客戶窗口」,這一客戶窗口是用這種窗口類別和WS_CHILD樣式呼叫CreateWindow來建立的。這一呼叫的最后一個(gè)參數(shù)是指向一個(gè)CLIENTCREATESTRUCT型態(tài)的結(jié)構(gòu)的指針。這個(gè)客戶窗口覆蓋框架窗口的顯示區(qū)域,并提供許多MDI支持。此客戶窗口的顏色是系統(tǒng)顏色COLOR_APPWORKSPACE。
文件窗口被稱為「子窗口」。通過初始化一個(gè)MDICREATESTRUCT型態(tài)的結(jié)構(gòu),以一個(gè)指向此結(jié)構(gòu)的指針為參數(shù)將消息WM_MDICREATE發(fā)送給客戶窗口,就可以建立這些文件窗口。
文件窗口是客戶窗口的子窗口,而客戶窗口又是框架窗口的子窗口。父-子窗口分層結(jié)構(gòu)如圖19-1所示。
圖19-1 Windows MDI應(yīng)用程序的父-子層次圖
您需要框架窗口的窗口類別(及窗口消息處理程序)和一個(gè)由應(yīng)用程序支持的每類子窗口的窗口類別(及窗口消息處理程序)。由于已經(jīng)預(yù)先注冊了窗口類別,所以不需要客戶窗口的窗口消息處理程序。
Windows 98的MDI支持包括一個(gè)窗口類別、五個(gè)函數(shù)、兩個(gè)數(shù)據(jù)結(jié)構(gòu)和12個(gè)消息。前面已經(jīng)提到了MDI窗口類別,即MDICLIENT,以及數(shù)據(jù)結(jié)構(gòu)CLIENTCREATESTRUCT和MDICREATESTRUCT。在MDI應(yīng)用程序中,這五個(gè)函數(shù)中的兩個(gè)用于取代DefWindowProc:不再將DefWindowProc呼叫用于所有未處理的消息,而是由框架窗口過程調(diào)用DefFrameProc,子窗口過程調(diào)用DefMDIChildProc。另一個(gè)MDI特有的函數(shù)TranslateMDISysAccel與第十章中討論的TranslateAccelerator的使用方式相同。MDI支持也包括ArrangeIconicWindows函數(shù),但有一條專用的MDI消息使得此函數(shù)對MDI程序來說不再必要。
第五個(gè)MDI函數(shù)是CreateMDIWindow,它使得子窗口可以在單獨(dú)的線程中被建立。這個(gè)函數(shù)不需要在單線程的程序中,我會(huì)展示這一點(diǎn)。
在下面的程序中,我將展示12條MDI消息中的9條(其它三個(gè)消息一般不用),這些消息的前綴是WM_MDI。框架窗口向客戶窗口發(fā)送其中某個(gè)消息,以便在子窗口上完成一項(xiàng)操作或者取得關(guān)于子窗口的信息(例如,框架窗口發(fā)送一個(gè)WM_MDICREATE消息給客戶窗口,以建立子窗口)。消息WM_MDIACTIVATE消息有點(diǎn)特別:框架窗口可以發(fā)送這個(gè)消息給客戶窗口來啟動(dòng)一個(gè)子窗口,而客戶窗口也把這個(gè)消息發(fā)送給將被啟動(dòng)或者失去活動(dòng)的子窗口,以便通知它們這一變化。
MDI 的范例程序
程序19-1 MDIDEMO程序說明了編寫MDI應(yīng)用程序的基本方法。
程序19-1 MDIDEMO
MDIDEMO.C
/*---------------------------------------------------------------------------
MDIDEMO.C -- Multiple-Document Interface Demonstration
(c) Charles Petzold, 1998
---------------------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
#define INIT_MENU_POS 0
#define HELLO_MENU_POS 2
#define RECT_MENU_POS 1
#define IDM_FIRSTCHILD 50000
LRESULT CALLBACK FrameWndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL CALLBACK CloseEnumProc (HWND, LPARAM) ;
LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK RectWndProc (HWND, UINT, WPARAM, LPARAM) ;
// structure for storing data unique to each Hello child window
typedef struct tagHELLODATA
{
UINT iColor ;
COLORREF clrText ;
}
HELLODATA, * PHELLODATA ;
// structure for storing data unique to each Rect child window
typedef struct tagRECTDATA
{
short cxClient ;
short cyClient ;
}
RECTDATA, * PRECTDATA ;
// global variables
TCHAR szAppName[] = TEXT ("MDIDemo") ;
TCHAR szFrameClass[] = TEXT ("MdiFrame") ;
TCHAR szHelloClass[] = TEXT ("MdiHelloChild") ;
TCHAR szRectClass[] = TEXT ("MdiRectChild") ;
HINSTANCE hInst ;
HMENU hMenuInit, hMenuHello, hMenuRect ;
HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HACCEL hAccel ;
HWND hwndFrame, hwndClient ;
MSG msg ;
WNDCLASS wndclass ;
hInst = hInstance ;
// Register the frame window class
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = FrameWndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szFrameClass ;
if (!RegisterClass (&wndclass))
{
MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
// Register the Hello child window class
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = HelloWndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = sizeof (HANDLE) ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szHelloClass ;
RegisterClass (&wndclass) ;
// Register the Rect child window class
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = RectWndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = sizeof (HANDLE) ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szRectClass ;
RegisterClass (&wndclass) ;
// Obtain handles to three possible menus & submenus
hMenuInit = LoadMenu (hInstance, TEXT ("MdiMenuInit")) ;
hMenuHello = LoadMenu (hInstance, TEXT ("MdiMenuHello")) ;
hMenuRect = LoadMenu (hInstance, TEXT ("MdiMenuRect")) ;
hMenuInitWindow = GetSubMenu (hMenuInit, INIT_MENU_POS) ;
hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;
hMenuRectWindow = GetSubMenu (hMenuRect, RECT_MENU_POS) ;
// Load accelerator table
hAccel = LoadAccelerators (hInstance, szAppName) ;
// Create the frame window
hwndFrame = CreateWindow (szFrameClass, TEXT ("MDI Demonstration"),
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, hMenuInit, hInstance, NULL) ;
hwndClient = GetWindow (hwndFrame, GW_CHILD) ;
ShowWindow (hwndFrame, iCmdShow) ;
UpdateWindow (hwndFrame) ;
// Enter the modified message loop
while (GetMessage (&msg, NULL, 0, 0))
{
if ( !TranslateMDISysAccel (hwndClient, &msg) &&
!TranslateAccelerator (hwndFrame, hAccel, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -