Table of Contents

Before reading this article, you'll need to read the introductory article, An Introduction to a Model-View-Controller Implementation for MFC, I posted last month. In it, I presented the underlying MVC Framework classes that integrate with the MFC Doc/View architecture, and discussed the basics of the Framework message handling and event architecture.

In this article, I plan to describe in detail how the Model ties into the MFC CDocument class, and how it is abstracted so that the actual data source is transparent to the application and the rest of the Framework.

To be honest, the Model abstraction wasn't present in the first article, rather XML specific extensions to the Framework were implemented. As I stated then, I attempt to generalize as much code as possible, though "sometimes, I don't see the generalities in the code that can be factored down to the lower levels". The Model abstraction is an example of code that I needed to write specifically before I could see how to generalize it. For those of you who are interested, you can compare the XmlMvc.dll code from the first article to the abstracted code that now exists as part of the MVC Framework in the SbjCore.dll. The current XmlMvc.dll contains only code that provides file IO and the creation, getting, and setting of XML elements and attributes. All other code has been factored down to the MVC Framework itself.

The source code provided with this article is contained in a VS2008 solution, SbjDev, with three projects. Although the three projects were present in the first article, the contents of the projects have changed dramatically due to the refactoring of the Model abstraction. Of course, the Shapes.exe sample application has been rewritten to take advantage of this refactoring.

I neglected to mention in the first article that the naming of the DLLs reflects their version. I use a convention that seems to be common among third-party vendors of MFC extensions:

<Project Name><Major/Minor Version><VC++ Major/Minor Version>.dll

Below, I list the current filenames that are produced by each of the projects:

Overview

The MVC Framework does not declare a class Model. Instead, the Model is considered to be the domain or application specific data that will be presented to the user of the application. The source of the data, whether it be a database, an XML document, or some other source, will typically be accompanied by a program or service for accessing that data. In the case of the XML Model provided with this article, it is the MSXML6 implementation of the Document Object Model (DOM).

The Framework declares an abstract interface through which it communicates in a common way with any given data source's access program. For each supported data source, a concrete implementation of the interface must be supplied. The MSXML6 DOM interface is implemented in the XmlMvc2090.dll accompanying this article.

Regardless of the concrete Model implementation, the Abstract Model provides two very important services.

Modification notification

Undo-Redo support

I'll discuss these further when discussing the implementation.

The Model Structure

The Framework identifies the basic concept of record/field, row/column, element/attribute as ModelItem/Attribute for no better reason than I was trying to be as generic as possible in my naming convention and not tie the names too closely to an existing Model implementation.

The Type of a ModelItem or Attribute is identified by a literal string. For instance, the type of a ModelItem might be:

"Person"

and a ModelItem of type "Person" might have an Attribute of:

"lastName"

A unique instance of a ModelItem is identified by a unique ModelItemHandle (again a generic term) which is implemented as, you guessed it, a HANDLE data type. The reason for this is that it causes no new dependency or coupling since it is part of the Windows API, and I can assume any concrete implementation of a Model will be able to cast whatever actual unique identifier it uses to a HANDLE.

The Model demands that ModelItems are hierarchical, so that any ModelItem may have child ModelItems associated with it. Even if the actual concrete Model contains only a list of ModelItems and none of those have children, the Abstract Model declares a ModelItemRoot from which all other ModelItems are descended. The value of the ModelItemRootHANDLE is defined as 0xFFFFFFFF. How the ModelItemRoot is interpreted in the concrete Model is dependent on the implementation. In the XML Model implementation, it obviously is interpreted as the XMLDocumentElement.

All of the components of the Abstract Model are contained in the namespace SbjCore::Mvc::Model.

Class Model::Controller

In the MVC Framework, the interface to the Model data is through the abstract base Model::Controller class. Unlike other Controller classes in the Framework, the Model::Controller class does not directly serve a Controlled CmdTarget or CWnd. Instead, it is a base for the Doc::Controller class described later in this article.

Model::Controller declares a set of private pure virtual data access methods for accessing and modifying the Model. These pure virtual methods are what must be implemented in a concrete Model::Controller derivative for a specific data source.

As you can see from the following listing, there is a lot more to the Model::Controller than just declaring the pure virtual methods. There is the static method GetItemRoot, which as discussed in the Overview, returns the hard coded ModelItemRootHANDLE. In addition, there is a method that returns an SbjCore::UndoRedo::Manager, methods for managing the currently selected ModelItems, and the public data access methods that call the corresponding private virtual methods implemented in the derived classes. I'll discuss each of these further, following the Model::Controller listing.

Currently Selected ModelItems

I think the concept of "currently selected" ModelItems is straightforward (i.e., a user clicks on a representation of an item or group of items in one of the Views). A vector is maintained by the Model::Controller, and is accessed through the following three methods by the various Framework Controller classes.

GetSelectedItems

SetSelectedItems

ClearSelectedItems

Data Access Methods

The function of most of the data access methods is fairly self evident; however, below is a list with a short description of their purpose. Of course, the actual implementation is provided by the derived classes through their corresponding virtual methods.

CreateItem - returns a HANDLE to an newly created ModelItem

InsertChild - inserts a child ModelItem

RemoveChild - removes a child ModelItem

GetItemAttrValue - gets an Attribute value

SetItemAttrValue - sets an Attribute value

AssureUniqueItemAttrValue - assures that an Attribute value is unique among its siblings

GetItemTypeName - returns a ModelItem's type

GetParentItem - returns the HANDLE of an ModelItem's parent

GetItemChildren - returns a list of child ModelItemHANDLEs

GetItemAttrNames - returns a list of Attribute names for a ModelItem

GetItemAttrName - returns an Attribute name by index

CookAttrName - provides a formatted version of an Attribute type name (e.g., lastName is returned as Last Name)

In addition to calling their corresponding pure virtual methods, each of the three data access methods that modify the Model provide optional Undo-Redo support. The three methods are:

InsertChild

RemoveChild

SetItemAttrValue

Whereas InsertChild and RemoveChild always fire events (as discussed in the first article) indicating changes to the Model, SetItemAttrValue supports the ability to optionally fire events. This allows multiple Attributes to be modified under the firing of one Event. Each of the methods follows the same general format, so rather than discuss each one separately, I'll use the SetItemAttrValue method for illustrative purposes, since it contains code for both optional supports. After outlining the implementation of the method, I'll go into more detail describing Undo-Redo support and Event Firing.

Before calling the corresponding pure virtual method OnSetItemAttrValue, SetItemAttrValue checks the value of the bAddToUndoRedo parameter, and if true, saves the current value of the named Attribute so it can be used to undo the Attribute value change. If the pure virtual returns true, and again based on the true value of bAddToUndoRedo, SetItemAttrValue declares and implements a derivative of the SbjCore::UndoRedo::Handler class. Notice that the UndoRedoHandler class calls SetItemAttrValue to implement the Undo and Redo functionality, except that here, the bAddToUndoRedo parameter is set to false. It then dynamically allocates an instance of the UndoRedoHandler class, and pushes it onto the undo stack maintained by the SbjCore::UndoRedo::Manager.

Finally, two Event classes are optionally fired; one indicating the specific change to the Model, and a second, indicating the general condition of the modification of the CDocument class, which by default calls CDocument::SetModifiedFlag with the value of the Doc::Events::DocModified parameter.

Undo-Redo Support

The Undo-Redo architecture is actually not part of the MVC Framework. Like the Event architecture described in the first article, it too is generic, and can be used without any Framework dependencies. The two components of the architecture, Manager and Handler, are declared under the namespace SbjCore::UndoRedo. I'll discuss the UndoRedo::Handler first since you've already been introduced to it in the last section.

Class UndoRedo::Handler

UndoRedo::Handler is the pure virtual base class from which each of the UndoRedoHandler classes defined in the Model::Controller data access methods is derived. The derivatives handle the actual details of performing any undo or redo of an action. Derivatives should make their destructor protected to force dynamic allocation, as these are pushed onto the UndoRedo::Manager's stack, and once there, the manager handles deletion once the UndoRedo::Handler is no longer needed as part of the Undo-Redo process.

Notice that the UndoRedo::Handler has a method for returning a HandlerName. This name is added to a list of handler descriptions maintained by the UndoRedo::Manager class, which can be queried to provide the user with a list of multiple actions to undo or redo.

Class UndoRedo::Manager

The UndoRedo::Manager class maintains two internal stacks, one for Undo and one for Redo. To provide Undo-Redo handling for an action or command, an UndoRedo::Handler derivative is allocated on the heap and pushed onto the Manager's undo stack with a call to Push. When a call to Manager::Undo is made, the UndoRedo::Handler is popped off the undo stack, its Undo method is executed, and the UndoRedo::Handler is pushed onto the redo stack. If a subsequent call to Redo is made, the Handler is popped off the redo stack and its Redo method is executed, and the UndoRedo::Handler is returned to the undo stack. If a new call to Push is made, the redo stack is cleared.

As mentioned in the discussion of UndoRedo::Handler, a list of descriptions of the Handler objects on each stack can be retrieved through calls to Manager::GetUndoList or Manager::GetRedoList. The MFC Feature Pack CMFCRibbonUndoButton class takes advantage of this through its dropdown listbox, providing a method for users to process more than one action at a time. Complementing this, the Manager::Undo and Manager::Redo methods can be passed a count of actions to process. Methods are provided for attaching instances of CMFCRibbonUndoButton for both undo and redo. More on the CMFCRibbonUndoButton implementation later.

The Model::Controller instantiates an instance of a SbjCore::UndoRedo::Manager which is accessed through the following method:

The UndoRedo::Manager is accessed by CmdMsgHandler classes for ID_EDIT_UNDO and ID_EDIT_REDO. The Model::ControllerImpl contains instances of these CmdMsgHandler classes, and they are attached to the Model::Controller in its constructor. Each CmdMsgHandler is essentially identical, the differences being which UndoRedo::Manager stack is accessed. For brevity's sake, I'll only list the CmdMsgHandler for ID_EDIT_UNDO.

Accessing the UndoRedo::Manager for the assigned CMFCRibbonUndoButton, the OnUndoHandler gets the number of actions the user has chosen to undo, and calls the UndoRedo::Manager object's Undo method. Similarly, in the OnHandleCmdUI method, it queries the UndoRedo::Manager as to the enable state.

There's one more player in the Undo-Redo architecture, and that's the Ribbon::UndoRedoMenuHandler. It is attached to the SbjCore::Mvc::FrameWndExController class which acts as Controller for the Shapes application's Controlled CMainFrame. Below is the implementation of its OnHandleWndMsg method.

The UndoRedoMenuHandler handles the AFX_WM_ON_BEFORE_SHOW_RIBBON_ITEM_MENU registered Windows message when the user clicks on the dropdown arrow portion of the button. Once called, it queries the CMFCRibbonBaseElement for the message ID, handling both the ID_EDIT_UNDO and ID_EDIT_REDO messages. Accessing the current Model::Controller, it gets the UndoRedo::Manager, assigns the button, and queries the UndoRedo::Manager for the list of UndoRedo::Handler descriptions, and fills its dropdown listbox. Once the user selects the number of actions to process, the appropriate ID_EDIT_UNDO or ID_EDIT_REDO CmdMsgHandler is called.

Event Firing Support

The namespace Model contains a number of predefined Event IDs and Event derivations for handling the notification of changes to the Model. An example was seen in the listing for the SetItemAttrValue method, earlier in the article. These are contained in the project location: SbjCore/Mvc/Model/ModelEvents.h. Below is a list of the available Model::Event IDs, along with a short description:

EVID_ITEM_INSERTING - fired by Model::Controller::InsertChild before an Item is inserted

EVID_ITEM_INSERTED - fired by Model::Controller::InsertChild after inserting an Item

EVID_ITEM_REMOVING - fired by Model::Controller::RemoveChild before an Item is removed

EVID_ITEM_REMOVED - fired by Model::Controller::RemoveChild after removing an Item

EVID_ITEM_CHANGING - fired by Model::Controller::SetItemAttrValue before an Item is changed

EVID_ITEM_CHANGED - fired by Model::Controller::SetItemAttrValue after changing an Item

EVID_SELITEM_CHANGED - fired by Model::Controller::SetSelectedItems when the Items selected have changed

I've never actually had an occasion to handle one of the events ending in "ING"; however, I can see where there might be a desire to short circuit one of these actions before it has actually been carried out. These events will be revisited in future articles when I discuss how they are handled by the various Views and Controls in the MVC Framework.

The first article discussed the concept of Controlled CmdTarget and CWnd classes. The Doc::ControlledDocument class derives from ControlledCmdTarget<CDocument>, and through its accompanying Model::Controller derived Doc::Controller class, provides the actual base for the concrete Model implementation. In the case of XML, the XmlMvc::XmlDoc::Controller class, which I will discuss in the next section.

You may have noticed that there was no mention of files when discussing the Abstract Model and its Model::Controller. Since files are something that are handled in MFC by the CDocTemplate and CDocument classes, it seemed more appropriate to introduce them in the Doc::Controller. This also implies that the Model can be used in other than file based situations.

In the MVC Framework, the actual creating, opening, and saving of files is delegated to the concrete Doc::Controller derived class. Passing the responsibility for these tasks to the Doc::Controller derivative is handled by Doc::ControlledDocument. It overrides the following CDocument methods:

and calls Doc::Controller pure virtual methods of the same name. Of course, the concrete Doc::Controller derivative implements these methods appropriately for the underlying Model it supports. If the virtual methods return true, the Doc::ControlledDocument takes care of notifying the observers by firing the appropriate events listed below.

I assume you are familiar with the structure of XML documents and the MSXML DOM interfaces. The relationships to the Abstract Model are as follows.

ModelItemRoot - a specific IXMLDOMElement interface to the root DocumentElement

ModelItem - an IXMLDOMElement interface

Attribute - an IXMLDOMAttribute interface

Before going into how the XmlDoc::Controller implements the data and file access methods, I want to discuss how the XmlDoc::Controller implements the ModeItemHANDLE.

ModelItem HANDLE Creation and Assignment

As far as I know, there is no natural unique identifier assigned to each element in an XML document, so the XmlDoc::Controller has manufactured one. It does this by injecting an Attribute with a unique value into each element when it is created or first accessed. In addition to uniquely identifying each element, the controller must keep track of the next available unique value. It does this by injecting an Attribute into the XML DocumentElement to contain this value. The two Attributes are named respectively, "sbjHandle" and "nextSbjHandle". I figure these names have little chance of colliding with any real Attribute names. The sample Shapes.xml listed below illustrates how the Attributes appear. The "nextSbjHandle" Attribute has an initial value of 0xF0000001; however, it is displayed in the file as a decimal value, as are the "sbjHandle" Attributes.

IXMLDOMElement to HANDLE and HANDLE to IXMLDOMElement

To implement the data and file access methods, the XmlDoc::Controller needs to be able to retrieve an IXMLDOMElement interface given its HANDLE, and retrieve a HANDLE to a given IXMLDOMElement. The two methods provided for this in the controller's ControllerImpl are listed below:

The GetHandleFromNode method queries the passed MSXML2::IXMLDOMElementPtr for its "sbjHandle" Attribute. If it is not found, the value of the "nextSbjHandle" Attribute from the DocumentElement is returned and is injected into the element, and finally the "nextSbjHandle" Attribute is bumped.

Retrieving the IXMLDOMElement from a HANDLE is a bit more complex. As you can see, normally, it is possible to use an XPath query on the document to find the element; however, when a new element is created, it is given a HANDLE. But, since it has yet to be inserted into the document, the XPath query will fail. As a matter of fact, the OnInsertChild method needs to retrieve the element from the HANDLE to actually insert it. To allow for this, the controller keeps a map of HANDLE to MSXML2::IXMLDOMElementPtr. I'm not sure if it wouldn't be better to put all the elements in the map, to avoid having the overhead of using the XPath query, but for now, I'm going to leave it as how it is. This may change in the future.

Implementing the Model::Controller Data and the Doc::Controller File Access Methods

I'm not going to go through every method, because most of them follow the same form; wrapping calls to the DOM, and marshaling between IXMLDOMElement and HANDLE. For illustrative purposes, I'll list the OnInsertChild method.

Note that the _com_error is caught. All of the smart pointer DOM routines throw it when an error is encountered. The catch (...) is mainly there as a development tool, to investigate any unexpected exceptions.

One other data access method is of interest, OnAssureUniqueItemAttrValue.

This method is used when an Attribute value is being presented to the user as a unique identifier. For instance, in the Shapes.exe application, when a new Rectangle is created, it has a default "label" Attribute of "New Rectangle". If the user creates a second Rectangle without changing the default "label" of the first, the method will deduce that "New Rectangle" is already in use and change the second Rectangle's "label" to "New Rectangle (2)".

At this point, I think you'll begin to see the true benefit of the MVC Framework. To apply the Model to the Shapes application takes only a few modifications to the original MFC AppWizard generated ShapesDoc class.

The first step is to control the CDocument derived ShapesDoc class. The following listing is of the ShapesDoc.h file. Note that modifications to the original are marked in bold.

You'll notice that the CDocument base class has been replaced by SbjCore::Mvc::ControlledDocument. As discussed in the first article, this allows an assigned Controller class first crack at any WM_COMMAND message sent to ShapesDoc. The typedef SbjCore::Mvc::ControlledDocument t_Base is just a convenience so references to the base class in the .cpp file are t_Base, and are automatically updated should the actual base class change. OnNewDocument and Serialize are removed (actually relegated to SbjCore::Mvc::ControlledDocument), and the private struct ShapesDocImpl* has been added. This private struct is a common way of hiding implementation details, and you'll see it in almost every SbjCore and XmlMvc class. As you'll see, in the next step when we replace the Shapes.cpp code, it also doubles as the Controller class for ShapesDoc.

Next, the ShapesDoc.cpp code must be modified. I'm going to show this in two steps: first the basic attachment to the XML Model implementation, and then some additional application specific code.

The struct ShapesDocImpl has been declared as a derivative of XmlMvc::XmlDoc::Controller. In their constructor, there is a call to XmlMvc::XmlDoc::Controller::SetDocElementName telling the controller what the DocumentElement type should be, in this case "Shapes". This is used to validate existing files, and to create the DocumentElement in new files. This is the only reference to the XmlMvc::XmlDoc::Controller in the application. All other references are to the underlying Model abstraction, SbjCore::Mvc::Model::Controller.

In the ShapesDoc class, code has been added to the constructor and destructor to create and delete the m_pImpl instance of struct ShapesDocImpl, and to assign the ShapesDoc constructor to be its Controller.

The second set of modifications is to the ShapesDocImpl Controller and the addition of CmdMsgHandler classes specific to the Shapes application.

Since the XML Model knows only that the DocumentElement type is "Shapes", and knows nothing of the rest of the Model content, a CmdMsgHandler has been added to handle requests for new ModelItems of type "Rectangle" and "Ellipse". It is then added to the ShapesDocImpl controller, once for each type. The second addition is the OnNewDocument override to create the ModelItem "Drawing" which is a container for all the "Rectangle" and "Ellipse" ModelItem children.

In this article, I have discussed the details of how the MVC Framework presents a Model abstraction to the application while hiding the data source. Of course, this article hasn't covered the application Views and how the MVC Framework supports them (the topic of future articles), but similar support exists which minimize the changes and additions required to the associated application classes. Although I have yet to cover these issues, the code accompanying the article contains all the code necessary for the current implementation of the Framework and the sample Shapes application. Once again, as I said in concluding the first article, run the Shapes application, explore the code, and please offer any feedback you'd like to contribute.

Share

About the Author

Real name is Steve Johnson. Programming since 1979. Started on a Heathkit Micro with a DEC LSI-11 and UCSD Pascal. Moved to PCs & DOS as soon as Turbo Pascal became available. Did some Assembly, ISR, TSR etc. All this while working for a Manufacturing Co. for 8 years. Had my own solo Co. doing barcode labeling software for 4 years (terrible business man, all I wanted to do was code). Since then working for various software companies. Moved to Windows around the time of 3.1 with Borland C then C++. Then on to VC++ and MFC, and just about anything I could get my hands on or had to learn for my job, and been at it ever since. Of course recently I've been playing with .NET, ASP, C#, WPF etc.

Comments and Discussions

Until today I didn't have access to an XP box without VS2008 and the Feature Pack installed, but after getting the program to run I find that it still has problems. Sorry to all who have tried in vane to get it to work correctly. Before I publish the next article in the series, I'll see if I can't come up with a resolution to the existing problems.

I've submitted an updated Binaries Zip file including a Windows installer msi file. This should install the necessary VC9 dlls along with the program. If you want to get the install quicker, send your email to sbj@sbjcat.com.
Thanks for your interest,
Steve

I hate to say it, but when I had that full beard, too many people told me I looked like Jerry Garcia. Maybe that could be it. I have since gone to a goatee and the comments have stopped. Seriously, I doubt we've met, I've lived in Marin County, California my entire adult life, and have been some what of a recluse in the software community. Anyway, I hope you enjoyed the article, there are more to come.

Well I couldn't run it, even after installing the link you posted above, it still complains about "ordinal 8580 could not be located in the dynamic link library mfc90.dll". I tried rebooting and still got the same message. This is Windows XP sp2.

¡El diablo está en mis pantalones! ¡Mire, mire!

Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!

Right now I am working with Feature Pack and wondering how I should implement property grid control. I have my implementation and it works, but I am not sure about the design. So I have been looking for some good tutorials (with some code ) about design pattern in MFC and maybe then I can use property grid control properly.

I am just thrilled about your work and thinking if I can adopt your approach to my project. Apparently I miss your introduction article but I will start from there.

Again, thank you for sharing your knowledge. I know that the universe is still on my side and it will send someone to teach me what I need to know.

Rome,
I'm glad you enjoyed the first two articles. It's nice to see someone who may actually use the Framework. And yes, the next article, The SBJ MVC Framework - The Views, Design, Explorer and PropertyGrid will go into detail describing how the three views respond to changes in the model. Hopefully in the next week or so I'll have a chance to complete it and post. I'll be sure to answer any questions you may have while you work with you application.

I was stepping through the code, this morning, from the second article to see how the views responded to events/messages. I'll be re-writing a small and "old" invoice application to see how the framework is constructed. Probably start at the end of next week. If everything seems to suite my needs, which at the moment seems to, I might use it in a much larger application. I'll let you know if I have questions as I go along.

To make it easier to ask questions, here is my email, sbj@sbjcat.com. Of course if you think the questions would serve to augment the article, continue to post and I will post answers to them. If you truly do intend to use it in a large application, perhaps we could collaborate. If you are interested, please email me.