📄 tabbed_views2.shtml.htm
字号:
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="Author" CONTENT="Zafir Anjum">
<TITLE>Doc/View - Tabbed Views (2)</TITLE>
</HEAD>
<body background="../fancyhome/back.gif" tppabs="http://www.codeguru.com/fancyhome/back.gif" bgcolor="#FFFFFF" link="#B50029" vlink="#8E2323" alink="#FF0000">
<table WIDTH="100%">
<tr WIDTH="100%">
<td><A HREF="http://209.66.99.126/cgi/ads.cgi?advert=myad2"><IMG SRC="../../209.66.99.126/advertise2.gif" tppabs="http://209.66.99.126/advertise2.gif" ALT="" BORDER=2></A><td>
</tr>
</table>
<CENTER>
<H3>
<FONT COLOR="#AOAO99">Tabbed Views (2)</FONT></H3></CENTER>
<HR>
This sample was contributed by <A HREF="mailto:mailto:acp107@psu.edu">Alger Pike</A>.
<P><IMG SRC="tabbed_views2.gif" tppabs="http://www.codeguru.com/doc_view/tabbed_views2.gif" BORDER=1>
<P>Many times in an SDI interface the programmer would like to have many
views of the same document. Look at the MFC sample collect.
It consists of an SDI app which has nine different views for each of the
data types in its document. The view is switched by selecting a menu
item. There are several other different ways this user interface
could be organized and presented to the user. One
would be by presenting a TabCtrl to the user and have a Tab for each
view. This is the approach I wanted to take in my own SDI application.
Following then will be the code for CTabCtrlView, a view which replaces
the standard SDI client window and
handles the displaying of the multiple view. An AVI of the control
follows:
<BR><P>As an example of this control I will describe how to modify the MFC
collect sample so it uses my new control as its interface. The first
thing you must do is derive your own class from CTabCtrlView. There
are several virtual functions you must override. This allows you
to add your own application specific Tab items and Send Message Commands.
The first function to override is InitTabs. This function is responsible
for inserting the Tabs into the TabCtrl. Proceed as you would for
any CTabCtrl object.:
<TT><FONT COLOR="#990000">
<PRE>void CMyTabView::InitTabs(CTabCtrlView* pView)
{
TC_ITEM TabCtrlItem;
TabCtrlItem.mask = TCIF_TEXT | TCIF_IMAGE;
TabCtrlItem.pszText = "CStringList";
TabCtrlItem.iImage = 1;
m_TabCtl.InsertItem( 0, &TabCtrlItem );
TabCtrlItem.pszText = "CTypedPtrList";
m_TabCtl.InsertItem( 1, &TabCtrlItem );
TabCtrlItem.pszText = "CList";
m_TabCtl.InsertItem( 2, &TabCtrlItem );
TabCtrlItem.pszText = "CDWordArray";
m_TabCtl.InsertItem( 3, &TabCtrlItem );
TabCtrlItem.pszText = "CTypedPtrArray";
m_TabCtl.InsertItem( 4, &TabCtrlItem );
TabCtrlItem.pszText = "CArray";
m_TabCtl.InsertItem( 5, &TabCtrlItem );
TabCtrlItem.pszText = "CMapStringToString";
m_TabCtl.InsertItem( 6, &TabCtrlItem );
TabCtrlItem.pszText = "CTypedPtrMap";
m_TabCtl.InsertItem( 7, &TabCtrlItem );
TabCtrlItem.pszText = "CMap";
m_TabCtl.InsertItem( 8, &TabCtrlItem );</PRE>
<PRE> //Important You must always call the base class
CTabCtrlView::InitTabs(pView);
return;
}</PRE>
</FONT></TT>
The next step is to override the function HandleTabs(). This function
will send a message back to the main window for each tab that needs processing.
The way MFC is written it is the job of the main frame to handle switching
view and assigning the view. Instead of reinventing the wheel I decided
it would be easier to send a message to the main frame and allow it to
handle the changing of the views. The HandleTabs for the new MFC collect
should look like this:
<TT><FONT COLOR="#990000">
<PRE>BOOL CMyTabView::HandleTabs(int sel)
{
//The MFC doc/view model expects the
//mainFrame to handle changing view
//so we let the mainframe do it.
CWnd* Wnd = AfxGetMainWnd();
switch(sel)
{
case 0:
Wnd->SendMessage(WM_COMMAND, ID_STRINGLIST, 0);
break;
case 1:
Wnd->SendMessage(WM_COMMAND, ID_TYPEDLIST, 0);
break;
case 2:
Wnd->SendMessage(WM_COMMAND, ID_INTLIST, 0);
break;
case 3:
Wnd->SendMessage(WM_COMMAND, ID_DWORDARRAY, 0);
break;
case 4:
Wnd->SendMessage(WM_COMMAND, ID_TYPEDPTRARRAY, 0);
break;
case 5:
Wnd->SendMessage(WM_COMMAND, ID_POINTARRAY, 0);
break;
case 6:
Wnd->SendMessage(WM_COMMAND, ID_MAPSTRINGTOSTRING, 0);
break;
case 7:
Wnd->SendMessage(WM_COMMAND, ID_TYPEDPTRMAP, 0);
break;
case 8:
Wnd->SendMessage(WM_COMMAND, ID_MAPDWORDTOMYSTRUCT, 0);
break;
default:
ASSERT(0);
return FALSE;
}
return TRUE;
}</PRE>
</FONT></TT>
As you notice each Tab sends the main frame the corresponding COMMAND message
that the menu would have if the user would have selected a menu item.
This implementation allows you to be redundant by using tabs or the menu
to change the view.
<P>There needs to be a couple of changes to the main frame window code
as well. The first thing is to make an protected object from your derived
class. Now that the object exists we need to tell the main frame that this
window will be its client window. Override the OnCreateClient function
and change the function to look like this:
<TT><FONT COLOR="#990000">
<PRE>BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
if(!m_tabView.CreateStatic(this))
{
TRACE0("Failed to CreatePropertySheet\n");
return FALSE;
}
CRuntimeClass* pNewViewClass = RUNTIME_CLASS(CStringListView);
m_tabView.SetTab(0);
CSize size(160, 180);
if (!m_tabView.CreateView(pNewViewClass, size, pContext))
{
TRACE0("Failed to create first pane\n");
return FALSE;
}
SetActiveView(m_tabView.GetActiveView());
return TRUE;
}</PRE>
</FONT></TT>
<P>Now when the application creates the main client window your CTabCtrlView
object should appear as the client window for your application. The final
step is to change the existing OnExample function in the main frame window.
We need to tell it that when it creates a view it should call CTabCtrl's
Create View and not the one for the main Frame. Also we call the TabCtrlView's
RecalcLayout function which will correctly position the new view in the
TabCtrl's window area.
<TT><FONT COLOR="#990000">
<PRE>void CMainFrame::OnExample(UINT nCmdID)
{
if (nCmdID == m_nCurrentExample)
return; // already selected
// Set the child window ID of the active view to AFX_IDW_PANE_FIRST.
// This is necessary so that CFrameWnd::RecalcLayout will allocate
// this "first pane" to that portion of the frame window's client
// area not allocated to control bars. Set the child ID of
// the previously active view to some other ID; we will use the
// command ID as the child ID.
CView* pOldActiveView = GetActiveView();
::SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, m_nCurrentExample);
CRuntimeClass* pNewViewClass;
switch (nCmdID)
{
case ID_STRINGLIST:
pNewViewClass = RUNTIME_CLASS(CStringListView);
break;
case ID_TYPEDLIST:
pNewViewClass = RUNTIME_CLASS(CTypedPtrListView);
break;
case ID_INTLIST:
pNewViewClass = RUNTIME_CLASS(CIntListView);
break;
case ID_DWORDARRAY:
pNewViewClass = RUNTIME_CLASS(CDWordArrayView);
break;
case ID_TYPEDPTRARRAY:
pNewViewClass = RUNTIME_CLASS(CTypedPtrArrayView);
break;
case ID_POINTARRAY:
pNewViewClass = RUNTIME_CLASS(CPointArrayView);
break;
case ID_MAPSTRINGTOSTRING:
pNewViewClass = RUNTIME_CLASS(CMapStringToStringView);
break;
case ID_TYPEDPTRMAP:
pNewViewClass = RUNTIME_CLASS(CTypedPtrMapView);
break;
case ID_MAPDWORDTOMYSTRUCT:
pNewViewClass = RUNTIME_CLASS(CMapDWordToMyStructView);
break;
default:
ASSERT(0);
return;
}
// create the new view
CCreateContext context;
context.m_pNewViewClass = pNewViewClass;
context.m_pCurrentDoc = GetActiveDocument();
// New Code below
CView* pNewView = m_tabView.CreateView(pNewViewClass, CSize(100,100), &context);
if (pNewView != NULL)
{
// the new view is there, but invisible and not active...
pNewView->ShowWindow(SW_SHOW);
pNewView->OnInitialUpdate();
SetActiveView(pNewView);
m_tabView.RecalcLayout(); //<-- New Code
RecalcLayout();
m_nCurrentExample = nCmdID;
// finally destroy the old view...
pOldActiveView->DestroyWindow();
}
}</PRE>
</FONT></TT>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -