Figure 2 FormSwap
MainFrm.h
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
class CRightView;
class CLeftView;
class CMySplitterWnd : public CSplitterWnd {
public:
// functions to get protected CSplitterWnd dimensions
CSize GetBorderSize() { return CSize(m_cxBorder, m_cyBorder); }
CSize GetSplitterSize() { return CSize(m_cxSplitter, m_cySplitter); }
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo);
};
class CMainFrame : public CFrameWnd {
public:
CMainFrame();
virtual ~CMainFrame();
protected:
CStatusBar m_wndStatusBar; // status bar
CToolBar m_wndToolBar; // toolbar
CMySplitterWnd m_wndSplitter; // splitter window
virtual BOOL OnCreateClient( LPCREATESTRUCT lpcs, CCreateContext* pContext);
virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo);
virtual void CalcWindowRect(LPRECT lpClientRect,
UINT nAdjustType = adjustBorder);
DECLARE_DYNCREATE(CMainFrame)
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
};
MainFrm.cpp
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
// #includes, etc.
.
.
.
const CXLIST = 50;
// Get the size of a dialog
static CSize GetDialogSize(LPCTSTR lpszTemplName, CWnd* pParent);
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT);
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT))) {
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// Make frame just big enough to fit dialog
CSize sz = GetDialogSize(MAKEINTRESOURCE(IDD_DIALOG1), this);
CRect rc(CPoint(0,0), sz);
CalcWindowRect(&rc);
SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), SWP_NOMOVE|SWP_NOZORDER);
return 0;
}
//////////////////
// Client window created: Set up splitter window and save
// pointers to the two panes
//
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pcc)
{
// create splitter window
if (!m_wndSplitter.CreateStatic(this, 1, 2))
return FALSE;
CSplitterWnd& sw = m_wndSplitter;
if (!sw.CreateView(0,0,RUNTIME_CLASS(CLeftView), CSize(CXLIST,100), pcc) ||
!sw.CreateView(0,1,RUNTIME_CLASS(CRightView),CSize(100,100), pcc)) {
sw.DestroyWindow();
return FALSE;
}
CRightView* pRightView = STATIC_DOWNCAST(CRightView, sw.GetPane(0,1));
ASSERT(pRightView);
CLeftView* pLeftView = STATIC_DOWNCAST(CLeftView, sw.GetPane(0,0));
ASSERT(pLeftView);
// Connect views to each other
pRightView->SetListView(pLeftView);
pLeftView->SetFormView(pRightView);
// Activate the input view. This is important for it to receive commands.
SetActiveView(pRightView);
return TRUE;
}
//////////////////
// Splitter wnd function to route commands to all splitter panes/views.
//
BOOL CMySplitterWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
for (int row=0; row<GetRowCount(); row++) {
for (int col=0; col<GetColumnCount(); col++) {
if (GetPane(row,col)->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
}
}
// Call default routing--very important!
return CSplitterWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
//////////////////
// Route commands to splitter window.
//
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// Pass to splitter
if (m_wndSplitter.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// Call default routing--very important!
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
//////////////////
// CDialog class to get the size of a dialog.
// Assumes dialog is invisible (no WS_VISIBLE).
//
class CTempDlg : public CDialog {
public:
CRect m_rcDlg;
protected:
virtual BOOL OnInitDialog();
};
////////////////
// Initialize dialog: save size, then quit.
//
BOOL CTempDlg::OnInitDialog()
{
GetWindowRect(&m_rcDlg);
m_rcDlg -= m_rcDlg.TopLeft();
EndDialog(0);
return TRUE;
}
//////////////////
// static fn uses CTempDlg to get the size of any dialog used in a CFormView.
//
static CSize GetDialogSize(LPCTSTR lpszTemplName, CWnd* pParent)
{
CTempDlg dlg;
dlg.Create(lpszTemplName, pParent);
return dlg.m_rcDlg.Size();
}
//////////////////
// Calculate the size of the total frame, given a desired client (form) size.
// Start with client rect and add all the extra stuff.
//
void CMainFrame::CalcWindowRect(PRECT prcClient, UINT nAdjustType)
{
CRect rect(0, 0, 32767, 32767);
RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery,
&rect, &rect, FALSE);
CSize szBorder = m_wndSplitter.GetBorderSize();
CSize szSplitter = m_wndSplitter.GetSplitterSize();
prcClient->bottom += rect.Height() + // total toolbar height +
GetSystemMetrics(SM_CYMENU) + // menu +
GetSystemMetrics(SM_CYCAPTION) + // title bar +
2 * GetSystemMetrics(SM_CYSIZEFRAME) + // top/bottom window frame +
2 * szBorder.cy; // splitter borders
prcClient->right += CXLIST + // left pane width
2 * GetSystemMetrics(SM_CXSIZEFRAME) + // L/R window border
4 * szBorder.cx + // 2 panes==> 4 borders
szSplitter.cx; // 1 splitter
}
LeftView.h
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
class CMyDoc;
class CRightView;
//////////////////
// Left hand splitter pane is a list view.
//
class CLeftView : public CListView {
public:
virtual ~CLeftView();
CMyDoc* GetDocument();
virtual void OnInitialUpdate();
void SetFormView(CRightView* pFormView) { m_pFormView = pFormView; }
protected:
CRightView* m_pFormView;
virtual BOOL OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* plr);
CLeftView();
DECLARE_DYNCREATE(CLeftView)
DECLARE_MESSAGE_MAP()
afx_msg LRESULT OnUpdateUI(WPARAM, LPARAM);
afx_msg LRESULT OnItemChanged(NMLISTVIEW* pnmlv);
};
LeftView.cpp
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
// #includes, etc.
•
•
•
CLeftView::CLeftView()
{
m_pFormView = NULL;
}
CLeftView::~CLeftView()
{
}
LPCTSTR FormNames[NFORMS] = {
_T("Form 1"), _T("Form 2"), _T("Form 3"), _T("Form 4") };
//////////////////
// Update view for first time: add form names
//
void CLeftView::OnInitialUpdate()
{
CListView::OnInitialUpdate();
CListCtrl& lc = GetListCtrl();
lc.ModifyStyle(LVS_TYPEMASK, LVS_LIST|LVS_SHOWSELALWAYS);
// add form names to list
lc.DeleteAllItems();
for (int i=0; i<NFORMS; i++)
lc.InsertItem(i, FormNames[i]);
}
//////////////////
// Handle WM_IDLEUPDATECMDUI: Set selection state based on current form.
//
LRESULT CLeftView::OnUpdateUI(WPARAM, LPARAM)
{
int iWhich = m_pFormView->GetForm();
CListCtrl& lc = GetListCtrl();
LVITEM lvi;
memset(&lvi, 0, sizeof(lvi));
lvi.mask = LVIF_STATE;
lvi.stateMask = LVIS_SELECTED|LVIS_FOCUSED;
for (int i=0; i<=NFORMS; i++) {
lvi.iItem = i;
lvi.state = (i==iWhich) ? LVIS_SELECTED|LVIS_FOCUSED : 0;
lc.SetItem(&lvi);
}
return 0;
}
//////////////////
// Reflect-handle LVN_ITEMCHANGED to select the chosen form. This should
// really be implemented as an ON_NOTIFY_REFLECT handler, but there is an
// elusive bug somewhere that occurs in release builds only when I use
// ON_NOTIFY_REFLECT, so I am doing it this way instead. OnChildNotify lets
// any control handle its own notifications.
//
BOOL CLeftView::OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* plr)
{
if (msg==WM_NOTIFY) {
ASSERT(lp);
if (((NMHDR*)lp)->code == LVN_ITEMCHANGED)
OnItemChanged((NMLISTVIEW*)lp);
}
return CListView::OnChildNotify(msg, wp, lp, plr);
}
//////////////////
// User selected a different item: select the form
//
LRESULT CLeftView::OnItemChanged(NMLISTVIEW* pnmlv)
{
if ((pnmlv->uChanged & LVIF_STATE) &&
(pnmlv->uNewState & LVIS_SELECTED)) {
m_pFormView->SetForm(pnmlv->iItem);
}
return 0;
}
RightView.h
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
#include "Doc.h"
const NFORMS=4; // total number of forms
class CListView;
//////////////////
// Right hand splitter pane is a form view
//
class CRightView : public CFormView {
public:
virtual ~CRightView();
CMyDoc* GetDocument() { return (CMyDoc*)m_pDocument; }
void SetListView(CListView* pListView) { m_pListView = pListView; }
int GetForm();
BOOL SetForm(UINT iWhich);
BOOL SetForm(LPCTSTR lpszTemplateName);
protected:
CRightView();
CListView* m_pListView;
DECLARE_DYNCREATE(CRightView)
DECLARE_MESSAGE_MAP()
afx_msg void OnViewNextForm();
};
RightView.cpp
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal.
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 5.0 on Windows 95
//
// CRightView shows how to change the form in a CFormView.
// CRightView::SetForm is the function that does it.
//
// #includes, etc.
.
.
.
IMPLEMENT_DYNCREATE(CRightView, CFormView)
BEGIN_MESSAGE_MAP(CRightView, CFormView)
ON_COMMAND(ID_NEXT_FORM, OnViewNextForm)
END_MESSAGE_MAP()
CRightView::CRightView() : CFormView(IDD_DIALOG1)
{
m_pListView = NULL;
}
CRightView::~CRightView()
{
}
//////////////////
// Handle View | Other Form command: call ChangeForm with new template name
//
void CRightView::OnViewNextForm()
{
UINT nID = (UINT)m_lpszTemplateName - IDD_DIALOGFIRST;
if (++nID >= NFORMS)
nID = 0;
SetForm(nID);
}
//////////////////
// Change the form underlying a CFormView.
//
BOOL CRightView::SetForm(UINT iWhich)
{
ASSERT(0<=iWhich && iWhich<NFORMS);
return SetForm(MAKEINTRESOURCE(IDD_DIALOGFIRST + iWhich));
}
//////////////////
// This is the function that changes the form within the form view.
//
BOOL CRightView::SetForm(LPCTSTR lpszTemplateName)
{
if (lpszTemplateName == m_lpszTemplateName)
return TRUE;
// Must temporarily remove the doc so MFC won't have a conniption.
CDocument* pSaveDoc = m_pDocument;
m_pDocument = NULL; // remove
// Preserve same window ID (usually AFX_IDW_PANE_FIRST)
UINT nIDView = GetDlgCtrlID();
// Preserve same parent (frame)
CWnd* pParent = GetParent();
ASSERT(pParent);
// Preserve same window pos and style
CRect rc;
GetWindowRect(&rc);
pParent->ScreenToClient(&rc);
DWORD dwStyle = GetStyle();
// Remove the current dialog (window) and destroy it.
HWND hwnd = UnsubclassWindow();
ASSERT(hwnd);
::DestroyWindow(hwnd);
// Now the view is in more or less the same state as after calling
//
// new CRightView;
//
// There is no window, no m_hWnd, and no doc. So now I can recreate
// the view, using a different dialog template. Don't need create
// context since doc already exists.
//
m_lpszTemplateName = lpszTemplateName;
VERIFY(Create(lpszTemplateName, NULL, dwStyle, rc, pParent, nIDView, NULL));
// restore document
m_pDocument = pSaveDoc;
return TRUE;
}
//////////////////
// Get current selected form as integer from 0 to NFORMS-1
//
int CRightView::GetForm()
{
int iWhich = (UINT)m_lpszTemplateName - IDD_DIALOGFIRST;
ASSERT(0<=iWhich && iWhich<NFORMS);
return iWhich;
}