A Tabbed MDI Frame Window






2.95/5 (9 votes)
A tabbed frame window to show several views of a document in a single frame.

Introduction
In a recent project, I wanted to show the document data using different views. I didn't like the default MFC implementation, creating a new frame window for each view.
I headed to Code Project and found several tabbed window articles (Property Sheet View, A Multi Document Tabbed Interface, etc.) but none was exactly what I needed: one frame per document, and one tab per view. With a little help from Google, I found an old article at CodeGuru named "Tabbed Views" by Salvatore Mosaico. I took the original idea, removed the grouping feature, and created this class, adding support for another feature I needed: tool bars and status bars in the child frame.
Using the Code
- Derive a new class from
CTabbedMDIChildWnd
. If you do this by hand, remember to define the message map and dynamic creation. You can use the class wizard to do the dirty work for you.- Select a new MFC class:
- Choose
CMDIChildWnd
as the base class: - And replace all occurrences of
CMDIChildWnd
with our class. Do this both in the *.h and *.cpp files: - Don't forget to include the
CTabbedMDIChildWnd
header in the *.h.
#include "TabbedMDIChildWnd.h"
- Select a new MFC class:
- Include the header files of your views:
#include "FirstView.h" #include "SecondView.h"
- To tell the frame window which tabs to use, use the helper class
CTabbedMDIChildWnd::CTabItem
. This class is derived formTCITEM
, with the addition of aCRuntimeClass
to create the view. You can use the provided constructor as in the next example. - Override
OnCreateClient
. Inside the function, define the tabs, and call the base class:
BOOL CDemoChildWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // this is the heart of the class, each call to DefineView will // add a tab of the specified class DefineTab(CTabbedMDIChildWnd::CTabItem(_T("First View"), RUNTIME_CLASS(CFirstView))); DefineTab(CTabbedMDIChildWnd::CTabItem(_T("Second View"), RUNTIME_CLASS(CSecondView))); return CTabbedMDIChildWnd::OnCreateClient(lpcs,pContext); }
- Your class in now ready. It is a good time to compile the project!
- The next step is to change the MDI child frame inside the
InitInstance
function of the application:
// Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_TabbedFrameTYPE, RUNTIME_CLASS(CTabbedFrameDoc), RUNTIME_CLASS(CDemoChildWnd), // custom MDI child frame RUNTIME_CLASS(CTabbedFrameView)); AddDocTemplate(pDocTemplate);
Inside the Class
The class has a CTabCtrl
member and creates one tab for each view defined by a call to DefineTab
. It handles the TCN_SELCHANGE
and TCN_SELCHANGING
notify messages to hide and show the views as the tabs are selected.
It uses a std::vector
to keep track of the defined tabs using the runtime class of the different views. This implementation imposes a limitation, only one instance of a view class is supported. Calling DefineTab
several times using the same runtime class is not a good idea!
If you want to change the appearance of the tab control, you can customize CTabbedMDIChildWnd::OnCreateClient
to change the styles of the tab control, or make additional calls to add images, etc.
History
- 1.1 Fixed small memory leak (thanks to J.H.Park)
- 12th March, 2008: Updated downloads