Figure 2   VIEWS

DYNTEMPL.H

 ////////////////////////////////////////////////////////////////
// Definition for CDynViewDocTemplate, a document template class that
// implements "dynamic views", i.e., the ability to change the view
// class dynamically, while the app is running. See DynTempl.cpp.
// CDynViewDocTemplate is designed to work in a MDI app.
//
// Copyright 1996 Microsoft Systems Journal. If this code works, 
// it was written by Paul DiLascia. If not, I don't know who wrote it.

////////////////
// This structure associates a view class with 
// a display name and index (position in table).
//
struct DYNAMICVIEWINFO {
   CRuntimeClass* rtc;     // MFC runtime class
   LPCSTR         name;    // display name of view
};

//////////////////
// CDynViewDocTemplate manages multiple views of the same doc. 
// It handles the "View As" commands, and restoring of initial 
// view when a document is loaded.
//
class CDynViewDocTemplate : public CMultiDocTemplate {
public:
   CDynViewDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
      CRuntimeClass* pFrameClass, 
      const DYNAMICVIEWINFO* pViewInfo);
   virtual ~CDynViewDocTemplate();

   virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,
      BOOL bMakeVisible = TRUE);

   int GetViewID(CDocument *pDoc);  // <== NOTE: You must implement this!
   int GetViewID(CView* pView);
   const DYNAMICVIEWINFO* GetViewInfo(CView* pView);

protected:
   DECLARE_DYNAMIC(CDynViewDocTemplate)
   const DYNAMICVIEWINFO*  m_pViewInfo;
   UINT                    m_nViewClasses;

   // Helper fn to change the view
   CView* ReplaceView(CFrameWnd* pFrame, int iNewView, BOOL bMakeVis=TRUE);

   //{{AFX_MSG(CDynViewDocTemplate)
   afx_msg BOOL OnViewAs(UINT nCmdID);
   afx_msg void OnUpdateViewAs(CCmdUI* pCmdUI);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

DYNTEMPL.CPP

 ////////////////////////////////////////////////////////////////
// Implementation for CDynViewDocTemplate, a document template that
// supports multiple views per doc.
//
// Copyright 1996 Microsoft Systems Journal. If this code works, 
// it was written by Paul DiLascia. If not, I don't know who wrote it.
//
#include "stdafx.h"
#include "doc.h"
#include "dyntempl.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CDynViewDocTemplate, CMultiDocTemplate)

//////////////////
// NOTE: You must #define ID_VIEW_AS_BEGIN and ID_VIEW_AS_END in your 
// resource.h file. CDynViewDocTemplate uses RANGE handlers to handle 
// the "View As" commands and command update, in the given range.
//
BEGIN_MESSAGE_MAP(CDynViewDocTemplate, CDocTemplate)
   //{{AFX_MSG_MAP(CDynViewDocTemplate)
   ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_AS_BEGIN,ID_VIEW_AS_END,OnUpdateViewAs)
   ON_COMMAND_EX_RANGE(ID_VIEW_AS_BEGIN, ID_VIEW_AS_END, OnViewAs)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//////////////////
// Constructor is like CMultiDocTemplate, but takes an array of 
// DYNAMICVIEWINFO's instead of a single runtime class for the view.
//
CDynViewDocTemplate::CDynViewDocTemplate(UINT nIDResource, 
   CRuntimeClass* pDocClass, 
   CRuntimeClass* pFrameClass, 
   const DYNAMICVIEWINFO* pViewInfo)
   : CMultiDocTemplate(nIDResource, pDocClass, pFrameClass, NULL)
{
   ASSERT(pViewInfo);
   m_pViewInfo = pViewInfo;
   m_pViewClass = pViewInfo[0].rtc;

#ifdef _DEBUG
   // Check that # of entries in table matches begin/end command IDs
   for (int n=0; pViewInfo[n].name; n++)
      ;
   if (n!=ID_VIEW_AS_END-ID_VIEW_AS_BEGIN+1) {
      TRACE0("Number of view classes in table does not match command ID\n");
      TRACE0("range ID_VIEW_AS_BEGIN to ID_VIEW_AS_END!\n");
      ASSERT(FALSE);
   }
#endif
}

CDynViewDocTemplate::~CDynViewDocTemplate()
{
}

//////////////////
// Get the 0-based view class ID given a ptr to a view.
//
int CDynViewDocTemplate::GetViewID(CView* pView)
{
   for (int i=0; m_pViewInfo[i].name; i++) {
      if (m_pViewInfo[i].rtc == pView->GetRuntimeClass())
         return i;
   }
   return -1; // (invalid)
}

//////////////////
// Get class info about a view.
//
const DYNAMICVIEWINFO* CDynViewDocTemplate::GetViewInfo(CView* pView)
{
   ASSERT_VALID(pView);
   int iViewID = GetViewID(pView);
   return iViewID >=0 ? &m_pViewInfo[iViewID] : NULL;
}

//////////////////
// Override to change the view class when a document is opened,
// to use whichever view class was saved with the doc.
//
void CDynViewDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, 
   CDocument* pDoc, BOOL bMakeVisible)
{
   CMultiDocTemplate::InitialUpdateFrame(pFrame, pDoc, bMakeVisible);
   ReplaceView(pFrame, GetViewID(pDoc), bMakeVisible);
}

//////////////////
// Handle "View As Xxx" command to change view class. Both this and
// InitialUpdateFrame call a common helper, ReplaceView.
//
BOOL CDynViewDocTemplate::OnViewAs(UINT nCmdID) 
{
   CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
   ASSERT_VALID(pFrame);
   ASSERT(pFrame->IsFrameWnd());
   pFrame = pFrame->GetActiveFrame();
   ReplaceView(pFrame, nCmdID-ID_VIEW_AS_BEGIN, TRUE);
   return FALSE;
}

///////////////////
// Replace the current view with a one of a different class
//
CView* CDynViewDocTemplate::ReplaceView(CFrameWnd* pFrame, 
   int iNewViewType, BOOL bMakeVisible)
{
   ASSERT(ID_VIEW_AS_BEGIN<=iNewViewType+ID_VIEW_AS_BEGIN && 
      iNewViewType+ID_VIEW_AS_BEGIN<=ID_VIEW_AS_END);

   CView* pView = pFrame->GetActiveView();
   ASSERT_VALID(pView);

   CRuntimeClass* pViewClass = m_pViewInfo[iNewViewType].rtc;
   if (!pView->IsKindOf(pViewClass)) {

      // Tell MFC not to delete the doc when the view is destroyed
      CDocument* pDoc = pView->GetDocument();
      BOOL bSaveAutoDelete = pDoc->m_bAutoDelete;
      pDoc->m_bAutoDelete = FALSE;
      pFrame->SetActiveView(NULL);  // it's safer

#ifndef DONT_RECYCLE_HWND
      // This implementation reuses the actual (Windows) window
      //
      HWND hwnd = pView->Detach();  // remove window from view
      delete pView;                 // destroy C++ obj & remove from doc too
      pView = (CView*)pViewClass->CreateObject();  // create new view
      ASSERT(pView);
      ASSERT_KINDOF(CView, pView);
      pView->Attach(hwnd);          // reuse the same HWND !
      pDoc->AddView(pView);         // add view to doc
      pFrame->SetActiveView(pView); // make it the active view
#else
      // This implementation create a whole new (Windows) window
      //
      CRect rcView;
      pView->GetWindowRect(&rcView);// save original view window size
      pView->DestroyWindow();       // destroy old view

      // Create new view using CFrameWnd::CreateView, 
      // which requires a "CCreateContext"
      CCreateContext context;
      context.m_pNewViewClass = pViewClass;
      context.m_pCurrentDoc   = pDoc;
      pView = (CView*)pFrame->CreateView(&context);
      ASSERT(pView);
      ASSERT_VALID(pView);

      // Set view window size to same as old view
      pFrame->ScreenToClient(&rcView);
      pView->MoveWindow(&rcView, FALSE);
#endif

      pDoc->m_bAutoDelete = bSaveAutoDelete;

      // This will do good stuff, like update the title 
      // and send WM_INITIALUPDATE to the view.
      //
      pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
   }
   return pView;
}

//////////////////
// Update "View As Xxx" command: set radio button for whichever kind
// of view is currently active.
//

void CDynViewDocTemplate::OnUpdateViewAs(CCmdUI* pCmdUI) 
{
   ASSERT(ID_VIEW_AS_BEGIN<=pCmdUI->m_nID && pCmdUI->m_nID<=ID_VIEW_AS_END);

   CFrameWnd* pFrame = (CFrameWnd*)AfxGetMainWnd();
   ASSERT_VALID(pFrame);
   ASSERT(pFrame->IsFrameWnd());
   CView* pView = pFrame->GetActiveFrame()->GetActiveView();
   pCmdUI->SetRadio(pView && 
      pView->IsKindOf(m_pViewInfo[pCmdUI->m_nID-ID_VIEW_AS_BEGIN].rtc));
}

CHILDFRM.H

 /////////////////
// MDI child frame overrides OnUpdateFrameTitle to 
// show which view is displayed.
//
class CChildFrame : public CMDIChildWnd {
   DECLARE_DYNCREATE(CChildFrame)
public:
   CChildFrame();
   virtual ~CChildFrame();
   virtual void OnUpdateFrameTitle(BOOL bAddToTitle);
protected:
   //{{AFX_MSG(CChildFrame)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

CHILDFRM.CPP

 #include "stdafx.h"
#include "view3.h"
#include "doc.h"
#include "view.h"
#include "ChildFrm.h"
#include "DynTempl.h"

IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)

BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
   //{{AFX_MSG_MAP(CChildFrame)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CChildFrame::CChildFrame()
{
}

CChildFrame::~CChildFrame()
{
}

//////////////////
// Display the view type in the window title
//
void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
   // First let MFC do standard title
   CMDIChildWnd::OnUpdateFrameTitle(bAddToTitle);
   
   CDocument* pDoc = GetActiveDocument();
   if (pDoc) {
      CDynViewDocTemplate* pTemplate = 
         (CDynViewDocTemplate*)pDoc->GetDocTemplate();
      ASSERT_KINDOF(CDynViewDocTemplate, pTemplate);
      ASSERT_VALID(pTemplate);

      CBaseView* pView = (CBaseView*)GetActiveView();
      const DYNAMICVIEWINFO* pInfo = pTemplate->GetViewInfo(pView);
      if (pInfo) {
         char text[256];
         GetWindowText(text, sizeof(text));  // Get MFC title..
         strcat(text, " As ");               // and append " As [viewName]"
         strcat(text, pInfo->name);          // ..
         SetWindowText(text);                // change frame title
      }
   }
}

DOC.H

 //////////////////
// Document class holds a string of text and what 
// kind of view last viewed the doc.
//
class CView3Doc : public CDocument {
   DECLARE_DYNCREATE(CView3Doc)
public:
   virtual  ~CView3Doc();
   void     SetText(const char* pszText);
   LPCSTR   GetText()      { return m_sText; }
   int      GetViewID()    { return m_nViewID; }
   virtual  void Serialize(CArchive& ar);
protected:
   CView3Doc();
   CString  m_sText;       // contents of doc
   int      m_nViewID;     // identifies view that last saved the doc
   //{{AFX_MSG(CView3Doc)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

DOC.CPP

 #include "stdafx.h"
#include "view3.h"
#include "doc.h"
#include "view.h"
#include "dyntempl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CView3Doc, CDocument)

BEGIN_MESSAGE_MAP(CView3Doc, CDocument)
   //{{AFX_MSG_MAP(CView3Doc)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CView3Doc::CView3Doc() : m_sText("Use \"File Edit\" to change this text.")
{
   m_nViewID = 0; // default view = 1st view in table
}

CView3Doc::~CView3Doc()
{
}

/////////////////
// Change document text
//
void CView3Doc::SetText(const char* pszText)
{ 
   m_sText = pszText; 
   SetModifiedFlag();
   UpdateAllViews(NULL);
}

////////////////
// Serialization: Save doc text and class "ID" of current active view.
// The ID is the 0-based index into the doc template's view class table.
//
void CView3Doc::Serialize(CArchive& ar)
{
   if (ar.IsStoring()) {
      ar << m_sText;
      POSITION pos = GetFirstViewPosition();
      ASSERT(pos);
      m_nViewID = ((CDynViewDocTemplate*)GetDocTemplate())->
         GetViewID(GetNextView(pos));
      ar << m_nViewID;
   } else {
      ar >> m_sText;       // restore text and view class ID
      ar >> m_nViewID;     // CDynViewDocTemplate will change the view
   }
}

///////////////////
// This function should be in dyntempl.cpp, but since it's the only
// function that needs to know about the document, it's easier to put it
// here and require that the "programmer" implement it. Otherwise, I'd
// have to implement a new class CDynViewDoc, and require apps to derive
// their doc from it. 
//
int CDynViewDocTemplate::GetViewID(CDocument *pDoc) 
{
   ASSERT_KINDOF(CView3Doc, pDoc);
   return ((CView3Doc*)pDoc)->GetViewID();
}

RESOURCE.H

 //{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by view3.rc
//
#define IDD_ABOUTBOX                    100
#define IDR_MAINFRAME                   128
#define IDR_VIEW3TYPE                   129
#define IDD_FILEEDIT                    130
#define IDC_EDIT1                       1000
#define ID_VIEW_AS_TYPE1                32771
#define ID_VIEW_AS_TYPE2                32772
#define ID_VIEW_AS_TYPE3                32773
#define IDD_FILE_EDIT                   32774
#define ID_FILE_EDIT                    32775

#define ID_VIEW_AS_BEGIN                ID_VIEW_AS_TYPE1
#define ID_VIEW_AS_END                  ID_VIEW_AS_TYPE3

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        131
#define _APS_NEXT_COMMAND_VALUE         32776
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

VIEW.CPP

 ////////////////////////////////////////////////////////////////
// Implementation of the three view classes in VIEW3
//
#include "stdafx.h"
#include "view3.h"
#include "Doc.h"
#include "View.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CBaseView, CView)

BEGIN_MESSAGE_MAP(CBaseView, CView)
   //{{AFX_MSG_MAP(CBaseView)
   ON_COMMAND(ID_FILE_EDIT, OnFileEdit)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CBaseView::CBaseView()
{
}

CBaseView::~CBaseView()
{
}

//////////////////
// Dialog to edit the document contents (a text string).
//
class CFileEditDlg : public CDialog {
public:
   CString  m_sText;
   CFileEditDlg(CWnd* pParent = NULL) : CDialog(IDD_FILEEDIT, pParent) { }
   virtual void DoDataExchange(CDataExchange* pDX) {
      CDialog::DoDataExchange(pDX);
      DDX_Text(pDX, IDC_EDIT1, m_sText);
      DDV_MaxChars(pDX, m_sText, 255);
   }
};

//////////////////
// Handle "File Edit" command
//
void CBaseView::OnFileEdit() 
{
   CView3Doc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   CFileEditDlg dlg;
   dlg.m_sText = pDoc->GetText();
   if (dlg.DoModal() == IDOK)
      pDoc->SetText(dlg.m_sText);
}

//////////////////////////////////////////////////////////////////////
// The three views follow. Each one implements OnDraw a different way.

IMPLEMENT_DYNCREATE(CView1, CBaseView)
IMPLEMENT_DYNCREATE(CView2, CBaseView)
IMPLEMENT_DYNCREATE(CView3, CBaseView)

//////////////////
// Draw document contents as ASCII text
//
void CView1::OnDraw(CDC* pDC)
{
   CView3Doc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   CString s = pDoc->GetText();
   CRect rc;
   GetClientRect(&rc);
   pDC->DrawText(s, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}

//////////////////
// Draw document contents as binary 1's and 0's
//
void CView2::OnDraw(CDC* pDC)
{
   CView3Doc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);

   // It's overkill to recompute this on every draw, but who cares? 
   // CPU cycles are cheap, and you gotta give that fan a reason to spin!
   //
   CString s;
   for (LPCSTR p = pDoc->GetText(); *p; p++) {  // for each character:
      for (int bit=0; bit<8; bit++)             //   for each bit:
         s += (*p & 1<<bit) ? '1' : '0';        //     append 0 or 1
      s+= ' ';                                  //   space between chars
   }
   CRect rc;
   GetClientRect(&rc);
   pDC->DrawText(s, s.GetLength(), &rc, DT_LEFT|DT_WORDBREAK);
}

//////////////////
// Draw document contents as vertical stripes like a bar code
//
void CView3::OnDraw(CDC* pDC)
{
   CView3Doc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   CRect rc;
   GetClientRect(&rc);
   int x=0, bottom = rc.Height();
   for (LPCSTR p = pDoc->GetText(); *p; p++) {  // for each character:
      for (int bit=0; bit<8; bit++) {           //   for each bit:
         if (*p & 1<<bit) {                     //     if bit is ON:
            pDC->MoveTo(x, 0);                  //       draw a..
            pDC->LineTo(x, bottom);             //       ..vertical line
         }
         x++;                                   //   next x-pixel
      }
   }
}

VIEW3.H

 ////////////////////////////////////////////////////////////////
// VIEW3 Copyright 1996 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// See VIEW3.CPP for Description of program.
#include "resource.h"

//////////////////
// Application class is generic
class CApp : public CWinApp {
public:
   virtual BOOL InitInstance();
   //{{AFX_MSG(CApp)
   afx_msg void OnAppAbout();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

VIEW3.CPP

 ////////////////////////////////////////////////////////////////
// VIEW3 Copyright 1996 Microsoft Systems Journal. 
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// VIEW3 Shows how to use CDynViewDocTemplate to implement an app that
// supports different views of the same document.
// This program requires VC++ 4.0 or later.
//
#include "stdafx.h"
#include "view3.h"
#include "MainFrm.h"
#include "ChildFrm.h"
#include "Doc.h"
#include "View.h"
#include "DynTempl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

BEGIN_MESSAGE_MAP(CApp, CWinApp)
   //{{AFX_MSG_MAP(CApp)
   ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
   //}}AFX_MSG_MAP
   ON_COMMAND(ID_FILE_NEW,  CWinApp::OnFileNew)
   ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

// To add another view, put it in this table.
// Default view is first, must be in same order as "View As" menu commands.
static const DYNAMICVIEWINFO MyViewClasses[] = {
   { RUNTIME_CLASS(CView1), "ASCII" },
   { RUNTIME_CLASS(CView2), "Binary" },
   { RUNTIME_CLASS(CView3), "Binary Stripes" },
   { NULL, NULL }
};

CApp theApp;

BOOL CApp::InitInstance()
{
   // Create "dynamic" document template. 
   AddDocTemplate(new CDynViewDocTemplate(IDR_VIEW3TYPE,
      RUNTIME_CLASS(CView3Doc),
      RUNTIME_CLASS(CChildFrame),
      MyViewClasses)); // array of view classes

   // create main MDI Frame window (standard MFC)...
   CMainFrame* pMainFrame = new CMainFrame;
   if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
      return FALSE;
   m_pMainWnd = pMainFrame;

   // Standard AppWiz junk...
   CCommandLineInfo cmdInfo;
   ParseCommandLine(cmdInfo);
   if (!ProcessShellCommand(cmdInfo))
      return FALSE;
   pMainFrame->ShowWindow(m_nCmdShow);
   pMainFrame->UpdateWindow();

   return TRUE;
}

void CApp::OnAppAbout()
{
   CDialog aboutDlg(IDD_ABOUTBOX);
   aboutDlg.DoModal();
}