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; 
}