【Windows編程】系列第十一篇:多文檔界面框架

前面咱們所舉的例子中都是單文檔界面框架,也就是說這個窗口裏面的客戶區就是一個文檔界面,能夠編寫程序在裏面輸入或者繪製文本圖形輸出,可是不能有出現多個文檔的狀況。好比下面的UltraEdit就是一個典型的多文檔界面,他能夠同時編輯多個文檔,每一個文檔還能夠最大化,最小化等等,咱們今天就來看看多文檔的基本框架是怎麼實現的。程序員

ultra_mdi1

多文檔界面的框架建立須要幾下幾步。編程

  • 主框架窗口建立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"));
}

本實例運行後,界面以下:

mdi0

選擇File->New新建一個視圖後demo程序以下,能夠看到菜單編程視圖的菜單:

mdi1

最大化後能夠看到視圖窗口和填滿客戶窗口:

mdi2

雖然本程序只實現了一個視圖的實例,可是再增長一個是很容易的,只要想辦法在菜單中調用CreateDocView函數,而且正確處理對象指針便可。實例並無增長狀態欄,由於這個對多文檔並非必須的,要增長的讀者能夠參考前面的建立Toolbar和Statusbar一文。

本實例實現了一個基本的多文檔窗口框架,讀者朋友能夠在此基礎上加上工具欄、狀態欄、視圖窗口建立對類的處理,多實例以及具體的需求,完成實用化的多文檔界面。

 

更多經驗交流能夠加入Windows編程討論QQ羣454398517

 

關注微信公衆平臺:程序員互動聯盟(coder_online),你能夠第一時間獲取原創技術文章,和(Java/C/C++/Android/Windows/Linux)技術大牛作朋友,在線交流編程經驗,獲取編程基礎知識,解決編程問題。程序員互動聯盟,開發人員本身的家。

【Windows編程】系列第十篇:文本插入符

轉載請註明出處http://www.coderonline.net/?p=2052,謝謝合做!

相關文章
相關標籤/搜索