This is a continuation from the previous module... Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:
The MYMFC21B Example: A Double View Class SDI Static Splitter
In MYMFC21B, we'll extend MYMFC21 by defining a second view class and allowing a static splitter window to show the two views. (The HexView.h and HexView.cpp files are cloned from the original view class.) This time the splitter window works a little differently. Instead of starting off as a single pane, the splitter is initialized with two panes. The user can move the bar between the panes by dragging it with the mouse or by choosing the Window Split menu item. The easiest way to generate a static splitter application is to let AppWizard generate a dynamic splitter application and then edit the generated CMainFrame::OnCreateClient function.
|
The steps
As usual, begin with the Visual C++ AppWizard, creating an SDI application.
Figure 12: Step 4 of 6 AppWizard for MYMFC21B, invoking the Advanced options.
Figure 13: Using the split window.
|
Figure 14: Step 6 of 6, renaming the view files, class and using CScrollView as the base view class.
Figure 15: Step 6 of 6, renaming the document files and class.
Figure 16: MYMFC21B project summary.
Note that this is an SDI application. Add a CStringArray data member to the CPoemDoc class. Edit the PoemDoc.h header file or use ClassView.
public:
CStringArray m_stringArray;
Figure 17: Using ClassView to add a CStringArray data member to the CPoemDoc class.
Figure 18: Entering the member variable type and name.
Listing 12.
The document data is stored in a string array. The MFC library CStringArray class holds an array of CString objects, accessible by a zero-based subscript. You need not set a maximum dimension in the declaration because the array is dynamic.
Add a CRect data member to the CStringView class. Edit the StringView.h header file or use ClassView:
private:
CRect m_rectPrint;
Figure 19: Using ClassView to add a CRect data member to the CStringView class.
Figure 20: Entering the member variable type and name.
Listing 13.
Edit three CPoemDoc member functions in the file PoemDoc.cpp. AppWizard generated skeleton OnNewDocument() and Serialize() functions, but we'll have to use ClassWizard to override the DeleteContents() function. We'll initialize the poem document in the overridden OnNewDocument() function. DeleteContents() is called in CDocument::OnNewDocument, so by calling the base class function first we're sure the poem won't be deleted. The text, by the way, is an excerpt from the twentieth poem in Lawrence Ferlinghetti's book A Coney Island of the Mind. Type 10 lines of your choice. You can substitute another poem or maybe your favorite Win32 function description. Add the following code:
BOOL CPoemDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
m_stringArray.SetSize(10);
m_stringArray[0] = "The pennycandystore beyond the El";
m_stringArray[1] = "is where I first";
m_stringArray[2] = " fell in love";
m_stringArray[3] = " with unreality";
m_stringArray[4] = "Jellybeans glowed in the semi-gloom";
m_stringArray[5] = "of that september afternoon";
m_stringArray[6] = "A cat upon the counter moved among";
m_stringArray[7] = " the licorice sticks";
m_stringArray[8] = " and tootsie rolls";
m_stringArray[9] = " and Oh Boy Gum";
return TRUE;
}
Listing 14.
The CStringArray class supports dynamic arrays, but here we're using the m_stringArray object as though it were a static array of 10 elements. The application framework calls the document's virtual DeleteContents() function when it closes the document; this action deletes the strings in the array. A CStringArray contains actual objects, and a CObArray contains pointers to objects. This distinction is important when it's time to delete the array elements. Here, the RemoveAll() function actually deletes the string objects:
void CPoemDoc::DeleteContents()
{
// called before OnNewDocument() and when document is closed
m_stringArray.RemoveAll();
}
Figure 21: As in MYMFC21, adding the RemoveAll() function to the document class.
Listing 15.
Serialization isn't important in this example, but the following function illustrates how easy it is to serialize strings. The application framework calls the DeleteContents() function before loading from the archive, so you don't have to worry about emptying the array. Add the following code:
void CPoemDoc::Serialize(CArchive& ar)
{
m_stringArray.Serialize(ar);
}
Listing 16.
Edit the OnInitialUpdate() function in StringView.cpp. You must override the function for all classes derived from CScrollView. This function's job is to set the logical window size and the mapping mode. Add the following code:
void CStringView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height());
CSize sizePage(sizeTotal.cx / 2, sizeTotal.cy / 2); // page scroll
CSize sizeLine(sizeTotal.cx / 100, sizeTotal.cy / 100); // line scroll
SetScrollSizes(MM_TWIPS, sizeTotal, sizePage, sizeLine);
}
Listing 17.
Edit the OnDraw() function in StringView.cpp. The OnDraw() function of class CStringView draws on both the display and the printer. In addition to displaying the poem text lines in 10-point roman font, it draws a border around the printable area and a crude ruler along the top and left margins. OnDraw() assumes the MM_TWIPS mapping mode, in which 1 inch = 1440 units. Add the code shown below.
void CStringView::OnDraw(CDC* pDC)
{
int i, j, nHeight;
CString str;
CFont font;
TEXTMETRIC tm;
CPoemDoc* pDoc = GetDocument();
// Draw a border — slightly smaller to avoid truncation
pDC->Rectangle(m_rectPrint + CRect(0, 0, -20, 20));
// Draw horizontal and vertical rulers
j = m_rectPrint.Width() / 1440;
for (i = 0; i <= j; i++)
{
str.Format("%02d", i);
pDC->TextOut(i * 1440, 0, str);
}
j = -(m_rectPrint.Height() / 1440);
for (i = 0; i <= j; i++)
{
str.Format("%02d", i);
pDC->TextOut(0, -i * 1440, str);
}
// Print the poem 0.5 inch down and over;
// use 10-point roman font
font.CreateFont(-200, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN, "Times New Roman");
CFont* pOldFont = (CFont*) pDC->SelectObject(&font);
pDC->GetTextMetrics(&tm);
nHeight = tm.tmHeight + tm.tmExternalLeading;
TRACE("font height = %d, internal leading = %d\n", nHeight, tm.tmInternalLeading);
j = pDoc->m_stringArray.GetSize();
for (i = 0; i < j; i++)
{
pDC->TextOut(720, -i * nHeight - 720, pDoc->m_stringArray[i]);
}
pDC->SelectObject(pOldFont);
TRACE("LOGPIXELSX = %d, LOGPIXELSY = %d\n", pDC->GetDeviceCaps(LOGPIXELSX),
pDC->GetDeviceCaps(LOGPIXELSY));
TRACE("HORZSIZE = %d, VERTSIZE = %d\n", pDC->GetDeviceCaps(HORZSIZE),
pDC->GetDeviceCaps(VERTSIZE));
}
Listing 18.
Edit the OnPreparePrinting() function in StringView.cpp. This function sets the maximum number of pages in the print job. This example has only one page. It's absolutely necessary to call the base class DoPreparePrinting() function in your overridden OnPreparePrinting() function. Add the following code:
BOOL CStringView::OnPreparePrinting(CPrintInfo* pInfo)
{
pInfo->SetMaxPage(1);
return DoPreparePrinting(pInfo);
}
Listing 19.
Edit the constructor in StringView.cpp. The initial value of the print rectangle should be 8-by-15 inches, expressed in twips (1 inch = 1440 twips). Add the following code:
CStringView::CStringView() : m_rectPrint(0, 0, 11520, -15120)
{
}
Listing 20.
Build and test the application.
Figure 22: MYMFC21B program output. |
CHexView class, the cloned CStringView Class
Next, create CHexView by cloning the CStringView. Create the header and source files (HexView.h and HexView.cpp).
Figure 23: Creating and adding new CHexView class.
Figure 24: Creating the HexView.h header file.
Copy the cloned codes (StringView.h and StringView.cpp) and paste it to the HexView.h and HexView.cpp respectively. It is essentially the same code used for CStringView except for the OnDraw() member function
HEXVIEW.H // HexView.h : interface of the CHexView class // /////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_CHEXVIEW_H__0A59E9CC_50F1_4B8E_8A3E_C3ED2955CE9D__INCLUDED_) #define AFX_CHEXVIEW_H__0A59E9CC_50F1_4B8E_8A3E_C3ED2955CE9D__INCLUDED_
#if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000
class CHexView : public CScrollView { protected: // create from serialization only CHexView(); DECLARE_DYNCREATE(CHexView)
// Attributes public: CPoemDoc* GetDocument();
// Operations public:
// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CHexView) public: virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual void OnInitialUpdate(); // called first time after construct virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); //}}AFX_VIRTUAL
// Implementation public: virtual ~CHexView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif
protected:
// Generated message map functions protected: //{{AFX_MSG(CHexView) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() private: CRect m_rectPrint; };
#ifndef _DEBUG // debug version in CHexView.cpp inline CPoemDoc* CHexView::GetDocument() { return (CPoemDoc*)m_pDocument; } #endif
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_CHEXVIEW_H__0A59E9CC_50F1_4B8E_8A3E_C3ED2955CE9D__INCLUDED_)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HEXVIEW.CPP // HexView.cpp : implementation of the CHexView class //
#include "stdafx.h" #include "mymfc21B.h"
#include "PoemDoc.h" #include "HexView.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[ ] = __FILE__; #endif
///////////////////////////////////////////////////////////////////////////// // CHexView
IMPLEMENT_DYNCREATE(CHexView, CScrollView)
BEGIN_MESSAGE_MAP(CHexView, CScrollView) //{{AFX_MSG_MAP(CHexView) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview) END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// // CHexView construction/destruction
CHexView::CHexView() : m_rectPrint(0, 0, 11520, -15120) { // TODO: add construction code here }
CHexView::~CHexView() { }
BOOL CHexView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return CScrollView::PreCreateWindow(cs); }
///////////////////////////////////////////////////////////////////////////// // CHexView drawing
void CHexView::OnDraw(CDC* pDC) { // hex dump of document strings int i, j, k, l, n, nHeight; CString outputLine, str; CFont font; TEXTMETRIC tm;
CPoemDoc* pDoc = GetDocument(); font.CreateFont(-160, 80, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial"); CFont* pOldFont = pDC->SelectObject(&font); pDC->GetTextMetrics(&tm); nHeight = tm.tmHeight + tm.tmExternalLeading;
j = pDoc->m_stringArray.GetSize(); for (i = 0; i < j; i++) { outputLine.Format("%02x ", i); l = pDoc->m_stringArray[i].GetLength(); for (k = 0; k < l; k++) { n = pDoc->m_stringArray[i][k] & 0x00ff; str.Format("%02x ", n); outputLine += str; } pDC->TextOut(720, -i * nHeight - 720, outputLine); } pDC->SelectObject(pOldFont); }
void CHexView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height()); CSize sizePage(sizeTotal.cx / 2, sizeTotal.cy / 2); // page scroll CSize sizeLine(sizeTotal.cx / 100, sizeTotal.cy / 100); // line scroll SetScrollSizes(MM_TWIPS, sizeTotal, sizePage, sizeLine); }
///////////////////////////////////////////////////////////////////////////// // CHexView printing
BOOL CHexView::OnPreparePrinting(CPrintInfo* pInfo) { pInfo->SetMaxPage(1); return DoPreparePrinting(pInfo); }
void CHexView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing }
void CHexView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing }
///////////////////////////////////////////////////////////////////////////// // CHexView diagnostics
#ifdef _DEBUG void CHexView::AssertValid() const { CScrollView::AssertValid(); }
void CHexView::Dump(CDumpContext& dc) const { CScrollView::Dump(dc); }
CPoemDoc* CHexView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPoemDoc))); return (CPoemDoc*)m_pDocument; } #endif //_DEBUG
///////////////////////////////////////////////////////////////////////////// // CHexView message handlers
|
Listing 21: CHexView class, the cloned CStringView class.
The CHexView class (HexView.h and HexView.cpp) was written to allow programmers to appreciate poetry. It is essentially the same code used for CStringView except for the OnDraw() member function:
void CHexView::OnDraw(CDC* pDC)
{
// hex dump of document strings
int i, j, k, l, n, nHeight;
CString outputLine, str;
CFont font;
TEXTMETRIC tm;
CPoemDoc* pDoc = GetDocument();
font.CreateFont(-160, 80, 0, 0, 400, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
CFont* pOldFont = pDC->SelectObject(&font);
pDC->GetTextMetrics(&tm);
nHeight = tm.tmHeight + tm.tmExternalLeading;
j = pDoc->m_stringArray.GetSize();
for (i = 0; i < j; i++) {
outputLine.Format("%02x ", i);
l = pDoc->m_stringArray[i].GetLength();
for (k = 0; k < l; k++) {
n = pDoc->m_stringArray[i][k] & 0x00ff;
str.Format("%02x ", n);
outputLine += str;
}
pDC->TextOut(720, -i * nHeight - 720, outputLine);
}
pDC->SelectObject(pOldFont);
}
This function displays a hexadecimal dump of all strings in the document's m_stringArray collection. Notice the use of the subscript operator to access individual characters in a CString object.
CMainFrame Class
As in MYMFC21, the MYMFC21B application's main frame window class needs a splitter window data member and a prototype for an overridden OnCreateClient() function. You can let AppWizard generate the code by specifying Use Split Window, as in MYMFC21. You won't have to modify the MainFrm.h file. The implementation file, MainFrm.cpp, needs both view class headers (and the prerequisite document header), as shown here:
#include "PoemDoc.h"
#include "StringView.h"
#include "HexView.h"
Listing 22.
AppWizard generates dynamic splitter code in the OnCreateClient() function, so you'll have to do some editing if you want a static splitter. Instead of calling CSplitterWnd::Create, you'll call the CSplitterWnd::CreateStatic function, which is tailored for multiple view classes. The following calls to CSplitterWnd::CreateView attach the two view classes. As the second and third CreateStatic() parameters (2, 1) dictate, this splitter window contains only two panes. The horizontal split is initially 100 device units from the top of the window. The top pane is the string view; the bottom pane is the hex dump view. The user can change the splitter bar position but the view configuration cannot be changed.
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
VERIFY(m_wndSplitter.CreateStatic(this, 2, 1));
VERIFY(m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CStringView), CSize(100, 100), pContext));
VERIFY(m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CHexView), CSize(100, 100), pContext));
return TRUE;
}
Listing 23.
Testing the MYMFC21B Application
When you start the MYMFC21B application, the window should look like the one shown below. Notice the separate horizontal scroll bars for the two views.
Figure 25: MYMFC21B program output with horizontal split views.
Continue on next module...part 3.
Further reading and digging:
MSDN What's New (MFC Feature Pack) - feature pack.
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multi-byte character set: Story and program examples.