使用過 WPS 2000 的開發者一定對其文件切換功能印象深刻。當同時開啟多個文件時,該功能能讓使用者快速切換到指定文件。本文將詳細說明如何在自己的 MDI 程式中加入這樣的標籤功能。下圖是本文範例程式的執行畫面。
圖一 範例程式執行畫面
這是一個 Tab 標籤控制項,具備以下核心功能:
本文範例在實現上述功能外,還做了以下改進:
在 MDI 程式中加入一個包含 Tab 標籤的對話方塊列,並建立 CViewManager 類別。該類別使用指標陣列儲存所有開啟子視窗的視圖指標,使用字串陣列儲存文件標題。在視圖類別的 OnInitialUpdate() 中取得視圖指標與文件標題,存入 CViewManager 陣列並在 Tab 標籤中新增項目。在視圖類別的解構函式中從 Tab 標籤和 CViewManager 陣列中刪除對應項目。切換 Tab 標籤時,取得對應子視窗指標,並以該指標為參數呼叫 CMDIFrameWnd::MDIActivate() 激活該子視窗。
在 VC6.0 中使用精靈建立名為 TabMDIDemo 的 MDI 專案,各步驟均採用預設選項。
IDD_TAB_DLG_BAR,設定如下:
Style: Child
Border: Thin
移除標題列與系統選單
字型:新細明體,大小:10
在模板中加入 Tab 控制項 IDC_TAB,設定如下:
勾選 Buttons 選項 // 使 Tab 標籤呈現按鈕風格
勾選 Hot Track 選項 // 使滑鼠移上時文字變藍
仔細調整對話方塊列與 Tab 控制項的位置、尺寸。建立主框架時,將以該模板建立 Tab 標籤對話方塊列。CMyTab 類別,繼承自 CTabCtrl。CMyTab 加入 TCN_SELCHANGE 通知訊息對應函式,實現文件切換:
void CMyTab::OnSelchange(NMHDR* pNMHDR, LRESULT* pResult)
{
int idx = GetCurSel();
TC_ITEM ti;
ti.mask = TCIF_PARAM;
GetItem(idx, &ti);
CView * pView = (CView *) ti.lParam;
((CMDIFrameWnd *)AfxGetMainWnd())->MDIActivate((pView->GetParent())->GetParent());
*pResult = 0;
}
CMyTab 加入 WM_LBUTTONDBLCLK 訊息對應函式,實現雙擊最大化或還原子視窗:
void CMyTab::OnLButtonDblClk(UINT nFlags, CPoint point)
{
int idx = GetCurSel();
TC_ITEM ti;
ti.mask = TCIF_PARAM;
GetItem(idx, &ti);
CView * pView = (CView *) ti.lParam;
((CMDIFrameWnd *)AfxGetMainWnd())->MDIActivate((pView->GetParent())->GetParent());
BOOL bMaximize = FALSE;
CWnd* pActiveWnd = ((CMDIFrameWnd *)AfxGetMainWnd())->MDIGetActive(&bMaximize);
if (bMaximize)
((CMDIFrameWnd *)AfxGetMainWnd())->MDIRestore(pActiveWnd);
else
((CMDIFrameWnd *)AfxGetMainWnd())->MDIMaximize(pActiveWnd);
}
MainFrm.h 中加入:#include "MyTab.h"CMainFrame 加入成員變數:
protected:
CDialogBar m_wndTabBar;
public:
CMyTab m_MyTab;
ID_VIEW_TAB_BAR。在 MainFrm.cpp 訊息對應區加入:
ON_COMMAND_EX(ID_VIEW_TAB_BAR, OnBarCheck)
ON_UPDATE_COMMAND_UI(ID_VIEW_TAB_BAR, OnUpdateControlBarMenu)
CMainFrame::OnCreate() 中加入建立 Tab 列的程式碼:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// 精靈產生程式碼省略
// 建立 TAB 列
if (!m_wndTabBar.Create(this, IDD_TAB_DLG_BAR,
CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_HIDE_INPLACE,
ID_VIEW_TAB_BAR))
{
TRACE0("Failed to create dialog bar m_wndDialogbar\n");
return -1;
}
// 將 m_MyTab 與控制項 IDC_TAB 繫結
m_MyTab.SubclassDlgItem(IDC_TAB, &m_wndTabBar);
return 0;
}
CMainFrame 加入 WM_SIZE 訊息對應函式:
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
CMDIFrameWnd::OnSize(nType, cx, cy);
CRect rcClient;
GetClientRect(&rcClient);
m_wndTabBar.MoveWindow(rcClient.left, rcClient.top + 24, rcClient.Width(), 24, TRUE);
CRect rcTab;
m_wndTabBar.GetClientRect(&rcTab);
CTabCtrl *pMyTab = (CTabCtrl *)(m_wndTabBar.GetDlgItem(IDC_TAB));
pMyTab->MoveWindow(rcTab, TRUE);
}
新增 ViewManager.h、ViewManager.cpp 兩個檔案並加入專案。類別原始碼請參考本文附帶範例。主要成員說明:
OnActivateView():視圖被激活時呼叫,重新整理 Tab 控制項GetWindowNum():傳回開啟的子視窗數量RemoveAll():清空 arViews、arViewTitles 陣列RemoveView():在陣列與 Tab 控制項中刪除項目AddView():在陣列與 Tab 控制項中新增項目CViewManager() / ~CViewManager():建構/解構函式CPtrArray arViews:儲存子視窗視圖指標CStringArray arViewTitles:儲存文件標題bool bClosing:標記程式是否正在關閉MainFrm.h 加入:#include "ViewManager.h"CMainFrame 加入公有成員:public: CViewManager m_ViewManager;TabMDIDemoView.cpp 加入:#include "MainFrm.h"OnInitialUpdate():
void CTabMDIDemoView::OnInitialUpdate()
{
CView::OnInitialUpdate();
CMDIDialogbarDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CString cs = pDoc->GetTitle();
((CMainFrame*)AfxGetMainWnd())->m_ViewManager.AddView(cs, this);
}
CTabMDIDemoView::~CTabMDIDemoView()
{
((CMainFrame*)AfxGetMainWnd())->m_ViewManager.RemoveView(this);
}
OnActivateView():
void CTabMDIDemoView::OnActivateView(BOOL bActivate,
CView* pActivateView, CView* pDeactiveView)
{
((CMainFrame*)AfxGetMainWnd())->m_ViewManager.OnActivateView(bActivate, this);
CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}
本文附帶完整原始碼,編譯執行即可看到效果。