Xtreme Toolkit 的 CXTOutBarCtrl 是功能強大的 Outlook Bar 控制項,支援資料夾分組、拖曳排序、標籤編輯、圖示切換等豐富功能。本文將其從類別庫中剝離,移除對 XT 其他類別的依賴,製作成獨立可用的輕量級控制項(僅 4 個檔案),並提供完整整合範例。
XTMemDC.h/cpp:CDC 衍生類,提供無閃爍繪製輔助(雙緩衝)XTOutBarCtrl.h/cpp:核心 Outlook Bar 實作將上述 4 個檔案加入專案,在 stdafx.h 或 MainFrm.h 包含 XTOutBarCtrl.h。
class CMainFrame : public CFrameWnd
{
CSplitterWnd m_wndSplitter;
CXTOutBarCtrl m_wndOutlookBar;
CImageList m_ImageLarge, m_ImageSmall;
bool m_bDestroy;
// ...
};
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// 建立靜態分割視窗:1 列 2 欄
if (!m_wndSplitter.CreateStatic(this, 1, 2)) return FALSE;
// Outlook Bar 置於第 0 欄
if (!m_wndOutlookBar.Create(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
CRect(0,0,0,0), &m_wndSplitter,
m_wndSplitter.IdFromRowCol(0, 0), OBS_XT_DEFAULT))
{
TRACE0("Failed to create outlook bar.\n");
return FALSE;
}
// 右側建立檢視視窗
if (!m_wndSplitter.CreateView(0, 1, pContext->m_pNewViewClass,
CSize(100, 100), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
// 設定 Outlook Bar 外觀
m_wndOutlookBar.SetBackColor(RGB(0x3a, 0x6e, 0xa5));
m_wndOutlookBar.SetTextColor(RGB(0xff, 0xff, 0xff));
// 分割比例初始化
CRect r; GetClientRect(&r);
m_wndSplitter.SetColumnInfo(0, r.Width()/4, 0);
m_wndSplitter.RecalcLayout();
InitializeOutlookBar();
return TRUE;
}
void CMainFrame::InitializeOutlookBar()
{
// 建立圖示清單
m_ImageSmall.Create(16, 16, ILC_COLOR16 | ILC_MASK, 2, 1);
m_ImageLarge.Create(32, 32, ILC_COLOR16 | ILC_MASK, 2, 1);
int nIcons[] = { IDI_ICON1, IDI_ICON2, /* ... 共 11 個 */ };
for (int i = 0; i < 11; ++i) {
HICON hIcon = AfxGetApp()->LoadIcon(nIcons[i]);
m_ImageSmall.Add(hIcon);
m_ImageLarge.Add(hIcon);
}
m_wndOutlookBar.SetImageList(&m_ImageLarge, OBS_XT_LARGEICON);
m_wndOutlookBar.SetImageList(&m_ImageSmall, OBS_XT_SMALLICON);
// 新增資料夾與項目
int iFolder = m_wndOutlookBar.AddFolder(_T("捷徑 1"), 0);
m_wndOutlookBar.InsertItem(iFolder, 1, _T("項目 1"), 0, NULL);
m_wndOutlookBar.InsertItem(iFolder, 2, _T("項目 2"), 1, NULL);
// ... 更多項目
iFolder = m_wndOutlookBar.AddFolder(_T("捷徑 2"), 1);
m_wndOutlookBar.InsertItem(iFolder, 1, _T("項目 1"), 0, NULL);
m_wndOutlookBar.InsertItem(iFolder, 2, _T("項目 2"), 1, NULL);
// 設定通知接收者
m_wndOutlookBar.SetOwner(this);
m_wndOutlookBar.SetSelFolder(iFolder);
// 調整分割比例
CRect r; GetClientRect(&r);
m_wndSplitter.SetColumnInfo(0, r.Width()/7, 0);
m_wndSplitter.SetColumnInfo(1, r.Width()/5, 0);
}
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_MESSAGE(OBN_XT_NOTIFY, OnOutbarNotify)
END_MESSAGE_MAP()
LRESULT CMainFrame::OnOutbarNotify(WPARAM wParam, LPARAM lParam)
{
int nAction = (int)wParam;
XT_OUTBAR_INFO* pInfo = (XT_OUTBAR_INFO*)lParam;
ASSERT(pInfo);
switch (nAction) {
case OBN_XT_ITEMCLICK:
TRACE2(_T("Item selected: %d, Name: %s\n"), pInfo->nIndex, pInfo->lpszText);
break;
case OBN_XT_FOLDERCHANGE:
TRACE2(_T("Folder selected: %d, Name: %s\n"), pInfo->nIndex, pInfo->lpszText);
break;
case OBN_XT_ONLABELENDEDIT:
TRACE2(_T("Item edited: %d, New name: %s\n"), pInfo->nIndex, pInfo->lpszText);
break;
case OBN_XT_ONGROUPENDEDIT:
TRACE2(_T("Folder edited: %d, New name: %s\n"), pInfo->nIndex, pInfo->lpszText);
break;
case OBN_XT_DRAGITEM:
TRACE3(_T("Dragging From: %d, To: %d, Name: %s\n"),
pInfo->nDragFrom, pInfo->nDragTo, pInfo->lpszText);
break;
case OBN_XT_ITEMHOVER:
TRACE2(_T("Hovering Item: %d, Name: %s\n"), pInfo->nIndex, pInfo->lpszText);
break;
case OBN_XT_DELETEITEM:
if (!m_bDestroy && AfxMessageBox(_T("確定刪除此捷徑?"),
MB_ICONWARNING | MB_YESNO) == IDNO) return FALSE;
break;
case OBN_XT_DELETEFOLDER:
if (!m_bDestroy && AfxMessageBox(_T("確定刪除此資料夾?"),
MB_ICONWARNING | MB_YESNO) == IDNO) return FALSE;
break;
}
return TRUE;
}
| 優點 | 缺點 |
|---|---|
| 獨立編譯,無需完整 XT 類別庫 | 部分進階功能(如動畫、主題)被移除 |
| 原始碼開放,可深度客製化 | 需自行處理高 DPI 縮放適配 |
| 體積小巧,易於整合既有專案 | Unicode/ANSI 版本需注意字串轉換 |
CXTOutBarCtrl 獨立版保留了 Outlook Bar 核心體驗,同時擺脫龐大類別庫依賴。適合需要專業導航介面但不想引入完整商業函式庫的專案。原始碼開放性使其成為學習自訂控制項開發的優秀教材。