The steps for adding SECTabWnd and SEC3DTabWnd are identical.
Add an SECTabWnd (orSEC3DTabWnd) data member to your CFrameWnd-derived class. For example:
SECTabWnd m_tabWnd; |
If your application is: | Then: |
SDI-based | Add the data member to your CMainFrame class. |
MDI-based | Add the data member to your CMDIChildWnd descendant. |
FDI-based | Add the data member to your SECFDIChildWnd descendant. |
Override the OnCreateClient() method of the frame class and call the tabbed window Create() method to create the tabbed window. For example:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) { BOOL rtn_val; lpcs; //UNUSED rtn_val = m_tabWnd.Create(this); ... } |
In the OnCreateClient() frame class method, add the views or any CWnd-derived objects to the tabbed window object using the AddTab() method.
For example, the following line of code creates a new instance of a CView-derivative and inserts it into the tabbed window.
m_tabWnd.AddTab(RUNTIME_CLASS(CDemoView1), "Tab One", pContext, nID); |
The next line inserts any pre-existing CWnd-derived object into the tabbed window.
m_tabWnd.AddTab(pWnd, "Tab Two"); |
As the last step in your OnCreateClient() override, activate and scroll into view the tab that you want to be selected initially. For example:
m_tabWnd.ActivateTab(0); m_tabWnd.ScrollToTab(0); |
Edit the dialog resource in the resource editor.
Add an arbitrary control (for example, an edit control or a static control). Position and size it where you want the tabbed window, and give the control a unique control identifier.
In the OnInitDialog() handler for the dialog, retrieve the rectangle for the control and window you just added. For example:
CWnd* pWnd = GetDlgItem(uiControlID); CEdit* pwndEdit=(CEdit*)pWnd; // Retrieve the previous window in the tab order // and the rectangle to use for the call to create // (in parent client-area coordinates). CWnd* pwndPrev = pwndEdit->GetWindow(GW_HWNDPREV); CRect rc; pWnd->GetWindowRect(&rc); this->ScreenToClient(&rc); // Now we no longer need the original window and can // safely destroy it. pWnd->DestroyWindow(); |
Now, call the tabbed window Create() method. Then, set the size, position, and tab order for the control.
if (m_tabWnd.Create(this)) { SetWindowPos(pwndPrev, rc.TopLeft().x, rc.TopLeft().y, rc.Width(), rc.Height(), 0); } |
In the call to SECTabWnd::Create(), leave off the TWS_FULLSCROLL or TWS_LEFTRIGHTSCROLL flags.
Ensure that the font used on the tabs is a True Type font, such Arial or Times New Roman. For more information, see Section 13.5.11, "To change the font of a tab."
In the call to SEC3DTabWnd::Create(), specify one of the following style flags:
TWS_TABS_ON_BOTTOM
TWS_TABS_ON_TOP
TWS_TABS_ON_LEFT
TWS_TABS_ON_RIGHT
For example:
rtn_val = m_tabWnd.Create(this, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | TWS_FULLSCROLL | TWS_TABS_ON_LEFT); |
The contained object for the tab must be CScrollView-derived. Override CWnd::GetScrollBarCtrl() for the contained window class. For example:
CScrollBar* CMyScrollView::GetScrollBarCtrl(int nBar) const { ASSERT(GetParent()-> IsKindOf(RUNTIME_CLASS(SECTabWnd))); return ((SECTabWnd*)GetParent())-> GetScrollBar(nBar); } |
For each tabbed window, pass WS_HSCROLL or WS_VSCROLL as required in the dwStyle parameter of the SECTabWndBase::SetScrollStyle() member function. For example:
m_tabWnd.SetScrollStyle(nTab, WS_HSCROLL | WS_VSCROLL); |
Derive a class from SECTabWnd/SEC3DtabWnd and add handlers for the WM_CHAR and WM_KEYDOWN messages.
In OnChar() or OnKeyDown() or both, provide your own tab-activation code. Within the handler, you can use the SECTabWndBase::ActivateTab() method to activate a specific tab.
If the tab contains CView derived class objects:
The default message routing mechanism sends the keyboard messages to the window with the keyboard focus so the active view receives the messages.
Handle the message(s) in the view/control class contained in the tab to redirect the message(s) to the tabbed window. This ensures that your tab-window's handler is called.
void CDemoView1::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { nChar; //UNUSED nRepCnt; //UNUSED nFlags; //UNUSED // Forward message to parent ASSERT(GetParent()-> IsKindOf(RUNTIME_CLASS(SECTabWndBase))); MSG msg = *GetCurrentMessage(); if (GetParent()) GetParent()->SendMessage( msg.message, msg.wParam, msg.lParam); } |
If the tabbed window contains CDialog/CFormView derived class objects:
When a CDialog/CFormView derived object is the active window, the application sends keyboard messages to the control within the dialog that has the focus by default. You need to subclass the control and redirect the keyboard messages to the tabbed window.
Call the overloaded SECTabWndBase::AddTab() method with the following declaration.
void AddTab(CWnd* pWnd, LPCTSTR lpszLabel, HICON hIcon = NULL); |
If the tab is removed at run time, don't forget to destroy the associated CWnd. See Section 13.5.9.
Call the overloaded SECTabWndBase::AddTab() method with the following declaration.
CWnd* AddTab(CRuntimeClass* pViewClass, LPCTSTR lpszLabel, CCreateContext* pContext = NULL, HICON hIcon = NULL, UINT nID = -1); |
If the tab creation is within CFrameWnd::OnCreateClient():
Use the pContext parameter passed to the OnCreateClient() method in the AddTab() method call.
If the tab creation is not occurring within CFrameWnd::OnCreateClient():
Create a CCreateContext on the stack and pass it to the AddTab() method call. For example:
void CMyFrameWnd::OnSheetNew() { CCreateContext context; context.m_pCurrentFrame = this; context.m_pCurrentDoc = GetDocToUse(); context.m_pNewViewClass = RUNTIME_CLASS(CMyView); context.m_pNewDocTemplate = GetDocTemplateToUse(); m_wndTab.AddTab(RUNTIME_CLASS(CMyView), &context); } |
The implementations of the GetDocToUse() and GetDocTemplateToUse() methods are application-specific.
In general, you can embed a tabbed window anywhere. This is not true if the tabbed window contains views. You cannot embed a view in a tabbed window that is, in turn, embedded in a control bar (docking window) or a dialog. However, you can embed a tabbed window containing views into a view contained in a frame using the docking views architecture.
If the tab is removed at run time, don't forget to destroy the associated CView. See Section 13.5.9.
Call the RemoveTab() method. Don't forget to destroy the contained CWnd. For example:
// Don't just delete the tab, destroy the associated // window too. if (m_tabWnd.GetTabInfo(nActiveTab, lpszLabel, bSelected, pActiveWnd, pExtra)) { pActiveWnd->ShowWindow(SW_HIDE); pActiveWnd->SendMessage(WM_CLOSE); } |
Call the GetTabInfo() method, which is defined as follows:
// Returns information about the tab with the supplied index. BOOL GetTabInfo(int nIndex, LPCTSTR& lpszLabel, BOOL& bSelected, CWnd*& pWnd, void*& pExtra); |
For example:
m_tabWnd.GetTabInfo(nActiveTab, lpszLabel, bSelected, pActiveWnd, pExtra); |
Call one of the following font accessor methods.
Font accessor method | Description |
Get/SetFontActiveTab() | Sets an active tab's current font to the specified font. |
Get/SetFontInactiveTab() | Sets an inactive tab's current font to the specified font. |
If you are specifying the font for a tab on a 3D tabbed window and the tabs are placed on either the left or right side of the tabbed window, the font must be a True Type font.
Add a message-map entry and message-handler member function to the parent class for each message. See Section 13.4. Each message-map macro entry takes the following form:
ON_MESSAGE( <tab control message id>, memberFxn ) |
memberFxn is the name of the parent member function you wrote to handle the notification and <tab control message id> is one of the following:
TCM_TABSEL
TCM_TABDBLCLK
TCM_TABSELCLR
TCM_TABREACTIVATE
The parent's function prototype is as follows:
afx_msg LRESULT memberFxn(WPARAM wParam, LPARAM lParam); |
wParam parameter contains the index of the activated tab. If this handler is called in response to a SelectTab() event, then the wParam is the index of the currently active tab.
Use the following code:
pTabWnd = (SECTabWnd*)pView->GetParent(); ASSERT_KINDOF(SECTabWnd, pTabWnd); |
Create the tabbed window.
m_tabWnd.Create(this); |
Create the splitter window. Specify the tabbed window as its parent.
m_wndSplitter.CreateStatic(&m_tabWnd, 1, 2); |
Insert your child windows into the splitter window.
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CDemoView2), CSize(225,100), pContext); m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CDemoView2), CSize(225,100), pContext); |
Insert the splitter window into the tabbed window.
m_tabWnd.AddTab(&m_wndSplitter, "Tab Two"); |
If you are using a tabbed window in conjunction with the docking views component of Objective Toolkit, you may experience a problem with view activation. SECTabWndBase::Create() asserts if the tab window is being added as a child of a docking view that already has an ID of AFX_ID_PANE_FIRST. A tab window is expected to have this ID if the tab window consumes the client area of an MDI child frame. In other words, it is to act as a container for one or more views that would normally otherwise have this ID. The same is true if the tab window consumes the client area in the SDI scenario.
When calculating the layout of the client area, MFC looks for a window with an ID of AFX_ID_PANE_FIRST. In a MDI application, the view window is typically the one with this ID. If multiple windows with this ID exist, view activation behavior becomes erratic.
To fix this problem, create a unique ID for the tab window and specify it during the call to Create(). For example:
VERIFY (m_wndTab.Create(this, WS_CHILD|WS_VISIBLE|TWS_TABS_ON_BOTTOM, ID_MYTAB)); // some unique id |
Copyright © Rogue Wave Software, Inc. All Rights Reserved.
The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.