Custom SDI —— 向视图添加控件


转自:http://www.cnitblog.com/buf/archive/2011/09/23/42270.html


介绍

在使用MFC Application Wizard时,如果选择Dialog Based App,添加控件是很容易的事情——只需要移动几下鼠标就可以轻松完成。但是,如果选择SDI或者MDI,添加控件就不是那么直接了。在此总结一下向视图添加控件的方法。

子窗体的创建

首先,使用App Wizard生成一个标准的SDI程序,我将它命名为AddCtrl。因为文档与这里要讨论的重点无关,所以去掉了文档/视图结构的支持。IDE为我们生成CAddCtrlApp、CAboutDlg、CMainFrame、CChildView等几个类。注意到CMainFrame有以下几个成员变量:

protected:  // control bar embedded members

    CStatusBar  m_wndStatusBar;//状态栏

    CToolBar    m_wndToolBar;//工具栏

    CChildView    m_wndView;//视图

我想,既然CMainFrame可以像一个容器那样容纳状态栏、工具栏等子窗体,那可不可以按照同样的方法向CChildView添加控件呢?

循此思路,先来看看状态栏和工具栏的创建过程。检查CMainFrame的几个成员函数,很明显OnCreate与控件的创建关系密切:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

       return -1;

    // create a view to occupy the client area of the frame

    if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,

       CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))

       return -1;

    // create toolbar

    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

       return -1;

    // create status bar

    if (!m_wndStatusBar.Create(this) ||

       !m_wndStatusBar.SetIndicators(indicators,

         sizeof(indicators)/sizeof(UINT)))

       return -1;      // fail to create

    ...

    return 0;

}

我们看到了一连串的Create(Ex)调用,MSDN上对CWnd::Create有这样的说明:
You construct a child window in two steps. First, call the constructor, which constructs theCWnd object. Then callCreate, which creates the Windows child window and attaches it toCWnd.Create initializes the window's class name and window name and registers values for its style, parent, and ID.

哈!这下很清楚了,状态栏作为CMainFrame的子窗体,其创建也同样经历了两个步骤:首先,状态栏的构造函数在构造CMainFrame时被调用;然后,状态栏的Create函数在CMainFrame::OnCreate中被调用。

这下简单了,我们依葫芦画瓢向视图添加一个树控件。首先给CChildView添加两个成员变量:

class CChildView : public CWnd

{

...

// Attributes

protected:

    CTreeCtrl m_tree;// add a tree control to view

    CImageList m_imgList;// image list for the tree control

...

};

然后,重载CChildView::OnCreate,在其中创建树控件和初始化image list:

int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CWnd::OnCreate(lpCreateStruct) == -1)

       return -1;

    // create tree control

    DWORD dwStyle = WS_CHILD|WS_VISIBLE|TVS_HASLINES|TVS_HASBUTTONS;

    if (!m_tree.Create(dwStyle, CRect(0, 0, 0, 0), this, 0x1001))

    {

       TRACE0("Failed to create tree control\n");

       return -1;      // fail to create

    }  

    // prepare image list

    m_imgList.Create(16, 16, ILC_COLOR32, 1, 1);

    m_imgList.SetBkColor(RGB(255, 255, 255));

    m_imgList.Add(AfxGetApp()->LoadIcon(IDI_ICON_ITEM));

    // setup image list for the tree control

    m_tree.SetImageList(&m_imgList, TVSIL_NORMAL);

   

    return 0;

}

为了测试新添加的树控件能否正常工作,我给主菜单添加了一个Test命令,点击时会向树控件添加一些项目:

void CChildView::Test()

{

    CString item;

   

    for(int i = 0; i < 26; i++)

    {

       item = _T('A') + i;

       m_tree.InsertItem(item.GetBuffer(0), 0, 0);

    }

}

事情到这还不算完,编译运行点击Test视图里空白如故,就像啥也没发生过一样。哪里出了问题呢?回头再看看树控件Create函数:

virtual BOOL Create(

   DWORD dwStyle,

   const RECT& rect,

   CWnd* pParentWnd,

   UINT nID

);

dwStylepParentWnd不用再解释了,第二个参数rect应该怎样获得呢?我希望树控件总是充满视图,于是修改CChidView::OnCreate函数中创建树控件的部分:

// create tree control

DWORD dwStyle = WS_CHILD |WS_VISIBLE |WS_BORDER |TVS_HASLINES

        | TVS_HASBUTTONS;

CRect rect;

GetClientRect(&rect);

if (!m_tree.Create(dwStyle, rect, this, 0x1001))

{

    TRACE0("Failed to create tree control\n");

    return -1;      // fail to create

}

结果还是一样,推测是不是因为调用GetClientRectCChildView还没有创建完成,此时视图的客户区域大小依然为0,调试发现果然如此。于是向CChildView添加一个函数用来做一些初始化工作:

void CChildView::InitialUpdate()

{

    CRect rect;

    GetClientRect(&rect);   

    m_tree.MoveWindow(&rect, TRUE);

}

然后在CMainFrame::OnCreate返回前添加对这个InitialUpdate的调用:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    ...

    RecalcLayout();

    m_wndView.InitialUpdate();

    return 0;

}

再次编译运行,终于看到视图里有东西了 ...


重载WM_SIZE消息处理函数调整子窗体大小

最后还有一个小问题,当改变框架大小时,树控件的大小并没有跟着改变。这个也很好解决,重载CChildView::OnSize,让树控件的大小随着视图大小调整:

void CChildView::OnSize(UINT nType, int cx, int cy)

{

    CWnd::OnSize(nType, cx, cy);

    // Resize tree control to fill the whole view.

    m_tree.MoveWindow(0, 0, cx, cy, TRUE);

}


视图子窗体的消息处理

为了演示如何对视图子窗体的消息进行处理,我向CChildView再次添加了一个按钮,添加过程与树控件的情况相同。那么,如何为按钮的点击事件编写处理函数呢?Framework提供的默认处理函数为OnCmdMsg,此函数的参数包含了发生事件的控件ID(也就是在调用控件Create函数时指定的那个值)、事件的类型等信息。因此可以重载视图的OnCmdMsg函数,在默认处理前检测是否按钮的点击事件并作相应处理:

BOOL CChildView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

{

    if (IDC_BTN_REFRESH == nID && CN_COMMAND == nCode)

    {

        AfxMessageBox(_T("Refresh Button Clicked!"));

        return TRUE;

    }

    return CWnd ::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

总结

向视图添加控件的基本过程如下:

      1、向视图类添加控件成员变量

      2、重载视图的OnCreate函数,添加对控件Create函数的调用

      3、视图创建完成后,初始化控件的大小

      4、重载视图的OnSize函数,添加对控件大小的设置

      5、如果要对控件消息进行处理,重载视图的OnCmdMsg函数

 

p.s.本文是从自己的学习笔记整理来的,本来有几张图片,这里贴起来还挺麻烦,罢了。
p.s.其实都挺简单,不过我个人的体会,从Dialog Based App到Doc/View结构还真是挺不适应的,由于对整个框架的不了解,想加点自己的代码也不知道安插在哪里 ... 写给向我一样的新手
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值