Click here to Skip to main content
Click here to Skip to main content

Using views effectively without the doc/view overhead

, 14 Nov 2003
Rate this:
Please Sign up or sign in to vote.
An article on making the MFC framework do the work for you when creating views based on CView or its derivatives.

Introduction

Hi, and welcome to my very first article here at CodeProject. I have tried to make this article as clear and professional-looking as possible, but as always, typing and context errors or outright bugs may still remain. If you happen to stumble upon one, feel free to contact me or leave a note on the comments section.

Now, the article itself discusses, like its topic states, about using different types of view classes without the doc/view framework. In a nutshell, we will be creating a standard frame window application, and force it to use a custom view class. To add an extra degree of difficulty, we will use a CFormView-derived class.

Background

Anyone who has worked with CFormView or similar view classes know how irritating it is that their constructors and Create-member methods are stashed into a private section of the class' declaration. And to make things even worse, it is impossible to alter this without hacking the MFC source files. Most inconvenient..

A while ago, I was working on a project which started as a normal dialog-based solution. Further down the road, it became apparent that the application needs a toolbar and a menu. Well, adding those to a dialog window proved out to be quite difficult, so I took a different type of approach: changed my dialog to a CFormView-derived class. After a few more hours of debugging, I got the code to compile ok, but oh what a hell broke loose when I tried to run it... Asserts, access violations, unhandled exceptions, you name it..

Making MFC framework do the hard job

So let's quit the rambling, and get to work. First, you need to have a standard MFC SDI application, so fire up your IDE and create one. It doesn't need to be special in any way, if you have Visual Studio, just use its wizards to create it. Remember, however, to uncheck the doc/view architecture check-box!

As I am a Visual Studio user myself, all my examples from now on will conform to it. If you use a different IDE, follow its procedures to accomplish the same. Once your application skeleton is created, throw away the pregenerated child view class' source files. We won't be needing those. Instead, create a new formview (dialog) resource and place one or two controls into it. They can be any controls you please. Visual Studio has a template for forms from version 5.0 onwards, and you can access it by clicking the small plus sign next to the 'dialog' when inserting a new resource.

When done, create a new class for your dialog, specifying CFormView as the base class. Name your derived class CMainView, for example. Of course, you can use any name you think is suitable. Just remember to alter the example codes I present to match your selection.

Open up your newly created class, and change its constructors to public type.

class CMainView : public CFormView
{
    DECLARE_DYNCREATE(CMainView)

// Change constructor to public. It is private by default
public:
    CMainView();           
    virtual ~CMainView();

    ...

After doing that, surf up to your main frame window class (MainFrm.h by default), and replace the original child view include command from its top. Put a command to include our view class' header instead. Browse a bit more down and add a member variable to the class, called CMainView* m_pMainView. While at it, you can remove the old child view variable. Just remember, that the App Wizard generated code uses this variable in the OnSetFocus and OnCmdMsg implementations of the frame window. You must alter these functions to use your view pointer instead.

Now open up the implementation file (MainFrm.cpp) and browse to its OnCreate handler. This handler is called when the frame window is created, and we promptly alter it to do something else instead. Alter the handler to represent the following:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    // First, build the view context structure
    CCreateContext ccx;

    // Designate the class from which to build the view
    ccx.m_pNewViewClass = RUNTIME_CLASS(CMainView);

First off, you can see that we create a new context structure CCreateContext ccx. Basically, this structure, as part of the framework, is responsible for dictating the view and the document used in this context. The framework creates these automatically when you register a document template and order it to create a new view for you.

In the next line, we designate our view class to work as the base of the new view to create. You could use any CView-derived class here, as long as it is declared to support dynamic creation. If you use the Class Wizard to derive your classes from any CView or its derivate, the necessary steps for implementing a dynamic creation are done for you.

    // Using the structure, create a view
    m_pMainView = DYNAMIC_DOWNCAST( CMainView, this->CreateView(&ccx) );

    // Did we succeed ?
    if ( !m_pMainView )
    {
        TRACE0("Creation of view failed\n");
    }

Wait, wait, what's that horrible call ?! DYNAMIC_DOWNCAST blah blah ?

Calm down, take a deep breath. This call is actually the framework's method to creating views into a frame window. What we do here is to order the frame window (this -pointer) to call its member function CreateView and specify our freshly designed CCreateContext structure as its parameter.

The method returns a pointer to the created view's CWnd base class, if it is successful. If it didn't succeed, it returns NULL. Our next step in the call is to cast down from the CWnd base class all the way to our CMainView class. After all, CView is derived from CWnd, and CFormView is indirectly derived from CView. The cast is perfectly legal and good.

Additionally, you can specify a new ID for the view as the second parameter of CreateView. If you don't specify the ID, a default ID of AFX_IDW_PANE_FIRST is used.

Here is an alternate way to accomplish the same, if you feel unfamiliar with nested function calls:

    // Creating the child view step-by-step
    // First order the frame window to create a new view for us
    CWnd* pNewView = this->CreateView(&ccx);

    // Cast down from the returned pointer
    m_pMainView = DYNAMIC_DOWNCAST( CMainView, pNewView );

    // Succesfull ?
    if ( !m_pMainView )
    {
         TRACE0("Creation of view failed\n");
    }

Ok, let's move on. Now our view is created and bound to the frame window. However, if the frame window is using toolbars, status bars or rebars, it is possible that those bars are displayed incorrectly if we now make the view visible. So let's order the frame window to do a recalculation (repositioning) of its bars.

    // Do layout recalc
    RecalcLayout();

All that is left to do now is to show the view and make it active. The following code snippet uses the freshly created pointer to show it and order it to call its OnInitialUpdate handler. We also mark this view as active for the frame window.

    // Show the view and do an initial update
    m_pMainView->ShowWindow(SW_SHOW);
    m_pMainView->OnInitialUpdate();

    // Set this view active
    SetActiveView(m_pMainView);

If you went and ran your application now, the form wouldn't look too good, as the frame window does not get automatically resized based on the form's dimensions. Luckily, there is a function inside our view class that will order the frame window to resize its client area to accommodate space for it. Naturally, as the client area is resized, the entire window resizes as well.

    // Order it to resize the parent window's client area to fit
    m_pMainView->ResizeParentToFit(FALSE);

    // Done
    return 0;
}

That's it ! You can now build your application and it will give you a nifty little form instead of the boring white background.

Aftermath

This article covers only the basics of this type of framework exploitation.

Now, you might want to alter the formview's background to make it appear different, or you might remove some flags or settings to make it appear more smooth (the initial formview looks "sunked" into the frame window).

Remember, the the frame window in question can be any window that supports encapsulation of views, such as a CSplitterWnd. The calling convention of this approach is a bit different, though. Perhaps I will write another article to tackle this problem.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Antti Keskinen
Software Developer (Senior) Cybercom Finland Oy
Finland Finland
I live in the Frozen North, in Finland. Although the opportunities for good jobs or sunny days are scarce here, I still like it here. But at some point I'll move abroad.. Oh, and yes, there ARE polar bears in here Smile | :)
 
I graduated as a B.Sc in Computer Science in 2006, and currently I'm employed by Cybercom Finland Oy, a world-wide software house. I mainly develop Windows Desktop software using Qt, .Net Framework and other relevant technologies.
 
When I'm not knee-depth in the code, I play computer games, Go or go to the gym. I love music, in all it's forms, and dancing is close to my heart. Or then I spend time with my friends. I like friends. You can never have enough of them Smile | :)
 
I'm a keen player of games. Simulations and RPG are the key words. Old-time games such as Mega Man are good as well, not forgetting the block-buster hits either.

Comments and Discussions

 
GeneralRe: changing border style [modified] PinmemberAntti Keskinen28-Jan-07 0:05 
GeneralRe: changing border style Pinmemberlurkerlurks28-Jan-07 3:25 
GeneralRe: changing border style PinmemberAntti Keskinen28-Jan-07 9:28 
GeneralRemoving the frame Pinmemberedwardking21-Sep-06 12:25 
GeneralRe: Removing the frame PinmemberAntti Keskinen21-Sep-06 19:25 
GeneralRe: Removing the frame Pinmemberedwardking21-Sep-06 23:55 
GeneralRe: Removing the frame PinmemberAntti Keskinen22-Sep-06 11:41 
GeneralYour code was useful Pinmemberthannerulavanya24-Mar-06 0:07 
GeneralRe: Your code was useful PinmemberAntti Keskinen24-Mar-06 8:00 
QuestionOnInitialUpdate() called twice PinmemberYang Guang @ ECNU2-Feb-06 6:49 
AnswerRe: OnInitialUpdate() called twice PinmemberAntti Keskinen2-Feb-06 9:48 
QuestionUse with splitters? PinmemberAlternativett30-Jan-06 2:53 
AnswerRe: Use with splitters? PinmemberAntti Keskinen30-Jan-06 6:16 
GeneralRe: Use with splitters? PinmemberAlternativett13-Feb-06 15:05 
QuestionHow to implement > 2 FormViews Pinmemberjimmy-jim13-Dec-05 12:33 
AnswerRe: How to implement > 2 FormViews PinmemberAntti Keskinen14-Dec-05 8:54 
QuestionHow to use CEditView Pinmemberemouse26-May-05 10:31 
AnswerRe: How to use CEditView PinmemberAntti Keskinen26-May-05 18:28 
GeneralRe: How to use CEditView Pinmemberemouse27-May-05 10:44 
GeneralNice -n- Simple Pinmembersmjones21-Mar-05 12:37 
GeneralRe: Nice -n- Simple PinmemberAntti Keskinen10-May-05 22:41 
Generalindeterminate Framesize at start Pinmembertensing22-Jul-04 2:33 
GeneralRe: indeterminate Framesize at start PinmemberAntti Keskinen17-Aug-04 9:38 
GeneralWell done! Pinmemberstokos20-Dec-03 13:26 
GeneralRe: Well done! PinmemberAntti Keskinen21-Dec-03 2:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 15 Nov 2003
Article Copyright 2003 by Antti Keskinen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid