前面咱們所舉的例子中都是單文檔界面框架,也就是說這個窗口裏面的客戶區就是一個文檔界面,能夠編寫程序在裏面輸入或者繪製文本和圖形輸出,可是不能有出現多個文檔的狀況。好比下面的UltraEdit就是一個典型的多文檔界面,他能夠同時編輯多個文檔,每一個文檔還能夠最大化,最小化等等,咱們今天就來看看多文檔的基本框架是怎麼實現的。程序員
多文檔界面的框架建立須要幾下幾步。編程
主框架窗口建立windows
主框架窗的建立跟普通的窗口沒有什麼區別,就是本身註冊一個類並用該類建立一個重疊窗口,這個能夠用CreateWindow/CreateWindowEx函數完成,主框架窗口能夠用本身的菜單和狀態欄。微信
客戶區窗口建立微信公衆平臺
客戶區建立的建立一樣用你CreateWindow,但須要指定類爲「MDICLIENT」,用這個類會建立多文檔的客戶區窗口。或者採用CreateWindowEx函數,指定擴展風格爲WS_EX_MDICHILD。客戶區不須要菜單和狀態欄。框架
視圖窗口建立函數
建立工做或者視圖窗口做爲實際文檔窗口,這個也是須要本身註冊類並建立本身須要的視圖窗口。視圖窗口能夠有本身的菜單,通常不須要狀態欄。當視圖窗口激活時,主窗口的菜單須要替換成視圖窗口的菜單,由於這個時候每每是對視圖窗口進行操做,菜單替換也是理所固然的。菜單設置經過WM_MDISETMENU消息完成。該消息定義以下:工具
SendMessage(hWndControl, WM_MDISETMENU, wParam, lParam) wParam表示新框架窗口菜單, lParam表示要設置的菜單句柄
OK,基本的API就這些,下面咱們直接上demo程序來演示,因爲視圖通常是一個多文檔的實例,因此demo代碼中用類來存儲視圖,每個視圖就是一個類的實例:ui
主文件,該文件包括主框架窗口、客戶窗口已經視圖窗口調用:spa
#include <windows.h> #define CLIENT_WND_BGROUND #define IDM_FIRSTCHILD 1001 #define IDM_FILE_NEW 1002 HINSTANCE g_hInst; TCHAR szMDIMainFrame[]= TEXT("MDI Main Frame"); TCHAR gszAppName[] = TEXT("MDI Demo"); HWND ghWndMDIClient; HWND ghWndMainFrame; HMENU ghMainFrameMenu; void CreateChildWndMenu(void); HWND CreateDocView(HWND hParentWnd, HINSTANCE hInstance); static LRESULT CALLBACK MainFrameProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); #ifdef CLIENT_WND_BGROUND static WNDPROC pfOrigClientWndProc; LRESULT CALLBACK ClientWndSubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_ERASEBKGND: { RECT rect; HDC hDC = (HDC)wParam; GetClientRect(hwnd, &rect); HBRUSH hbr = CreateSolidBrush(RGB(125, 150, 125)); HBRUSH holdbr = (HBRUSH)SelectObject(hDC, hbr); FillRect(hDC, &rect, hbr); SelectObject(hDC, holdbr); DeleteObject(hbr); } return TRUE; default: break; } return CallWindowProc(pfOrigClientWndProc, hwnd, uMsg, wParam, lParam); } #endif HMENU CreateMainFrameMenu(void) { //主框架菜單 HMENU hMenu = CreateMenu(); //文件菜單 HMENU hFileMenu = CreateMenu(); AppendMenu(hFileMenu, MF_STRING, IDM_FILE_NEW, TEXT("&New")); AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, TEXT("&File")); return hMenu; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { MSG msg; WNDCLASSEX wndclass = {0}; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = MainFrameProc; wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszClassName = szMDIMainFrame; if(!RegisterClassEx(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), gszAppName, MB_ICONERROR); return 0; } g_hInst = hInstance; ghMainFrameMenu = CreateMainFrameMenu(); CreateChildWndMenu(); //Create the main MDI frame window ghWndMainFrame = CreateWindow(szMDIMainFrame, gszAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, // allows system choose an x position CW_USEDEFAULT, // allows system choose a y position 400, 300, NULL, // handle to parent window ghMainFrameMenu,// handle to menu hInstance, // handle to the instance of module NULL); // Long pointer to a value to be passed to the window through the ShowWindow(ghWndMainFrame, SW_SHOW); UpdateWindow(ghWndMainFrame); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg) ; DispatchMessage(&msg) ; } return msg.wParam ; } LRESULT CALLBACK MainFrameProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_CREATE: { CLIENTCREATESTRUCT ccs; ccs.hWindowMenu = NULL; ccs.idFirstChild = IDM_FIRSTCHILD; ghWndMDIClient = CreateWindow(TEXT("MDICLIENT"), NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, g_hInst, (void *)&ccs); #ifdef CLIENT_WND_BGROUND pfOrigClientWndProc = (WNDPROC)SetWindowLong(ghWndMDIClient, GWL_WNDPROC, (LONG)ClientWndSubClassProc); #endif } return 0; case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_FILE_NEW: CreateDocView(ghWndMDIClient, g_hInst); return 0; } break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefFrameProc(hWnd, ghWndMDIClient, message, wParam, lParam); }
下面是視圖窗口,每一個視圖窗口由一個類來描述,本文僅僅是一個demo,只有實例化一個視圖窗口做爲演示:
#include <windows.h> #define IDM_EDIT_COPY 1003 #define IDM_EDIT_PASTE 1004 class CViewInfo { public: CViewInfo(HINSTANCE hInst); ~CViewInfo(); HWND CreateViewWindow(HWND hParentWnd); private: HINSTANCE m_hInst; HWND m_hWndView; }; static TCHAR szViewWndName[] = TEXT("View Window"); static CViewInfo *g_pSystemInfo; static HMENU ghChildWndMenu; extern HMENU ghMainFrameMenu; CViewInfo::CViewInfo(HINSTANCE hInst) { m_hWndView = NULL; m_hInst = hInst; } CViewInfo::~CViewInfo() { m_hWndView = NULL; } LRESULT CALLBACK ViewWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_MDIACTIVATE: { HWND hWndClient = GetParent(hWnd); HWND hWndFrame = GetParent(hWndClient); HMENU hMenu = GetSubMenu(ghChildWndMenu, 0); if (lParam == (LPARAM)hWnd) { SendMessage(hWndClient, WM_MDISETMENU, (WPARAM)ghChildWndMenu, (LPARAM)hMenu); EnableMenuItem(ghChildWndMenu, IDM_EDIT_COPY, MF_GRAYED); } // Set the framewindow menu if losing focus if (lParam != (LPARAM)hWnd) { SendMessage(hWndClient, WM_MDISETMENU, (WPARAM)ghMainFrameMenu, (LPARAM)NULL); } // call DrawMenuBar after the menu items are set DrawMenuBar(hWndFrame); return 0 ; } case WM_CLOSE: delete g_pSystemInfo; break; case WM_DESTROY: return 0; } return DefMDIChildProc(hWnd, message, wParam, lParam); //Frame window calls DefFrameProc rather than DefWindowProc } HWND CViewInfo::CreateViewWindow(HWND hParentWnd) { WNDCLASSEX wcDoc; wcDoc.cbSize = sizeof(WNDCLASSEX); wcDoc.style = CS_HREDRAW | CS_VREDRAW; wcDoc.lpfnWndProc = ViewWindowProc; wcDoc.cbClsExtra = 0; wcDoc.cbWndExtra = 0; wcDoc.hInstance = m_hInst; wcDoc.hIcon = NULL; wcDoc.hCursor = LoadCursor(NULL, IDC_ARROW); wcDoc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wcDoc.lpszMenuName = NULL; wcDoc.lpszClassName = szViewWndName; wcDoc.hIconSm = NULL; if(!RegisterClassEx(&wcDoc)) { DWORD dw_LastError = GetLastError(); if(ERROR_CLASS_ALREADY_EXISTS != dw_LastError) { return 0; } } m_hWndView = CreateWindowEx(WS_EX_MDICHILD, szViewWndName, TEXT("New view"), 0, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, hParentWnd, NULL, m_hInst, NULL); return m_hWndView; } HWND CreateDocView(HWND hParentWnd, HINSTANCE hInstance) { HWND hView; g_pSystemInfo = new CViewInfo(hInstance); hView = g_pSystemInfo->CreateViewWindow(hParentWnd); if (hView == NULL) { delete g_pSystemInfo; g_pSystemInfo = NULL; } return hView; } void CreateChildWndMenu(void) { //View菜單 ghChildWndMenu = CreateMenu(); //編輯菜單 HMENU hEditMenu = CreateMenu(); AppendMenu(hEditMenu, MF_STRING, IDM_EDIT_COPY, TEXT("&Copy")); AppendMenu(hEditMenu, MF_STRING, IDM_EDIT_PASTE, TEXT("&Paste")); AppendMenu(ghChildWndMenu, MF_POPUP, (UINT_PTR)hEditMenu, TEXT("&Edit")); }
本實例運行後,界面以下:
選擇File->New新建一個視圖後demo程序以下,能夠看到菜單編程視圖的菜單:
最大化後能夠看到視圖窗口和填滿客戶窗口:
雖然本程序只實現了一個視圖的實例,可是再增長一個是很容易的,只要想辦法在菜單中調用CreateDocView函數,而且正確處理對象指針便可。實例並無增長狀態欄,由於這個對多文檔並非必須的,要增長的讀者能夠參考前面的建立Toolbar和Statusbar一文。
本實例實現了一個基本的多文檔窗口框架,讀者朋友能夠在此基礎上加上工具欄、狀態欄、視圖窗口建立對類的處理,多實例以及具體的需求,完成實用化的多文檔界面。
更多經驗交流能夠加入Windows編程討論QQ羣:454398517。
關注微信公衆平臺:程序員互動聯盟(coder_online),你能夠第一時間獲取原創技術文章,和(Java/C/C++/Android/Windows/Linux)技術大牛作朋友,在線交流編程經驗,獲取編程基礎知識,解決編程問題。程序員互動聯盟,開發人員本身的家。
轉載請註明出處http://www.coderonline.net/?p=2052,謝謝合做!