您的位置:首页 > 其它

MFC无文档多视应用程序框架

2009-02-23 20:53 190 查看
///////////////////////////////////////////////////////////////////////////////////
//Author:  aaashun
//Date:    23/2/2009
//Email:   aaashun@gmail.com
///////////////////////////////////////////////////////////////////////////////////
大多数情况下MFC的MDI模板为开发多窗口或多文档应用程序提供了很大的方便,但有时MFC向导生成的框架并非是我们需要的,怎么办?对MFC生成的框架动手术吧。本文以多视(无文档)为例说明手术的关键部分,讲的有点乱,如果需要源码请来邮件索要。
 
最终效果图:



 
1.选择MDI类型,去掉文档视图关联支持



2.一路next,子窗口类型样式选择最大化



 
3.去掉CChildView类
删除ChildView.h和ChildView.cpp两个文件,去掉CChildFrame里的几个重写(Dump等)、几个消息响应(WM_CREATE等)和一个事件响应(IDC_FILE_CLOSE等)。
4.去掉IDC_FILE_NEW在App类中响应
5.增加ShunStdAfx.h头文件并在stdafx.h里 include一下,我会把自定义的一些东西放在这个文件里。
 
至此已经把VC生成的框架程序删减的差不多了,保证编译通过再进行下边的工作--开始添加。
二、接下来开始做左边的管理工具栏窗口。
1.增加DialogBar类型对话框,并为之增加类CManageBar,从CDialog派生(因为找不到CDialogBar)
2.替换ManageBar.h和ManageBar.cpp文件里的CDialog为CDialogBar
修改构造函数如下:
      CManageBar::CManageBar(CWnd* pParent /*=NULL*/)
                  : CDialogBar()
3.为CMainFrame添加成员CManageBar m_wndManageBar,并在OnCreate中添加相应代码创建并把它show出来:
    if (!m_wndManageBar.Create(this, IDD_DIALOGBAR,
        CBRS_LEFT | CBRS_SIZE_DYNAMIC, AFX_IDW_DIALOGBAR))
    {
        TRACE0("Failed to create dialogbar/n");
        return -1;
    }
    m_wndManageBar.EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndManageBar);
编译运行 报错 winfrm2.cpp 92 行,看提示
// assert fails when initial CBRS_ of bar does not
// match available docking sites, as set by EnableDocking()
原来是窗口不允许停靠,这样在停靠ManageBar的代码前面得加一句让窗口允许停靠。
    EnableDocking(CBRS_ALIGN_LEFT);
三、添加三个FormView资源
1.添加三个FormView资源并添加相应类分别为:
IDD_FORMVIEW_ADMIN,     CAdminView
IDD_FORMVIEW_CONSUMER, CConsumerView
IDD_FORMVIEW_GROUP,     CGroupView
注意这三个类都是从CFormView类里派生出来
四、在ManageBar上添加树形控件
1.添加树形控件ID为IDC_TREE_MANAGEBAR,关联一个成员变量m_tree
2.为CManageBar添加成员函数InitTreeCtrl,完成控件的初始化工作
HRESULT CManageBar::InitTreeCtrl(void)
{
    //
    m_imageList.Create ( IDB_BITMAP_TREE,16,1,RGB(255,255,255) );
    m_tree.SetImageList ( &m_imageList,TVSIL_NORMAL );
 
    UINT nMask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
    
    HTREEITEM hItem1 =/
        m_tree.InsertItem(nMask,L"管理员管理",0,1,NULL,NULL,NULL,TVI_ROOT,TVI_LAST);
            m_tree.InsertItem(nMask,L"管理员管理",/
                           0,1,NULL,NULL,NULL,hItem1,TVI_LAST);
            m_tree.InsertItem(nMask,L"分组管理",/
                           0,1,NULL,NULL,NULL,hItem1,TVI_LAST);
    HTREEITEM hItem2 =/
        m_tree.InsertItem(nMask,L"用户管理",/
                       0,1,NULL,NULL,NULL,TVI_ROOT,TVI_LAST);
    return S_OK;
}
3.重写CManageBar的Create函数在里面调用InitTreeCtrl如下:
BOOL CManageBar::Create(CWnd* pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID)
{
    // TODO: Add your specialized code here and/or call the base class
    BOOL bRes=CDialogBar::Create(pParentWnd, nIDTemplate, nStyle, nID);
    UpdateData(FALSE);//这行必须带上
    InitTreeCtrl();
    return bRes;
}
编译运行一下,界面完成了吧。
五、接下来进行我们要干的是让鼠标点击树控件的结点时找开相应的窗口
1.研究一下怎么让视图和框架关联起来
新建一个多文档工程Temp-->保持默认配置-->finish,看看CTempApp::InitInstance()里是怎么写的
    CMultiDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(IDR_TempTYPE,
        RUNTIME_CLASS(CTempDoc),
        RUNTIME_CLASS(CChildFrame), // custom MDI child frame
        RUNTIME_CLASS(CTempView));
    if (!pDocTemplate)
        return FALSE;
    AddDocTemplate(pDocTemplate);
这里还需要一个很讨厌的Doc类,我的目标是定制一个无文档的(如果你想拖个吃闲饭的doc类的话可以考虑这个方法),我们最好是自己构造关联.看一下MSDN里关于CCreateContext的介绍:The framework uses the CCreateContext structure when it creates the frame windows and views associated with a document,看看怎么用它:
    CCreateContext context;
    CRuntimeClass *pViewRuntimeClass;
    CChildFrame *pChildFrame;
    
    pViewRuntimeClass=RUNTIME_CLASS(CDemoView);
    pChildFrame=new CChildFrame();
    
    context.m_pNewViewClass=pViewRuntimeClass;
    pChildFrame->LoadFrame(IDR_MdiNoDocDemoTYPE,WS_MAXIMIZE|WS_OVERLAPPEDWINDOW,this,&context);
怎么样,自己构造视图子框架的关联就这么简单。
 
2.下边开始继续我们的代码,定义一个结构体用于保存CFormView, CChildFrame, CRuntimeClass三者实例的对应关联
class CChildFrame; //因为是放在ShunStdAfx.h里的所以先申明一下,不然会报错的.
 
struct SShunMapNode
{
    int m_nFormID;
    CChildFrame *m_pFrame;
    CRuntimeClass *m_pViewRuntime;
};
 
typedef CList<SShunMapNode,SShunMapNode&> CShunMapList; //用模板定义一个链表方便操作
为CMainFrame类增加一个成员变量CShunMapList m_mapList
三个成员函数
InitializeShunMapList//初始多窗口资源
UnInitializeShunMapList//卸载为子窗口分配的资源
SwitchFormView//用于完成子窗口的切换
 
具体代码如下:
BOOL CMainFrame::SwitchFormView(int nFormViewID,CString strTitle)
{
    // TODO: Add your command handler code here
    
//    CRuntimeClass* pMapNode=NULL;
//    CChildFrame *pFrame;
    //如果ID为空就退出
    if(nFormViewID==0)
        return FALSE;
    //查找nFormViewID对应的结点
    POSITION pos=m_mapList.GetHeadPosition();
    while(pos)
    {
        if(m_mapList.GetAt(pos).m_nFormViewID==nFormViewID)
            break;
        else
            m_mapList.GetNext(pos);
//        if(m_mapList.GetNext(pos).m_nFormViewID==nFormViewID)
//            break;
    }
    //如果没找到结点就退出
    if(pos==NULL)
        return FALSE;
//    m_mapList.GetPrev(pos);
    
    SShunMapNode mapNode;
    mapNode=m_mapList.GetAt(pos);
 
    if(mapNode.m_pFrame)
    {
        mapNode.m_pFrame->ShowWindow(SW_SHOW);
        mapNode.m_pFrame->MDIActivate();
        return TRUE;
    }
    else
    {
        CCreateContext context;
        context.m_pNewViewClass=mapNode.m_pViewRuntime;
 
        mapNode.m_pFrame=new CChildFrame();
        mapNode.m_pFrame->LoadFrame(IDR_MdiNoDocDemoTYPE,WS_MAXIMIZE|WS_OVERLAPPEDWINDOW,this,&context);
        mapNode.m_pFrame->SetWindowText(strTitle);
        mapNode.m_pFrame->ShowWindow(SW_SHOWMAXIMIZED);
        mapNode.m_pFrame->InitialUpdateFrame(NULL,TRUE);
        m_mapList.SetAt(pos,mapNode);    //必须用SetAt更新CList结点
        //return TRUE;
    }
    return TRUE;
}
 
BOOL CMainFrame::InitializeShunMapList(void)
{
    SShunMapNode shunMapNode;
    shunMapNode.m_pFrame=NULL;
 
    shunMapNode.m_nFormViewID=IDD_FORMVIEW_ADMIN;
    shunMapNode.m_pViewRuntime=RUNTIME_CLASS(CAdminView);
    m_mapList.AddTail(shunMapNode);
 
    shunMapNode.m_nFormViewID=IDD_FORMVIEW_GROUP;
    shunMapNode.m_pViewRuntime=RUNTIME_CLASS(CGroupView);
    m_mapList.AddTail(shunMapNode);
 
    shunMapNode.m_nFormViewID=IDD_FORMVIEW_CONSUMER;
    shunMapNode.m_pViewRuntime=RUNTIME_CLASS(CConsumerView);
    m_mapList.AddTail(shunMapNode);
    return TRUE;
}
 
BOOL CMainFrame::UnInitializeShunMapList(void)
{
    SShunMapNode mapNode;
    POSITION pos=m_mapList.GetHeadPosition();
    while(pos)
    {
        if(mapNode.m_pFrame)
        {
            delete mapNode.m_pFrame;
            mapNode.m_pFrame=NULL;
        }
        m_mapList.GetNext(pos);
    }
    return TRUE;
}
3.在树控件的TVN_SELCHANGED消息里切换到相应子窗体
void CManageBar::OnTvnSelchangedTreeManagebar(NMHDR *pNMHDR, LRESULT *pResult)
{
    LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: Add your control notification handler code here
    HTREEITEM hTreeItem = m_tree.GetSelectedItem();
 
    int nFrameID= (int)m_tree.GetItemData(hTreeItem);    //获取节点参数
    CString strTitle;                                    //获取节点字符串
    if(nFrameID)
    {
        while(1)
        {
            strTitle=m_tree.GetItemText(hTreeItem)+strTitle;
            hTreeItem=m_tree.GetParentItem(hTreeItem);
            if(hTreeItem)
                strTitle=CString(L"--")+strTitle;
            else
                break;
        }
        ((CMainFrame*)(::AfxGetApp()->m_pMainWnd))->SwitchFormView(nFrameID,strTitle);
    }
    *pResult = 0;
}
注意这一行代码:
int nFrameID= (int)m_tree.GetItemData(hTreeItem);    //获取节点参数
节点的参数就是FormView资源的ID,因此我们需要将这个参数在CManageBar::InitTreeCtrl(void)函数里传递给节点
因此修改代码如下:
HRESULT CManageBar::InitTreeCtrl(void)
{
    //
    m_imageList.Create ( IDB_BITMAP_TREE,16,1,RGB(255,255,255) );
    m_tree.SetImageList ( &m_imageList,TVSIL_NORMAL );
 
    UINT nMask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM;
    
    HTREEITEM hItem1 =/
        m_tree.InsertItem(nMask,L"管理员管理",0,1,NULL,NULL,NULL,TVI_ROOT,TVI_LAST);
            m_tree.InsertItem(nMask,L"管理员管理",0,1,NULL,NULL,IDD_FORMVIEW_ADMIN,hItem1,TVI_LAST);
            m_tree.InsertItem(nMask,L"分组管理",0,1,NULL,NULL,IDD_FORMVIEW_GROUP,hItem1,TVI_LAST);
    HTREEITEM hItem2 =/
        m_tree.InsertItem(nMask,L"用户管理",0,1,NULL,NULL,IDD_FORMVIEW_CONSUMER,TVI_ROOT,TVI_LAST);
    return S_OK;
}
 
4.如果一个子窗体里需要初始化很多数据,每次打开关闭都将占用相当系统资源。所以最好是第一次打开子窗体时分配内存并初始所有数据,收到关闭消息时就隐藏起来,第二次就不分配内存了直接把原来的窗体显示出来就行了。

 
可以编译一下,看看效果了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐