Stingray® Foundation : Chapter 8 Model View Controller : MVC Controllers
MVC Controllers
An MVC controller is an object that receives events and translates them into actions on the model and viewport. A controller determines the behavior of an MVC component. The controller has a strongly typed relationship with the model so that it can call methods exposed by the model’s interface and execute commands against the model. The controller can also call methods on the viewport. One of the attractive features of the MVC architecture is that different controllers can be used with the same viewport and model. The behavior of an MVC component can be modified by swapping one controller for another.
CMvcController
The CMvcController template class provides a base class for controllers. It takes two template parameters: the type of model and the type of viewport. An excerpt from the declaration of CMvcController is shown in Example 78.
Example 78 – Declaration of CMvcController
template<typename _Model, typename _Viewport>
class CMvcController : public IEventRouter
{
public:
typedef _Model ModelClass;
typedef _Viewport ViewportClass;
. . .
};
 
Notice that CMvcController declares embedded types for the model and viewport, which provides a way for code outside of the scope of the class to have knowledge of the model and viewport types.
The CMvcController class implements the IEventRouter interface. Event listeners can be added to the controller using the AddListener() function. Event listeners can either be mixed into the controller class or aggregated into the controller.
The sample code shown in Example 79 below demonstrates an implementation for an SFL Scribble controller. This sample code also implements the drawing canvas as an MVC component.
Example 79 – Sample Code for an SFL Scribble controller
class CCanvasController : public CMvcController<CCanvasModel,
IVisual>,
public CCommandAdapter,
public CMouseAdapter,
public ISubjectImpl
{
public:
CCanvasController() :
m_pStroke(NULL),
m_nLineWidth(1),
m_crLineColor(RGB(0,0,0))
{
m_ptCur.x = m_ptCur.y = 0;
 
// Add the controller as an event listener, since it mixes
// in event listener interfaces.
AddListener(static_cast<CCommandAdapter*>(this));
// Add the aggregrated keyboard listener
AddListener(&m_kbdListener);
}
 
virtual bool OnLButtonDown(UINT nFlags, POINT pt)
{
OutputDebugString("Left button down\n");
m_pStroke = new CStroke(m_nLineWidth, m_crLineColor);
GetModel()->m_strokes.push_back(m_pStroke);
 
m_pStroke->m_pts.push_back(pt);
GetViewport()->Draw(NULL);
 
return true;
}
 
virtual bool OnLButtonUp(UINT nFlags, POINT pt)
{
m_pStroke = NULL;
GetViewport()->Draw(NULL);
return true;
}
 
virtual bool OnMouseMove(UINT nFlags, POINT pt)
{
m_ptCur = pt;
 
if (m_pStroke != NULL)
{
m_pStroke->m_pts.push_back(pt);
GetViewport()->Draw(NULL);
}
 
CMouseUpdateMsg* pMouseUpd = new CMouseUpdateMsg(pt);
pMouseUpd->AddRef();
UpdateAllObservers(NULL, pMouseUpd);
pMouseUpd->Release();
 
return true;
}
 
virtual bool OnLineWidth(UINT nID, int nNotifyCode)
{
CLineWidthDlg dlg(m_nLineWidth);
 
if (dlg.DoModal() == IDOK)
{
m_nLineWidth = dlg.m_nLineWidth;
}
 
return true;
}
 
virtual bool OnLineColor(UINT nID, int nNotifyCode)
{
CColorDialog dlg(m_crLineColor);
 
if (dlg.DoModal() == IDOK)
{
m_crLineColor = dlg.GetColor();
}
 
return true;
}
 
ULONG STDMETHODCALLTYPE AddRef()
{
return 1;
}
 
ULONG STDMETHODCALLTYPE Release()
{
return 1;
}
 
BEGIN_GUID_MAP(CCanvasController)
GUID_CHAIN_ENTRY(CCommandAdapter)
GUID_CHAIN_ENTRY(CMouseAdapter)
GUID_CHAIN_ENTRY(ISubjectImpl)
END_GUID_MAP
 
BEGIN_COMMAND_MAP(CCanvasController)
COMMAND_ENTRY(ID_LINEWIDTH, OnLineWidth)
COMMAND_ENTRY(ID_LINECOLOR, OnLineColor)
END_COMMAND_MAP
 
protected:
CStroke* m_pStroke;
int m_nLineWidth;
COLORREF m_crLineColor;
POINT m_ptCur;
 
// Aggregate the keyboard listener instead of mixing
// it into the controller
 
class CKeyboardTestListener : public CKeyboardAdapter
{
public:
virtual bool OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
TCHAR msg[40];
_stprintf(msg, _T("%c pressed"), nChar);
::MessageBox(NULL, msg, _T("Key pressed"), MB_OK);
return true;
}
};
 
CKeyboardTestListener m_kbdListener;
};
MFC Specifics
The MvcController class provides an MFC-specific implementation of a controller that is tightly integrated with MFC message maps. It is derived from the more generic CMvcController class and mixes in the MFC-specific SECWndPlugIn class. The SECWndPlugIn is derived from CWnd and provides a mechanism for receiving messages from a window. SECWndPlugIn does not actually create a window. Instead, it is assigned the handle to the window it is plugged into. The advantage to this approach is that the MFC Class Wizard can be used to add message handlers directly to the controller, since it is derived from CWnd and contains an MFC message map.
This class does not conform to the SFL naming conventions for historical reasons. In previous versions of the MVC library, the MvcController class was the base class for all controllers.