65.9K
CodeProject 正在变化。 阅读更多。
Home

多重 MDI

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.83/5 (8投票s)

2006年5月24日

GPL3

3分钟阅读

viewsIcon

29519

downloadIcon

1258

为您的 WTL 应用程序提供一些样板代码。

Sample screen

引言

好的,我们同意 WTL 是进行现代 UI 开发的正确选择,特别是当您想保持原生、精简且高效时。不是吗? WTL 向导可以很好地为您选择的应用程序类型提供样板代码,包括 MDI,这也是本文的重点。 话虽如此,只有一件事困扰着我:您获得的 MDI 文档应用程序允许您使用同一模板的多个副本,也就是说,您只能拥有一种类型的文档/窗体/您选择的任何窗口(顺便说一句,MFC 对应的向导也为您提供了几乎相同的东西),因此一遍又一遍地获得相同的功能。 我的意思是,这有用吗? 但当然,这个框架将允许您开发类似 Office 的应用程序。

问题在于,MS Word、MS Excel 等都已经开发出来了,坦白说,普通程序员不太可能为它们开发下一代替代品。 在现实世界中,我们最有可能从事开发处理特定任务的应用程序,这些任务最终将需要独立的窗口/窗体,所有这些都是同一个主应用程序框架的子级。 在这一点上,WTL 或 MFC MDI 向导提供的样板代码不会有太大帮助。 VB 程序员几乎可以免费获得此功能,尽管我不会深入探讨细节。 对于 C++,特别是使用 WTL,有许多解决方法。 在本文中,我将提供一个对我来说相当直接的解决方案。

背景

这是第一个版本,使用 VS 2003 和 WTL 7.5 开发(可从 sourceforge.net 获取)。

使用代码

基本思想依赖于继承,但在我们深入研究之前,让我们检查一下 WTL MDI 向导用来构建应用程序的三个主要类

  • CMainFrame 提供应用程序的主窗口。
  • CChildFrame 正如其名称所暗示的那样:为子窗口提供框架。
  • CXXXView - 在随附的示例中,CMDI2View 是应用程序的“精髓”。 用 MFC 术语来说,它将构成文档视图。 这是您程序操作发生的地方。

关键是:要在您的应用程序中拥有多种类型的文档/窗体/其他任何内容,您只需从视图类继承,如下所示

class CMDI2View1 : public CMDI2View

class CMDI2View2 : public CMDI2View

就是这样!... 并非真的;您需要提供一些东西才能使其正常工作

  1. 将代码添加到继承的视图中以提供不同的功能。

    在演示应用程序中,我通过重写 OnPaint 方法来做到这一点,如下所示,在 CMDI2View1

    LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, 
               LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        CPaintDC dc(m_hWnd);
        RECT r;
        GetClientRect(&r);
        dc.SetBkColor(RGB(0,0,192));
        dc.SetBkMode(TRANSPARENT);
        dc.SetTextColor(RGB(255,255,255));
        CBrush brush;
        brush.CreateSolidBrush(RGB(0,0,192));
        dc.SelectBrush(brush);
        dc.Rectangle(0,0,r.right, r.bottom);
        dc.DrawText(_T("Documento tipo Uno"),-1, 
              &r,DT_VCENTER | DT_CENTER | DT_SINGLELINE);
        return 0;
    }

    ...在 CMDI2View2: 中。

    LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, 
            LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        CPaintDC dc(m_hWnd);
        RECT r;
        GetClientRect(&r);
        dc.SetBkColor(RGB(192,0,0));
        dc.SetBkMode(TRANSPARENT);
        dc.SetTextColor(RGB(192,192,192));
        CBrush brush;
        brush.CreateSolidBrush(RGB(192,0,0));
        dc.SelectBrush(brush);
        dc.Rectangle(0,0,r.right, r.bottom);
        dc.DrawText(_T("Documento tipo Dos"),-1, 
           &r,DT_VCENTER | DT_CENTER | DT_SINGLELINE);
        return 0;
    }
  2. 我们需要将 CChilFramem_view 成员重新定义为指向其基类 (CMDIView) 的指针。 因此,向导生成的行
    CMDI2View m_view

    变成

    CMDI2View* m_view

    这对于允许视图类中的多态行为是必要的。

  3. 我们需要在 CChildFrame 类中定义一个变量,该变量允许选择要构造的视图类型,因此
    int iType; //View selector
    
    ...
    
    //Define a constructor that allows iType member initialization, 
    //and a destructor that deletes the view.
    
    CChildFrame(int iiType = 1) : m_view(0), iType(iiType)
    {
    }
    
    ~CChildFrame()
    {
        if (m_view)
            delete m_view;
    }
  4. 我们需要修改 CChildFrame 类的 OnCreate 方法,我这样做了
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
            LPARAM /*lParam*/, BOOL& bHandled)
    {
        if (iType == 1)
            m_view = new CMDI2View1;
        else
            m_view = new CMDI2View2;
    
        m_hWndClient = m_view->Create(m_hWnd, rcDefault, NULL, 
                       WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | 
                       WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
    
        char buf[256] = {0};
        if (iType == 1)
            strcpy(buf, _T("Documento tipo I"));
        else
            strcpy(buf, _T("Documento tipo II"));
        SetWindowText(buf);
    
        if (iType == 1)
        {
            SetIcon(LoadIcon(_Module.GetResourceInstance(), 
                             MAKEINTRESOURCE(IDR_MDICHILD)));
        }
        else
        {
            SetIcon(LoadIcon(_Module.GetResourceInstance(), 
                             MAKEINTRESOURCE(IDR_MDICHILD2)));
        }
    
        bHandled = FALSE;
        return 1;
    }
    
    //The icon part and the Settitle part are not mandatory. 
    //They just contribute to highlight differences between views.
  5. 最后,我们需要一种方法来选择将在 CMainFrame 类中启动哪个视图。 为了实现这一点,我在 ID ID_FILE_NEW2 下添加了一个菜单项(以及工具栏图标)。

    随后,我像这样处理它

    //Message map
    COMMAND_ID_HANDLER(ID_FILE_NEW2, OnFileNew2)
    ...
    
    //Original OnFileNew, remains unmodified, just takes 
    //advantage of the default CChildFrame constructor
    LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, 
            HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
        CChildFrame* pChild = new CChildFrame;
        pChild->CreateEx(m_hWndClient);
    
        // TODO: add code to initialize document
    
        return 0;
    }
    
    //Added to provide for the construction of CMDI2View2, 
    //the key is CChildFrame construction parameter (2)
    LRESULT OnFileNew2(WORD /*wNotifyCode*/, WORD /*wID*/, 
            HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
        CChildFrame* pChild = new CChildFrame(2);
        pChild->CreateEx(m_hWndClient);
    
        return 0;
    }

现在... 真的就是这样了。

附注

有很多方法可以实现相同的功能,我只是发现这个例子快速而简单。 当然,您可以根据应用程序的需要添加任意数量的视图类型,但就本文而言,两个就足够了。 而 CFormView 将是视图的一个更好的基类,使用它不会增加太多难度,也不会增加显着的开销。 同样,我选择了一个原始视图以使其保持简单。

希望这有所帮助。

© . All rights reserved.