Virtual Developer Workshop: Containerized Development with Docker
OverviewFor a true SDI application, it is oft necessary to have more than one view. In most cases, it is also necessary to have splitter windows containing other views. This sample can be used to implement multi-views in SDI application with or without splitter. Menu and accelerator table can be changed between views, help ID too.
About this sampleThis sample implement 7 views. A standalone CEditView as first view, a splitter window containing a CTreeView and a CListView, an other splitter window containing a CView and a CScrollView, a standalone CRichEditView and a standalone CFormView. The CRichEditView has a new menu with find, next and replace as sample for view context menu.
Problem creating multi-views and multi-splittersThe framework implement functions to create new views CFrameWnd::CreateView() and CSplitterWnd::CreateView(). CFrameWnd, uses the AFX_IDW_PANE_FIRST as default dialog ID for the new view. This ID is used in the RecalcLayout() and if the view is a standalone view, there is not problem because there is only one view active with this ID at a time. CSplitterWnd implement a function IdFromRowCol() to calculate a dialog ID started with AFX_IDW_PANE_FIRST until AFX_IDW_PANE_LAST. Because there is only 256 possible values for this ID, the splitter limits the view on a 16x16 matrix. If we want to create a second splitter with other views, CSplitterWnd::CreateView() fail because the dialog ID exists in the first splitter.
Solution to create multi-splittersThe simple solution for the splitter dialog ID problem is to change the dialog id of the new created views to zero after creation of all panes so we preserve the ASSERT message if the programmer try to create an other view at the same row/column position. To make consistency, we do the same for standalone Views.
Solution to display multi-views and multi-splittersThe framework implements a function to activate views CFrameWnd::SetActiveView() but not to show or hide other views or splitters. If we want to activate a new view, we must first hide the actual splitter and views. To preserve the dialog ID architecture, we must set the dialog ID of hidden splitters and views to zero and the dialog ID of the active splitter and views to the correct ID i.e. IdFromRowCol() for views in splitter and AFX_IDW_PANE_FIRST for standalone view.
ImplementationTo create new views, it is only necessary to add doc template to the application, but the resource string must be cleared i.e. containing only the window title "ViewTitle\n\n\n\n\n\n". To implement this architecture automatically, I use a new CSDIDocTemplate class derived from CSingleDocTemplate containing splitter information (splitter #, row, column). The default constructor set the splitter # to -1 so to implement a standalone view, we do exactly the same as for a CSingleDocTemplate. For splitter views, we must give a splitter # different of -1, the row and column. The new CSDIFrameWnd class derived from CFrameWnd creates the splitters and views in his CSDIFrameWnd::OnCreateClient() member automatically and store a view pointer in the doc template for later use. To do this, it uses a new CSDIWinApp class derived from CWinApp that implement three new little functions to access these doc templates with view runtime class or view pointer. The CSDIFrameWnd class has only two user functions. ActivateView(CRuntimeClass* pViewClass) switch between views and IsViewActive(CRuntimeClass* pViewClass) to update the user interface.
Menu, Accelerator and Help IDIf you give new menu and accelerator table for the resource IDs in SDIDocTemplate menu and accelerator table will be changed. The help ID for the view will be changed according to the resource ID.
LimitationThe number of views or splitters is only limited by memory. Because the two user functions in CSDIFrameWnd use runtime class, it is not possible to have more than one view of the same class but normally, it is not a problem. With little change, you can implement a view ID in the CSDIDocTemplate and use it to switch between views.
Step by StepTo create an SDI application with multiple views and splitters, do this.
- Add SDIApp.* and SDIFrame.* to your project.
- Change the base class CWinApp to CSDIWinApp.
- Change the base class CFrameWin to CSDIFrameWnd.
- Use CSDIDocTemplate instead of CSingleDocTemplate.
- Create new views with ClassWizard.
- Create new resource ID string with new window title, but you can use the same for all new views.
- Create new menu with this resource ID if necessary.
- Add new CSDIDocTemplate in InitInstance() with resource ID, view and splitter number and position.
- In the CMainFrame, add message command to switch between views.
Date Last Updated: February 14, 1999