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