
Introduction
The idea of this project came to me when I tried to implement a project using a large amount of splitter views. I wanted to make my splitter views generic but the problem is the impossibility of passing arguments to the constructor of a class when using dynamic creation. More precisely, in my context, I am using the CSplitterWnd::CreateView() to create and replace views. This method uses the runtime class of a view, to dynamically instantiate a view object and thus one cannot pass an argument to its constructor. This prevents from implementing a generic splitter class creating a splitter view with views (or runtime classes of views) passed as arguments to its constructor. To overcome the problem, I used templates instead.
Using the code
Before explaining the code, I would like to point out that I have reduced it to the working minimum. This sample is a showcase for using templates and not a finished solution. CMySplitterView, declared in mysplitterview.h, is the main template class responsible for creating a splitter view with 2 views. The template arguments TView1 and TView2 are the views to show in the splitter view, while TSplitterType is the type of splitter view created.
template<class TView1, class TView2, class TSplitterType>
class CMySplitterView : public CView
The type defines the layout of the splitter view: horizontal or vertical. Two policy classes define the behaviour:
class CHSplitterType : public CObject
class CVSplitterType : public CObject
Each of these classes define the method CreateSplitterView() called by CMySplitterView::OnCreate() to create the splitter view with the specific layout. Moreover they define OnSize() called by CMySplitterView::OnSize() to set the default size of the views. This part of the code could be very much improved. For example, the views used by the template splitter class could expose a method called by OnSize() to get their desired default size.
Now, to make this work one will need to define the splitter classes used within the code. In the sample, I have used two splitter views: one for the right horizontal splitter view and one for the global vertical splitter view (all of the views are CSplitterView).
template class CMySplitterView<CSplitterView, CSplitterView, CHSplitterType>;
template class CMySplitterView<CSplitterView, tclRightView, CVSplitterType>;
Here tclRightView is a type definition for the right horizontal splitter view CMySplitterView<CSplitterView, CSplitterView, CHSplitterType>.
For the creation of the main view, I pass tclDefaultView, a type definition for CMySplitterView<CSplitterView, tclRightView, CVSplitterType>, to the constructor CSingleDocTemplate(). Using the type definitions, one can imagine any possible combination of 2-view splitter views.
Points of interest
Now comes a little tricky part of the sample. Of course you will need to use IMPLEMENT_DYNCREATE and BEGIN_MESSAGE_MAP for your splitter views. Microsoft provides macros for classes with one or two templates (IMPLEMENT_DYNCREATE_T, IMPLEMENT_DYNCREATE_T2 …). Unfortunately, these macros are buggy and there are no macros for classes with 3 templates. I have included them in mysplitterview.cpp: M_IMPLEMENT_DYNCREATE_T3 and M_BEGIN_MESSAGE_MAP_T3 and the helper macros M_IMPLEMENT_RUNTIMECLASS_T3 and M_RUNTIME_CLASS_T3. They work for classes with 3 templates. Here is how to use them:
M_IMPLEMENT_DYNCREATE_T3( CMySplitterView, CSplitterView,
CSplitterView, CHSplitterType, CView )
M_BEGIN_MESSAGE_MAP_T3( CMySplitterView, CSplitterView,
CSplitterView, CHSplitterType, CView )
ON_WM_SIZE()
ON_WM_CREATE()
END_MESSAGE_MAP()