• 【Windows编程】系列第十一篇:多文档界面框架


    前面我们所举的例子中都是单文档界面框架,也就是说这个窗口里面的客户区就是一个文档界面,可以编写程序在里面输入或者绘制文本图形输出,但是不能有出现多个文档的情况。比如下面的UltraEdit就是一个典型的多文档界面,他可以同时编辑多个文档,每个文档还可以最大化,最小化等等,我们今天就来看看多文档的基本框架是怎么实现的。

    ultra_mdi1

    多文档界面的框架创建需要几下几步。

    • 主框架窗口创建

    主框架窗的创建跟普通的窗口没有什么区别,就是自己注册一个类并用该类创建一个重叠窗口,这个可以用CreateWindow/CreateWindowEx函数完成,主框架窗口可以用自己的菜单和状态栏。

    • 客户区窗口创建

    客户区创建的创建同样用你CreateWindow,但需要指定类为“MDICLIENT”,用这个类会创建多文档的客户区窗口。或者采用CreateWindowEx函数,指定扩展风格为WS_EX_MDICHILD。客户区不需要菜单和状态栏。

    • 视图窗口创建

    创建工作或者视图窗口作为实际文档窗口,这个也是需要自己注册类并创建自己需要的视图窗口。视图窗口可以有自己的菜单,一般不需要状态栏。当视图窗口激活时,主窗口的菜单需要替换成视图窗口的菜单,因为这个时候往往是对视图窗口进行操作,菜单替换也是理所当然的。菜单设置通过WM_MDISETMENU消息完成。该消息定义如下:

    SendMessage(hWndControl, WM_MDISETMENU, wParam, lParam)
    wParam表示新框架窗口菜单,
    lParam表示要设置的菜单句柄

    OK,基本的API就这些,下面我们直接上demo程序来演示,由于视图一般是一个多文档的实例,所以demo代码中用类来存储视图,每一个视图就是一个类的实例:

    主文件,该文件包括主框架窗口、客户窗口已经视图窗口调用:

    #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,谢谢合作!

  • 相关阅读:
    混合 App 打开 H5 调试开关
    国内申请苹果美区ID
    windows鼠标右键文件太多
    已经配置好了的 jmeter + ant 框架
    fiddler的使用:抓包定位、模拟弱网
    jmeter响应数据中文乱码处理
    jmeter设置中文语言
    Jmeter使用CSV Data参数化,中文参数传递过程出现乱码问题
    monkey详解
    Chrome F12 谷歌开发者工具解析
  • 原文地址:https://www.cnblogs.com/yangdanny/p/4781701.html
Copyright © 2020-2023  润新知