博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Windows编程】系列第十一篇:多文档界面框架
阅读量:6888 次
发布时间:2019-06-27

本文共 8612 字,大约阅读时间需要 28 分钟。

hot3.png

前面我们所举的例子中都是,也就是说这个窗口里面的客户区就是一个文档界面,可以编写程序在里面输入或者和,但是不能有出现多个文档的情况。比如下面的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 
#define CLIENT_WND_BGROUND#define IDM_FIRSTCHILD 1001#define IDM_FILE_NEW   1002HINSTANCE 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_BGROUNDstatic 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); }#endifHMENU 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 
#define IDM_EDIT_COPY 1003#define IDM_EDIT_PASTE 1004class 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程序如下,可以看到菜单编程视图的菜单:

最大化后可以看到视图窗口和填满客户窗口:

mdi2

虽然本程序只实现了一个视图的实例,但是再增加一个是很容易的,只要想办法在菜单中调用CreateDocView函数,并且正确处理对象指针即可。实例并没有增加状态栏,因为这个对多文档并不是必须的,要增加的读者可以参考前面的一文。

本实例实现了一个基本的多文档窗口框架,读者朋友可以在此基础上加上工具栏、状态栏、视图窗口创建对类的处理,多实例以及具体的需求,完成实用化的多文档界面。

 

更多经验交流可以加入Windows编程讨论QQ群454398517

 

关注微信公众平台:(coder_online),你可以第一时间获取原创技术文章,和(Java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。,开发人员自己的家。

转载请注明出处,谢谢合作!

转载于:https://my.oschina.net/u/2336532/blog/501102

你可能感兴趣的文章
编译安装Centos7.2+Nginx1.12+PHP7.2.10+Mysql5.6.16
查看>>
React Native教程 1:初识React Native,了解index.ios.js里的结构
查看>>
Python - 类(class)的构造器(__init__) 详解 及 代码
查看>>
hadoop wordcount 、wordmean代码阅读与分析
查看>>
我的友情链接
查看>>
空间大小如何划分
查看>>
mybatis的sql 缓存,去除mybatis缓存
查看>>
做一个正能量满满的人
查看>>
jquery制作滑动面板
查看>>
PHP 编译错误及解决办法
查看>>
关于LIUNX的文本编辑器
查看>>
RHEL6 部署 ORACLE 11gR2
查看>>
Android项目使用support v7时遇到的各种问题
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
c_str函数
查看>>
Linux 用户跟用户组
查看>>
Stale NFS file handle的解决方法
查看>>
mysql之主从复制篇
查看>>
给程序员介绍一些C++开源库
查看>>