|
 |
Step 1
I created a new CViewBar class that inherits from one of the CSizingControlBar classes. (#define TViewBarBase to the base class you need). This class encapsulates the frame window so you don't have to know it even exists. (A frame window is just needed to support MFC, it doesn't matter what kind.) The creation and
sizing are handled within the class.
Also taken care of, is specification of the view class in the create member function. I just let MFC do all the work by passing the class type through to it. That way you don't have to derive eight billion different classes just to use a docking view. This leads to my next step.
// ViewBar.h: interface for the CViewBar class.
//
//////////////////////////////////////////////////////////////////////
#ifndef VIEWBAR_H
#define VIEWBAR_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "sizecbar.h"
#include "scbarg.h"
#include "scbarcf.h"
// You must #define this for viewbar to compile properly
#define TViewBarBase CSizingControlBarCF
class CViewBar : public TViewBarBase
{
DECLARE_DYNAMIC(CViewBar);
public:
CViewBar();
virtual ~CViewBar();
virtual BOOL Create(
CWnd* pParentWnd, // mandatory
CRuntimeClass *pViewClass, // mandatory
CCreateContext *pContext = NULL,
LPCTSTR lpszWindowName = NULL,
DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,
UINT nID = AFX_IDW_PANE_FIRST);
protected:
CFrameWnd *m_pFrameWnd;
CCreateContext m_Context;
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CViewBar)
public:
//}}AFX_VIRTUAL
//{{AFX_MSG(CViewBar)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#endif
// ViewBar.cpp: implementation of the CViewBar class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ViewBar.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
IMPLEMENT_DYNAMIC(CViewBar, TViewBarBase);
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CViewBar::CViewBar()
{
ZeroMemory(&m_Context, sizeof(m_Context));
// Create a frame object.
CRuntimeClass *pRuntimeClass = RUNTIME_CLASS(CFrameWnd);
CObject* pObject = pRuntimeClass->CreateObject();
ASSERT( pObject->IsKindOf( RUNTIME_CLASS( CFrameWnd ) ) );
m_pFrameWnd = (CFrameWnd *)pObject;
}
CViewBar::~CViewBar()
{
}
BEGIN_MESSAGE_MAP(CViewBar, TViewBarBase)
//{{AFX_MSG_MAP(CRegBar)
ON_WM_CREATE()
ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////////////
// CViewBar message handlers
/* ---------------------------------------------------------------
Overridden to set the view class before calling the
base create function.
For an SDI application the main frame window is created
when the document template is created and a valid
CreateContext is passed through (hurray!). Meaning there is
no problem creating the ViewBar in the frame window create.
However, for an MDI application the main frame window is
constructed outside the document creation, so the
CreateContext isn't present. In this case you can either
create and setup a CreateContext object with the desired
characteristics (doc template, frame window, etc.) and pass
it, or let the CViewBar::Create() create a CreateContext
with only the runtime class of the view, and the main frame
window set.
--------------------------------------------------------------- */
BOOL CViewBar::Create(
CWnd* pParentWnd,
CRuntimeClass *pViewClass,
CCreateContext *pContext,
LPCTSTR lpszWindowName,
DWORD dwStyle,
UINT nID)
{
ASSERT(pViewClass != NULL);
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
if (pContext)
memcpy(&m_Context, pContext, sizeof(m_Context));
else {
CFrameWnd *fw = (CFrameWnd *)AfxGetMainWnd();
if (fw) {
m_Context.m_pCurrentDoc = fw->GetActiveDocument();
m_Context.m_pCurrentFrame = fw;
}
}
m_Context.m_pNewViewClass = pViewClass;
return TViewBarBase::Create(
lpszWindowName,
pParentWnd,
nID,
dwStyle);
}
/* ---------------------------------------------------------------
Create the frame window associated with the view bar.
--------------------------------------------------------------- */
int CViewBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (TViewBarBase::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_pFrameWnd->Create(NULL, NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
CRect(0,0,0,0), this, NULL, 0,
&m_Context))
return -1;
return 0;
}
/* ---------------------------------------------------------------
Must remember to move the frame window as well...
--------------------------------------------------------------- */
void CViewBar::OnSize(UINT nType, int cx, int cy)
{
CRect rc;
TViewBarBase::OnSize(nType, cx, cy);
GetClientRect(rc);
m_pFrameWnd->MoveWindow(rc);
}
An aside:
Ho, humm. Don't you wish there were some way to represent the direct base class rather than having to specify it explicitly ?
Something like 'base::' would be handy. You could write code more generically. Sure, you could use templates but that is more complex than I like.
Step 2
How to create the view: Do the obvious thing and pass it through to MFC and let it do all the work. View information can be passed to MFC using the CCreateContext object. This is passed to many of the window creation functions in MFC. MFC uses this when creating a view. Unfortunately, this create context is not passed in the original sizing control bar create, meaning a little subterfuge is required (unless you want to modify the original class). Consequently, a CCreateContext object is maintained in the ViewBar class simply for the sake of not modifying the base class.
It's needed to be passed when frame window Create() is called. If you don't like this, you can modify the CSizingControlBar::Create function to accept a CCreateContext. However, since it was possible to avoid, I avoided it. This way my class is decoupled from the original, meaning if the original author decides to do an update I'll have a much easier time updating my software. And I, like all good programmers am incredibly lazy.
To create a ViewBar in you app use code like the following: (see the sample)
sTitle.Format(_T("My Bar %d"), i + 1);
if (!m_wndMyBars[i].Create(this,
RUNTIME_CLASS (CAForm),
(CCreateContext *)(lpCreateStruct->lpCreateParams),
sTitle))//AFX_IDW_CONTROLBAR_FIRST + 33 + i))
{
TRACE0("Failed to create ViewBar\n");
return -1; // fail to create
}
Aside from the create function, it can be used exactly like the existing sizing control bar classes. You need only pass the RUNTIME_CLASS of the view you want to create.
|
 |
 |