CMFCToolBar
2014年11月26日
動的にアイコンを変えられるツールバー
C++ でMFC を利用してデスクトップアプリケーションの開発をしていた時の覚書です。
画面を切り替えて使うアプリケーションなどでは、画面ごとに似たような機能を持ったツールバーを用意することがあると思います。
その場合に一つのツールバーで見た目と機能を切り替えると便利かと思ったので、試してみました。
下準備
CMFCToolBar を継承した新しいToolBar クラスを作成し、下記の関数をオーバーライドします。
BOOL CMyToolBar::LoadToolBar(UINT uiResID, UINT uiColdResID, UINT uiMenuResID, BOOL bLocked, UINT uiDisabledResID, UINT uiMenuDisabledResID, UINT uiHotResID) { m_uiOriginalResID = 0; return CMFCToolBar::LoadToolBar(uiResID, uiColdResID, uiMenuResID, bLocked, uiDisabledResID, uiMenuDisabledResID, uiHotResID); } BOOL CMyToolBar::LoadToolBarEx(UINT uiToolbarResID, CMFCToolBarInfo& params, BOOL bLocked) { m_uiOriginalResID = 0; return CMFCToolBar::LoadToolBarEx(uiToolbarResID,params,bLocked); }
実際にはLoadToolBar は内部でLoadToolBarEx を呼び出しているので、LoadToolBarEx だけオーバーライドすればできると思います。
動的にアイコンを変えたい場合にはm_uiOriginalResID の値を0 にしてあげる必要があります。
これはCMFCToolBar::LoadToolBarEx
内でビットマップをロードするLoadBitmapEx と同じ条件式のところで、m_uiOriginalResID の値を見ているからです。
if (m_uiOriginalResID != 0 || LoadBitmapEx(params, bLocked))
afxtoolbar.cpp のLoadToolBarEx 内でこのようにコードが書かれています(VisualStudio12.0で確認)。 m_uiOriginalResID が0 以外の場合ではLoadBitmapEx が呼び出されないため、変更したいツールバーのビットマップが読み込まれないことになってしまいます。
使い方
使い方は簡単で、いつも通りツールバーを作成します。
m_pMyToolBar->CreateEx( this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC, CRect(0, 0, 0, 0), MYTOOLBAR1_ID ); m_pMyToolBar->LoadToolBar(IDR_MYTOOLBAR1, 0, 0, TRUE); m_pMyToolBar->SetWindowText(_T("MyToolbar1")); m_pMyToolBar->SetControlBarStyle(m_pMyToolBar->GetControlBarStyle() & ~AFX_CBRS_CLOSE); this->DockPane(m_pWndToolBar.get());
そのあとに変更したいタイミング(たとえばツールバーのアイコンをクリックしたタイミングなど)で下記のコードを呼び出します。
m_pMyToolBar->CleanUpLockedImages(); m_pMyToolBar->LoadToolBar(IDR_MYTOOLBAR2, 0, 0, TRUE); m_pMyToolBar->AdjustLayout(); m_pMyToolBar->AdjustSizeImmediate();
これで無事にツールバーを変更することができます。
おまけ
実は上に書いたコードには落とし穴がありました。
ツールバーを作成した後に
m_pMyToolBar->SetControlBarStyle(m_pMyToolBar->GetControlBarStyle() & ~AFX_CBRS_CLOSE);
でツールバーの閉じるボタンを無効に設定しているのですが、実際には閉じるボタンは表示されてしまいます。
CanBeClosed で閉じることができるかどうかを判断できるのですが、CMFCToolBar は常にTRUE を返すようです。
ちょっと意味が分からないですね。
そこで、とりあえず下記のようにオーバーライドすることで設定した値で判断するように変更できます。
BOOL CMyToolBar::CanBeClosed() const { return CBasePane::CanBeClosed(); }
これで解決できるのですが、良いのかどうか・・・