Objective Grid : PART II Programmer’s Guide : Chapter 15 Advanced Concepts : Sharing Extended Functionality Across Classes
Sharing Extended Functionality Across Classes
To have common source that can be shared between CWnd-derived grids and CView-derived grids, we recommend using the template approach. This approach is used in the Objective Grid Designer source. An explanation of the template approach follows.
Shared Source
It is often beneficial to have the same code body for a view and a window. This can typically be done by isolating all the code that goes into implementing the control logic into a separate class and then deriving your window or view from this class. The Grid library uses this approach. It derives multiply from CWnd- or CView- based classes and CGXGridCore-based classes.
Figure 127 – Inheritance diagram for the Objective Grid Library
Considering that we implement a lot of functionality in our derived class, we can see one possible complication. Our implementation can extend either a window-based class or a view-based class at one time. In many cases, the code that we extend (add) will be common to both views and windows and can be put to good use with both.
To achieve this end, we used the handle view/window classes in earlier versions of the library. These worked on the principle of containment. They would contain a CGXGridCore-derived object and delegate to it. With increasingly better compiler support for templates, we decided that it was time to approach this problem with templates.
With templates, the inheritance hierarchy looks like this:
Figure 128 – Inheritance diagram when using templates for sharing code
Note that we can now extend the template class irrespective of the base class. Once the template class is complete, it can be used either as a window or a view by simply switching the template instantiation parameter. As you can see, this approach is very flexible.
There are some important issues that need to be taken care of before we can implement this approach with the MFC framework. The primary of these issues is with the message map. The default message maps cannot take template arguments. We changed these macros to accommodate message maps.
The code for an elementary template based window is shown below:
 
// Implementation of the template based grid
template<class T>
class TWnd:public T
{
// Do not use GRID_DECLARE_DYNCREATE
//GRID_DECLARE_DYNCREATE(TWnd)
protected:
//grid overrides:
virtual BOOL GetStyleRowCol(ROWCOL nRow, ROWCOL nCol,
CGXStyle& style, GXModifyType mt = gxCopy,
int nType = 0);
//implementation
//{{AFX_MSG(TWnd)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnAppAbout();
//}}AFX_MSG
GRID_DECLARE_MESSAGE_MAP()
};
 
///////////////////////////////////////////////////////////////////////
// CTemplateWindow
 
// special message map macro (ref macro.h)
CCEBEGIN_MESSAGE_MAP(TWnd, T)
//{{AFX_MSG_MAP(TWnd)
ON_WM_LBUTTONDOWN()
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
// you can use ClassWizard to add handlers
template<class T>
void TWnd<T>::OnLButtonDown(UINT nFlags, CPoint point)
{
T::OnLButtonDown(nFlags, point);
}
 
template<class T>
void TWnd<T>::OnAppAbout()
{
AfxMessageBox(_T("Template Window About\n"));
}
 
template<class T>
BOOL TWnd<T>::GetStyleRowCol(ROWCOL nRow, ROWCOL nCol, CGXStyle& style, GXModifyType mt, int nType)
{
BOOL b = T::GetStyleRowCol(nRow, nCol, style, mt, nType);
if(nType != -1 && nCol == 1 && nRow !=0)
{
style.SetValue(_T("Template grid!"));
}
else if(nType != -1 && nCol == 2 && nRow ==1)
{
style.SetValue(_T("Common validation code!"));
}
return b;
}
You will notice that, apart from the normal template syntax, the only change is with the message map. The syntax for the new message map is very simple; only the first line is changed. The logic is the same. Derived class maps to Base class.
 
\CCEBEGIN_MESSAGE_MAP(TWnd, T)
//{{AFX_MSG_MAP(TWnd)
Please take a look at the file gxmesmac.h if you are interested in the actual macros.
Now that you have the template-based common code in a template class you can instantiate your actual derived class without much effort:
 
class CTemplateView : public TWnd<CGXGridView>
{
protected: // create from serialization only
CTemplateView();
GRID_DECLARE_DYNCREATE(CTemplateView)
Now that the class that you are deriving from is a regular class, there is no need for the usage of any special macros in this derived class. The only exception is with the GRID_IMPLEMENT_DYNCREATE macro.
You will have to use this macro instead:
 
// Format
// <this class, template base class, ultimate base grid/window class>
CCEIMPLEMENT_DYNCREATE(CTemplateView, TWnd<CGXGridView>, CGXGridView)
The message maps (etc.) are the same as with the regular grid.
Please refer to the Template sample available from the Rogue Wave Web site, as explained in “Location of Sample Code” in the Getting Started part.