Stingray® Foundation : Chapter 8 Model View Controller : MVC Viewports
MVC Viewports
In a nutshell, a viewport is a visual component that observes and renders a model. The term “viewport” is used to avoid confusion with the MFC CView class. At a minimum, a viewport implements the IObserver, IVisual, IBounds2D, IEventRouter, and IVisualWindow interfaces. Viewports may also implement other interfaces such as ILogCoordinates, IZoom, and IVisualHost. Viewports can be lightweight, windowless objects, or they can be mixed in with window classes to create windowed objects. There is a great deal of flexibility in the way that viewport classes can be implemented.
CMvcViewport
The CMvcViewport template class is the base class for all viewports. An excerpt from the CMvcViewport declaration is shown in Example 66.
Example 66 – CMvcViewport declaration
template<typename _Visual, typename _Model, typename _Ctlr>
class CMvcViewport : public _Visual,
public IObserver,
public IEventRouterImpl
{
// Embedded types
public:
typedef CMvcViewport<_Visual, _Model, _Ctlr> ThisClass;
typedef _Visual VisualClass;
typedef _Model ModelClass;
typedef _Ctlr ControllerClass;
 
. . .
 
// Operations
public:
 
virtual BOOL Create(HWND hWndParent, LPRECT rc);
 
virtual ModelClass* GetModel() const;
virtual void SetModel(ModelClass* pModel);
 
virtual void SetController(ControllerClass* pController,
const bool bAutoDelCtlr = false)
virtual ControllerClass* GetController();
 
. . .
};
The first template parameter passed into the CMvcViewport template is the type of visual component the viewport will derive from, which can be any class that implements IVisual and IBounds2D. This includes classes such as CMvcVisualComponent, CMvcVisualPart, and CMvcLogicalPart, as well as any class derived from these classes. The CMvcViewport class takes a visual component and extends it to be a viewport. The functionality of the visual component class is inherited by the viewport. The code segment shown in Example 67 declares a viewport class derived from CMvcLogicalPart.
Example 67 – A viewport class derived from CMvcLogicalPart
class CMyViewport : public CMvcViewport<CMvcLogicalPartImpl,
CMyModel,
CMyController>
{
. . .
};
The CMyViewport class shown above inherits the capabilities of CMvcLogicalPart, such as a logical coordinate system and support for zooming. In other words, CMyViewport supports the ILogCoordinates and IZoom interfaces by virtue of the fact that it derives from CMvcLogicalPart.
Deriving from CMvcLogicalPart may be overkill if all you want is a simple, lightweight viewport that doesn’t require zooming and scrolling. In that case, deriving from CMvcVisualComponent is more appropriate. The code segment in Example 68 declares a viewport derived from CMvcVisualComponent. The CMySimpleViewport class is leaner than CMyViewport and supports only the basic interfaces required for a viewport.
Example 68 – Declaring a viewport derived from CMvcVisualComponent
class CMySimpleViewport : public CMvcViewport<CMvcVisualComponent,
CMyModel,
CMyController>
{
. . .
};
The CMvcViewport class also takes the type of model and type of controller as template parameters, which are used to declare type-safe functions for accessing the model and controller. CMvcViewport also declares the following embedded data types: ThisClass, VisualClass, ModelClass, and ControllerClass. The embedded typedefs are both a convenient short-hand and a way for code outside of the scope of the template to have knowledge of the data types used by an instance of the CMvcViewport template.
Associating Viewports with Windows
The CMvcViewport class contains several functions that require a window handle. The LPtoDP() and DPtoLP() conversion functions and the InvalidateRect() and ValidateRect() functions are the most notable. So the question is “how does a viewport get a window handle?” Some viewports are windowless and are contained within a window. Viewports can also be windowed, which means that they are windows. In either case, the CMvcViewport class accesses the viewport’s window handle through the IVisualWindow interface. The IVisualWindow interface is shown in Example 69 below.
Example 69 – IVisualWindow interface
struct __declspec(uuid("722E1FCB-034F-4030-A600-3140A9D23DB4"))
IVisualWindow : public IQueryGuid
{
virtual HWND GetWindowHandle() = 0;
};
Windowed viewport classes implement the GetWindowHandle() method by returning a handle to their own window. Windowless viewports store the handle of their parent window and return that handle in their implementation of GetWindowHandle(). The CMvcWindowlessViewport class provides an implementation of IVisualWindow for windowless viewports, shown in Example 70.
Example 70 – Implementation of IVisualWindow for windowless viewports
template <typename _Base>
class CMvcWindowlessViewport : public _Base, public IVisualWindow
{
public:
CMvcWindowlessViewport() :
m_hWnd(NULL)
{
}
 
protected:
HWND m_hWnd;
 
public:
BEGIN_GUID_MAP(CMvcWindowlessViewport<_Base>)
GUID_CHAIN_ENTRY(_Base)
GUID_ENTRY(IVisualWindow)
END_GUID_MAP
 
virtual BOOL Create(HWND hWndParent, LPRECT rc)
{
m_hWnd = hWndParent;
return _Base::Create(hWndParent, rc);
}
 
virtual HWND GetWindowHandle()
{
return m_hWnd;
}
};
MVC also provides several windowed viewport classes, which mix-in a window class with CMvcViewport. The window viewport classes are framework-specific (either ATL or MFC) and are discussed later in this section.
Getting a Device Context
CMvcViewport declares an embedded class called DC, which derived from the class CDC. The CMvcViewport::DC class is a convenient way to get a device context for the window associated with the viewport. The DC class gets the handle of the window that contains the viewport and passes it to the Windows API GetDC() function, which returns a device context for the window. The DC class can then optionally call the OnPrepareDC() function of the viewport to initialize the device context with the appropriate settings for the viewport.
If you are using SFL with MFC support disabled, then the CDC class is defined by the SFL Graphics package as CGraphicsContext. In other words, CDC is typedefed as CGraphicsContext. The CGraphicsContext class is a compatible replacement for MFC’s CDC class. Refer to Chapter 10, “GDI Classes,” for more information about the CGraphicsContext class. If MFC support is enabled, then MFC’s CDC class is used.
The code segment in Example 71 creates a DC object and uses it to clear the viewport. The Boolean flag passed to the constructor of the DC class indicates if the OnPrepareDC() function should be called. The first parameter passed to the DC class constructor is a pointer to the viewport, which is queried for the IVisualWindow interface in order to retrieve the window handle.
Example 71 – Clearing the viewport with a DC object
class CMyViewport : public CMvcWindowlessViewport<CMvcLogicalPartImpl,
CMyModel,
CMyController>
{
public:
void Clear()
{
CMyViewport::DC dc(this, TRUE);
CBrush brFill(RGB(255,255,255));
CRect rcFill(GetBounds());
dc.FillRect(&rcFill, &brFill)
}
};
Event Routing
A viewport routes events to its controller. The viewport is the point of contact with the window, which receives messages or events. These messages are forwarded onto the controller to be handled. In order to receive window messages, the viewport must hook itself into the message handling mechanism for the framework used (either ATL or MFC). For ATL, the CMessageMap class is used to plug viewports into ATL message maps. For MFC, the OnWndMsg() and OnCmdMsg() functions inherited from CWnd are overridden in order to capture the messages before they are sent to the message map. In either case, hooking into the message handling mechanism is fairly straightforward. A more detailed discussion of how the messages are intercepted by the viewport in ATL and MFC is provided later in this section.
In addition to providing a framework-specific mechanism for handling events, MVC also uses the SFL Events package in order to provide a framework neutral mechanism for handling events. The Events package provides an object-oriented approach to generating and handling events. Events are treated as objects that are handled by event listeners and routed by event routers. Encapsulating window messages in event objects provides a natural form of message cracking. The publish-subscribe relationship between event listeners and event routers is very flexible and provides a very dynamic approach to event routing. Please refer to Chapter 6, “Events Package,” for a more detailed discussion of the event-listener architecture.
Once the viewport receives a message from a window, it translates that message into an event object using an event factory. The event objects are then routed to the event listeners by calling the viewport’s RouteEvent() method. Recall that the CMvcViewport class mixes in the IEventRouterImpl class, which defines the RouteEvent() method. The viewport’s implementation of the RouteEvent() method passes the event to the controller, which gives its event listeners an opportunity to handle the event.
As mentioned previously, a framework-specific bridge class takes care of translating the window messages into events. Those classes usually take the form of a template wrapper or mix-in class that declares a virtual GetEventFactory() method. The bridge class handles the window message using the framework-specific mechanism and uses the event factory returned by the GetEventFactory() method to create an event object. Viewport classes can override the GetEventFactory() method and provide their own implementation of the event factory. This is particularly useful for filtering the messages received by the viewport.
Scrolling
Scrolling capabilities can be added to a viewport by decorating the viewport using the MvcScrollWrapper_T template class. In order to scroll a viewport, it must support a logical coordinate system by implementing the ILogCoordinates interface. The CMvcLogicalPart class implements the ILogCoordinates interfaces, so passing a CMvcLogicalPart derived class as the first parameter to CMvcViewport is an easy way to inherit an implementation of ILogCoordinates. The code excerpt in Example 72 shows the declaration of a viewport that supports ILogCoordinates and is capable of supporting scrolling.
Example 72 – Declaration of a viewport that supports scrolling
class CMyViewport : public CMvcViewport<CMvcLogicalPartImpl,
CMyModel,
CMyController>
{
. . .
};
Now, add scrolling by wrapping the viewport in the MvcScrollWrapper_T template as shown below.
 
typedef MvcScrollWrapper_T<CMyViewport> CmyScrollingViewport;
Refer to Visual Components section for more information about MvcScrollWrapper_T.
Zooming
The CMvcLogicalPart class implements the IZoom interface, so instantiating the CMvcViewport template with a CMvcLogicalPart derived class creates a viewport with zooming capabilities. Example 73 shows the declaration of a viewport that supports zooming.
Example 73 – Declaration of a viewport that supports zooming
class CMyViewport : public CMvcViewport<CMvcLogicalPartImpl,
CMyModel,
CMyController>
{
. . .
};
The viewport can be zoomed in and out using the SetMagnification(), IncreaseMagnification(), and DecreaseMagnification() methods inherited from IZoom.
Refer to Visual Components section for more information about CMvcLogicalPart.
ATL Specifics
To provide seamless integration with the ATL windowing classes, several ATL-specific viewport classes are provided.
CMvcAtlWndViewport
This template class mixes any CWindow derived class with a viewport. The template parameters are the viewport class and window class. The GetWindowHandle() method inherited from IVisualWindow is implemented by returning the m_hWnd member of CWindow. The CEventRouterMapWrapper class is mixed-in to provide an implementation of ProcessWindowMessage() method that translates messages into event objects and passes them to the RouteEvent() method. Refer to the Events section for more information about CEventRouterMapWrapper.
The following code segment, Example 74, declares a class that derives from CMvcAtlWndViewport.
Example 74 – Declaration of a class derived from CMvcAtlWndViewport
class CBullseyeViewport :
public CMvcAtlWndViewport<CBullseyeViewportBase,
CWindowImpl< CBullseyeViewport > >
{
. . .
};
CMvcClientViewport
This template class mixes SFL’s CClientWindowImpl class with a viewport. It is less generic than the CMvcAtlWndViewport class since it mixes in a specific type of window. Like the CMvcAtlWndViewport class, it implements the IVisualWindow interface and takes care of routing events to the controller. The first template parameter is the derived class and the second template parameter is the type of viewport. The code segment in Example 75 shows the declaration of an MVC client window.
Example 75 – Declaration of an MVC client window
class CMyViewClientWnd : public CMvcClientViewport
<CMyViewClientWnd,
CMyViewport> >
{
. . .
}
 
MFC Specifics
To provide seamless integration with the MFC windowing classes, several MFC-specific viewport classes are provided.
MvcViewport
The MvcViewport class is an MFC-specific implementation of a windowless viewport. This name of this class is inconsistent with the SFL naming conventions for historical reasons. In previous versions of the Stingray MVC library, the MvcViewport class was the base class for all viewports, and it was part of a deep inheritance hierarchy which consisted of the following classes:
MvcVisualComponent<-MvcVisualPart<-MvcLogicalPart<-MvcViewport
The CMvcViewport template class flattens the hierarchy so that viewports can derive from any visual component class. The MvcViewport class is derived from CMvcViewport and passes in MvcLogicalPart, MvcModel, and MvcController so that it is compatible with previous versions of MvcViewport. An excerpt from the declaration of MvcViewport is shown in Example 76.
Example 76 – MvcViewport class declaration
class MvcViewport :
public MvcViewport_T<MvcLogicalPart,MvcModel,MvcController>
{
. . .
};
 
The MvcViewport class implements a windowless viewport. It maintains a pointer to a CWnd object, which it uses to implement the IVisualWindow interface.
MvcScrollView_T
This is a template class that mixes the MFC CScrollView class with a viewport. The type of viewport is passed in as the template parameter. MvcScrollView_T takes care of synchronizing the logical origin and size of the viewport with the scroll bars provided by CScrollView. Example 77 shows an excerpt from the declaration of MvcScrollView_T.
Example 77 – MvcScrollView_T class declaration
template<class base_t>
class MvcScrollView_T : public CScrollView, public MvcWrapper_T<base_t>
MvcBufferedWrapper_T
The MvcBufferedWrapper_T template class provides back buffering for MVC viewports. It is a template class that takes the base viewport class as a template parameter. Using this wrapper class eliminates flicker when the viewport is rendered.