一般的多文档应用都会有居于左侧或右侧的导航窗口,在VC中是如何实现的呢?
试建了一个MDI项目,发现MFC的MDI功能在2008上有很大的增强,支持很多很炫的布局效果(见截图)。
这是典型的Office 2007风格,特别是Outlook 2007及VS 2008的风格,看起来相当的漂亮。
在进一步了解这个示例的代码前,我想自己尝试一下,如何在经典的MDI主窗口添加公用面板。达到如下效果:
经过一番努力,实现了这个功能,步骤如下:
第一步:从CDockablePane继承,创建一个自定义类CNavView
CNavView.h的代码:
#pragma once
// CNavView
class CNavView : public CDockablePane
{
DECLARE_DYNAMIC(CNavView)
public:
CNavView();
virtual ~CNavView();
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
public:
CTreeCtrl m_wndClassView;
void AdjustLayout();
protected:
DECLARE_MESSAGE_MAP()
};
CNavView.cpp的代码:
// NavView.cpp : implementation file
//
#include "stdafx.h"
#include "MDISample.h"
#include "NavView.h"
// CNavView
IMPLEMENT_DYNAMIC(CNavView, CDockablePane)
CNavView::CNavView()
{
}
CNavView::~CNavView()
{
}
BEGIN_MESSAGE_MAP(CNavView, CDockablePane)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
void CNavView::OnSize(UINT nType, int cx, int cy)
{
CDockablePane::OnSize(nType, cx, cy);
AdjustLayout();
}
void CNavView::AdjustLayout()
{
if (GetSafeHwnd() == NULL)
{
return;
}
CRect rectClient;
GetClientRect(rectClient);
int cyTlb = 3;
m_wndClassView.SetWindowPos(NULL, rectClient.left + 1, rectClient.top + cyTlb + 1, rectClient.Width() - 2, rectClient.Height() - cyTlb - 2, SWP_NOACTIVATE | SWP_NOZORDER);
}
// CNavView message handlers
int CNavView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CRect rectDummy;
rectDummy.SetRectEmpty();
// Create views:
const DWORD dwViewStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
if (!m_wndClassView.Create(dwViewStyle, rectDummy, this, 2))
{
TRACE0("Failed to create Class View/n");
return -1; // fail to create
}
HTREEITEM hRoot = m_wndClassView.InsertItem(_T("FakeApp classes"), 0, 0);
m_wndClassView.SetItemState(hRoot, TVIS_BOLD, TVIS_BOLD);
HTREEITEM hClass = m_wndClassView.InsertItem(_T("CFakeAboutDlg"), 1, 1, hRoot);
m_wndClassView.InsertItem(_T("CFakeAboutDlg()"), 3, 3, hClass);
m_wndClassView.Expand(hRoot, TVE_EXPAND);
hClass = m_wndClassView.InsertItem(_T("CFakeApp"), 1, 1, hRoot);
m_wndClassView.InsertItem(_T("CFakeApp()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("InitInstance()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("OnAppAbout()"), 3, 3, hClass);
hClass = m_wndClassView.InsertItem(_T("CFakeAppDoc"), 1, 1, hRoot);
m_wndClassView.InsertItem(_T("CFakeAppDoc()"), 4, 4, hClass);
m_wndClassView.InsertItem(_T("~CFakeAppDoc()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("OnNewDocument()"), 3, 3, hClass);
hClass = m_wndClassView.InsertItem(_T("CFakeAppView"), 1, 1, hRoot);
m_wndClassView.InsertItem(_T("CFakeAppView()"), 4, 4, hClass);
m_wndClassView.InsertItem(_T("~CFakeAppView()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("GetDocument()"), 3, 3, hClass);
m_wndClassView.Expand(hClass, TVE_EXPAND);
hClass = m_wndClassView.InsertItem(_T("CFakeAppFrame"), 1, 1, hRoot);
m_wndClassView.InsertItem(_T("CFakeAppFrame()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("~CFakeAppFrame()"), 3, 3, hClass);
m_wndClassView.InsertItem(_T("m_wndMenuBar"), 6, 6, hClass);
m_wndClassView.InsertItem(_T("m_wndToolBar"), 6, 6, hClass);
m_wndClassView.InsertItem(_T("m_wndStatusBar"), 6, 6, hClass);
hClass = m_wndClassView.InsertItem(_T("Globals"), 2, 2, hRoot);
m_wndClassView.InsertItem(_T("theFakeApp"), 5, 5, hClass);
m_wndClassView.Expand(hClass, TVE_EXPAND);
return 0;
}
第二步:在MainFrame的头文件中加入一个新成员变量:
#include "NavView.h"
......
public:
CNavView m_Nav;
第三步:在MainFrm.cpp中OnCreate方法最后加入如下代码
CString str;
str.LoadString(ID_VIEW_NAV);
if (!m_Nav.Create(str, this, CRect(0, 0, 200, 200), TRUE, ID_VIEW_NAV, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI))
{
TRACE0("Failed to create navigate window/n");
return FALSE; // failed to create
}
m_Nav.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_Nav);
注意:ID_VIEW_NAV是自定义的字符串资源。
这样,自定义面板就出现在主窗口的最左侧了。