2014年11月

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);
でツールバーの閉じるボタンを無効に設定しているのですが、実際には閉じるボタンは表示されてしまいます。

CMFCToolBar::CanBeClosed

CanBeClosed で閉じることができるかどうかを判断できるのですが、CMFCToolBar は常にTRUE を返すようです。
ちょっと意味が分からないですね。
そこで、とりあえず下記のようにオーバーライドすることで設定した値で判断するように変更できます。

BOOL CMyToolBar::CanBeClosed() const
{
    return CBasePane::CanBeClosed();
}

これで解決できるのですが、良いのかどうか・・・



rabbitcats at 00:50|PermalinkComments(0)TrackBack(0)C++