ID为IDD_TREEVIEW,Style=CHILD,BORDER=NONE,
删掉按钮OK和CANCEL,添加Tree控件IDC_TREE,占满整个对话框
导入位图资源,ID为IDB_BITMAP
新建列表对话框IDD_LISTCTRLVIEW,Style=CHILD,BORDER=NONE,
添加LISTCONTROL控件IDC_LIST,占满蓝色边框,
和编辑视图的对话框资源IDD_EDITVIEW,Style=CHILD,BORDER=NONE,删掉上面的按钮。
添加EditBox控件IDC_EDIT,占满蓝色边界线
建立2 个View 的类,这里我们让这2个View 的类继承于FormView,
CListControlView 继承于FormView 关联对话框 IDD_LISTVIEW,为了后面可以new 将构造函数改为publlic属性(默认为protected)
class CListControlView : public CFormView { public: CListControlView(); // protected constructor used by dynamic creation DECLARE_DYNCREATE(CListControlView)
};为列边框添加控件类型变量m_listCtrl
为对话框添加OnSize消息,使列表框与对话框等大
void CListControlView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy); // TODO: Add your message handler code here CFormView::ShowScrollBar(SB_VERT,FALSE); CFormView::ShowScrollBar(SB_HORZ,FALSE); if (GetSafeHwnd()) { if (m_listCtrl.GetSafeHwnd()) { CRect rect(0,0,cx,cy); m_listCtrl.MoveWindow(&rect); } } }
void CListControlView::OnInitialUpdate() { CFormView::OnInitialUpdate(); // TODO: Add your specialized code here and/or call the base class CRect rect; m_listCtrl.GetClientRect(&rect); m_listCtrl.InsertColumn(0, "From", LVCFMT_LEFT, rect.Width()/4); m_listCtrl.InsertColumn(1, "Subject", LVCFMT_LEFT, rect.Width()/4); m_listCtrl.InsertColumn(2, "Date", LVCFMT_LEFT, rect.Width()/4); m_listCtrl.InsertColumn(3, "Size", LVCFMT_LEFT, rect.Width()/4); }
CEditControlView 继承于FormView 关联对话框IDD_EDITVIEW,为了后面可以new 将构造函数改为publlic属性(默认为protected)
class CEditControlView : public CFormView { public: CEditControlView(); // protected constructor used by dynamic creation
为对话框添加OnSize消息,使编辑框与对话框等大
void CEditControlView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy); // TODO: Add your message handler code here CFormView::ShowScrollBar(SB_VERT,FALSE); CFormView::ShowScrollBar(SB_HORZ,FALSE); //编辑框与窗口大小一样 if (GetSafeHwnd()) { if (m_editCtrl.GetSafeHwnd()) { CRect rect(0,0,cx,cy); m_editCtrl.MoveWindow(&rect); } } }
现在创建关联树控件的视图类
CLeftPaneView : public CFormView关联对话框 IDD_TREEVIEW。
为CLeftPaneView树控件关联一个控件类型的变量m_treeCtrl
为对话框添加OnSize消息,使树控件与对话框等大
void CLeftPaneView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy); // TODO: Add your message handler code here if (GetSafeHwnd()) { CRect rect; GetClientRect(&rect); if (m_treeCtrl.GetSafeHwnd()) m_treeCtrl.MoveWindow(&rect); } }
void CLeftPaneView::OnInitialUpdate() { CFormView::OnInitialUpdate(); // TODO: Add your specialized code here and/or call the base class m_ImageList.Create(IDB_BITMAP, 16, 1, RGB(255, 0, 255)); m_treeCtrl.SetImageList(&m_ImageList, LVSIL_NORMAL); m_hSplitterView = m_treeCtrl.InsertItem("Splitter View", 0, 0); m_hListCtrlView = m_treeCtrl.InsertItem("ListCtrl View", 1, 1); m_hEditView = m_treeCtrl.InsertItem("EditCtrl View", 2, 2); }
源文件中添加
#include "ListControlView.h" #include "EditControlView.h"为了后面可以new CSplitterView将构造函数改为publlic属性(默认为protected)
class CSplitterView : public CView { public: // changed from protected CSplitterView(); // protected constructor used by dynamic creation // // };
int CSplitterView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here m_wndSplitter.CreateStatic(this, 2, 1); CCreateContext *pContext = (CCreateContext*) lpCreateStruct->lpCreateParams; m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CListControlView), CSize(150,0), pContext); m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CEditControlView), CSize(0,0), pContext); return 0; }
添加成员变量
// Attributes public: CSplitterWnd m_wndSplitter;
int CSplitterView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here m_wndSplitter.CreateStatic(this, 2, 1); CCreateContext *pContext = (CCreateContext*) lpCreateStruct->lpCreateParams; m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CListCtrlView), CSize(150,0), pContext); m_wndSplitter.CreateView(1,0,RUNTIME_CLASS(CEditCtrlView), CSize(0,0), pContext); return 0; } void CSplitterView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); // TODO: Add your message handler code here m_wndSplitter.MoveWindow(-2,-2,cx+4,cy+4); m_wndSplitter.SetRowInfo(0, cy-(cy/4), 0); m_wndSplitter.SetRowInfo(1, cy/4, 20); m_wndSplitter.RecalcLayout(); }
因为所有视图都要放在框架窗口中,所以,右面板需要一个框架窗口。
新建框架窗口类CRightPaneFrame,继承自CFrameWnd。
为了在框架中调用不同视图类,添加成员变量,并定义视图ID
#include "SplitterView.h" #include "ListControlView.h" #include "EditControlView.h" #define VIEW_SPLITTER 1 #define VIEW_LISTCTRL 2 #define VIEW_EDIT 3
public: CSplitterView* m_pSplitterView; CListControlView* m_pListCtrlView; CEditControlView* m_pEditCtrlView; UINT m_nCurrentViewID;
添加虚函数
BOOL CRightPaneFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // TODO: Add your specialized code here and/or call the base class // TODO: Add your specialized code here and/or call the base class m_pSplitterView = new CSplitterView; m_pSplitterView->Create(NULL, NULL, 0L, CFrameWnd::rectDefault, this, VIEW_SPLITTER, pContext); SetActiveView(m_pSplitterView); m_pSplitterView->ShowWindow(SW_SHOW); m_pSplitterView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); m_nCurrentViewID = VIEW_SPLITTER; m_pListCtrlView = new CListControlView; ((CView*) m_pListCtrlView)->Create(NULL, NULL, 0L, CFrameWnd::rectDefault, this, VIEW_LISTCTRL, pContext); m_pListCtrlView->ShowWindow(SW_HIDE); m_pListCtrlView->SetDlgCtrlID(VIEW_LISTCTRL); m_pEditCtrlView = new CEditControlView; ((CView*) m_pEditCtrlView)->Create(NULL, NULL, 0L, CFrameWnd::rectDefault, this, VIEW_EDIT, pContext); m_pEditCtrlView->ShowWindow(SW_HIDE); m_pEditCtrlView->SetDlgCtrlID(VIEW_EDIT); RecalcLayout(); return TRUE; // return CFrameWnd::OnCreateClient(lpcs, pContext); }
添加切换视图的函数
public:
void SwitchToView(UINT nView);
void CRightPaneFrame::SwitchToView(UINT nView) { CView* pOldActiveView = GetActiveView(); CView* pNewActiveView = NULL; switch (nView) { case VIEW_SPLITTER: pNewActiveView = (CView*) m_pSplitterView; break; case VIEW_LISTCTRL: pNewActiveView = (CView*) m_pListCtrlView; break; case VIEW_EDIT: pNewActiveView = (CView*) m_pEditCtrlView; break; } if (pNewActiveView) { // don't switch when views are the same if (pOldActiveView == pNewActiveView) return;//不变 SetActiveView(pNewActiveView);//改变活动的视图 pNewActiveView->ShowWindow(SW_SHOW);//显示新的视图 pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); pOldActiveView->ShowWindow(SW_HIDE);//隐藏旧的视图 pOldActiveView->SetDlgCtrlID(m_nCurrentViewID); m_nCurrentViewID = nView; RecalcLayout();//调整框架窗口 } }
注意:
pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
实际上AFX_IDW_PANE_FIRST是为了解决多个VIEW的情况下消息转发的问题,这是MFC内部使用的一个固定的值,所有当前的活动视图都会切换到AFX_IDW_PANE_FIRST这个ID,主窗口收到的一些消息(比如命令、通知等等消息)会转发给活动视图来处理,框架通过这个ID来定位活动视图。
当在某个SDI应用程序中使用多个视图并支持视图切换时,很容易忽略这点,造成某些消息得不到正确的响应,
因此当激活某个视图时也要把这个视图的ID改成AFX_IDW_PANE_FIRST。
当主框架调整窗口布局时,即调用RecalcLayout()这个函数时,会将ID为AFX_IDW_PANE_FIRST的窗口/视(必须可见)作为最后一个视图进行拉伸以填充剩余的区域。
在左面版也即树视中添加成员变量,包含右面板和树项
// Attributes public: CImageList m_ImageList; HTREEITEM m_hSplitterView; HTREEITEM m_hListCtrlView; HTREEITEM m_hEditView; CRightPaneFrame* m_pRightPaneFrame;头文件class CLeftPaneView : public CFormView前加前置声明,以便使用VIEW_SPLITTER等宏定义
class CRightPaneFrame;
在源文件加
#include "RightPaneFrame.h"
void CLeftPaneView::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // TODO: Add your control notification handler code here UINT nView = 0; HTREEITEM hSelectedItem = m_treeCtrl.GetSelectedItem(); if (hSelectedItem == m_hSplitterView) nView = VIEW_SPLITTER; else if (hSelectedItem == m_hListCtrlView) nView = VIEW_LISTCTRL; else if (hSelectedItem == m_hEditView) nView = VIEW_EDIT; if (nView) m_pRightPaneFrame->SwitchToView(nView); *pResult = 0; }
在源文件中添加
#include "LeftPaneView.h" #include "RightPaneFrame.h"
修改OnCreateClient函数
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // TODO: Add your specialized code here and/or call the base class if (!m_wndSplitter.CreateStatic(this, 1, 2)) { TRACE0("Failed to create splitter window\n"); return FALSE; } // Get the client rect first for calc left pane size CRect rect; GetClientRect(&rect); // create the left tree view first. if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CLeftPaneView), CSize(rect.Width()/3, 0), pContext)) { TRACE0("Failed to create left pane view\n"); return FALSE; } // The right pane is a frame which and contain several different views. // The is can be set to active or non-active if (!m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CRightPaneFrame), CSize(0, 0), pContext)) { TRACE0("Failed to create right pane frame\n"); return FALSE; } CLeftPaneView* pLeftPaneView = (CLeftPaneView*) m_wndSplitter.GetPane(0,0); pLeftPaneView->m_pRightPaneFrame = (CRightPaneFrame*) m_wndSplitter.GetPane(0,1); // Set the left pane as the active view SetActiveView((CView*) m_wndSplitter.GetPane(0, 0)); return TRUE; }