Windows 11でMFCのCStatusBarをダーク表示にする方法|背景色が変わらない原因とオーナードロー解決策

Windows 11のエクスプローラーのように、MFCアプリのステータスバー(CStatusBar)をダーク表示にしたいのに、SetWindowThemeやSetBkColorがまったく効かない――そんなときに効くのが「テーマ任せをやめて自前描画に切り替える」方法です。本記事では、背景色が変わらない理由と、背景・文字色を確実に暗色化するオーナードロー実装を落とし穴込みで解説します。

目次

Windows 11で「CStatusBarの背景色が変わらない」現象が起きる理由

結論から言うと、Windows 11上でのCStatusBar(正体は共通コントロールのステータスバー/STATUSCLASSNAME)は、テーマ(Visual Styles)による描画の影響を強く受けます。テーマ描画が有効だと、こちらが色を指定しても「結局テーマの見た目で塗られてしまう」「一部だけ上書きされる」といった状態になりやすく、結果としてダーク背景に寄せにくくなります。

実際によくある試行錯誤と結果を整理すると、次のような状況になりがちです。

試したこと期待実際に起きることなぜそうなるか
SetWindowTheme(hStatus, L"DarkMode_Explorer", nullptr)エクスプローラー風のダーク描画になるほぼ変化なし/限定的DarkMode系テーマ名がすべてのコントロールで効くとは限らず、ステータスバーは反映されないことが多い
CStatusBarCtrl::SetBkColor(...)背景が指定色に変わる背景が変わらないテーマ描画中は背景の塗りつぶしが内部で上書きされやすい
テーマを外す(SetWindowTheme(..., L" ", L" "))背景色が変えられる背景は変わるが、文字が黒いまま/枠線が白い/サイズグリップが浮く「背景だけ」変えても、文字色・枠線・グリップは別の描画経路なので残る
OnDrawItemで文字色を変える白文字で描ける呼ばれない(ステータスバーとして検出されない)ペインがオーナードローになっていないため、WM_DRAWITEM系の描画が発火しない

つまり、Windows 11の“それっぽいダーク外観”を狙ってテーマを当てても、MFCのCStatusBarは素直に追従しません。さらに、背景色だけを変えても文字色や境界線の処理が別ルートなので、見た目が中途半端になりやすいのがハマりどころです。

結論:ダーク表示にしたいなら「背景+文字」を自前描画に寄せるのが確実

一番再現性が高いのは、ステータスバーの描画を「テーマ任せ」から「こちらで描く」に寄せる方針です。具体的には、次の2段構えにします。

  • 背景色は必ず自分で塗る:WM_ERASEBKGND(MFCなら派生クラスでOnEraseBkgnd)で暗色に塗りつぶす
  • 文字色は必要に応じて自分で描く:各ペインをオーナードローにしてDrawItem(またはWM_DRAWITEM)でテキストを描画する

これをやると「背景だけ黒っぽいのに文字が黒い」「境界線だけ白いまま」「グリップが浮く」などのチグハグを、段階的に潰していけます。

方針の全体像

実装の見取り図を先に載せます。迷ったらこの順番で潰すと失敗しにくいです。

やること主に効く見た目ポイント
ステータスバーを派生クラス化全体後から拡張しやすい土台を作る
OnEraseBkgndで暗色塗り背景テーマの上書きを避けつつ、確実に暗い背景を作る
各ペインをオーナードローに設定文字、ペイン内描画ここがないとDrawItemが呼ばれない(最大の落とし穴)
DrawItemで白文字描画文字色SetTextColor+TRANSPARENTでにじみを防ぐ
境界線/サイズグリップを調整枠線、右下必要なら非表示化、または色を揃える

背景をダークにする:WM_ERASEBKGND(OnEraseBkgnd)で塗る

背景色だけでもまず安定させたい場合、もっとも効くのがWM_ERASEBKGNDの処理です。ステータスバーは内部描画で背景を消す(消去=塗り直す)タイミングがあるため、そこを押さえておくと「どのタイミングでも暗い背景が維持される」ようになります。

CStatusBar派生クラスの例

まずはCStatusBarを派生して背景塗りを差し込みます。色は固定でもいいですが、後で微調整しやすいようにメンバにしておくのがおすすめです。

class CDarkStatusBar : public CStatusBar
{
public:
    CDarkStatusBar()
        : m_clrBack(RGB(32, 32, 32))   // 背景(例)
        , m_clrText(RGB(230, 230, 230))// 文字(例)
    {}

    void SetDarkColors(COLORREF back, COLORREF text)
    {
        m_clrBack = back;
        m_clrText = text;
        Invalidate();
        UpdateWindow();
    }

protected:
    COLORREF m_clrBack;
    COLORREF m_clrText;

    afx_msg BOOL OnEraseBkgnd(CDC* pDC);

    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CDarkStatusBar, CStatusBar)
    ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

BOOL CDarkStatusBar::OnEraseBkgnd(CDC* pDC)
{
    // 子コントロール(ProgressBar等)を置くなら、塗りつぶしは素直に全面でOK。
    // ちらつきが気になる場合はWS_CLIPCHILDRENの併用も検討。
    CRect rc;
    GetClientRect(&rc);

    CBrush br(m_clrBack);
    pDC->FillRect(&rc, &br);

    // こちらで背景消去を完了したのでTRUE
    return TRUE;
}

この段階で「背景だけは確実に暗くなる」状態になります。ここが基礎工事です。以降の文字色や枠線の調整も、この土台があると作業が安定します。

テーマを外すべきか?

状況によりますが、Windows 11のテーマ描画と自前塗りが競合して「上に白い線が残る」「グリップがやたら目立つ」などが出るなら、ステータスバーに限ってテーマを外すのは現実的な選択肢です。

// 例:ステータスバーだけテーマを外す(クラシック寄り)
::SetWindowTheme(m_wndStatusBar.GetSafeHwnd(), L" ", L" ");

ただし、テーマを外しただけでは文字色までは変わりません。背景塗り+文字の自前描画までセットで考えると、見た目の一体感が出ます。

文字色をダーク向けに変える:ペインをオーナードローにしてDrawItemで描く

背景が暗いと、既定の黒文字は読めません。ここで必要になるのが、各ペイン(パート)のオーナードローです。重要なのは「ステータスバー全体」ではなくペインごとにオーナードローを有効化する点です。

最大の落とし穴:オーナードローにしていないと描画処理が呼ばれない

「OnDrawItemを書いたのに呼ばれない」「ステータスバーとして検出されない」という症状の多くは、ペイン側がオーナードローになっていないことが原因です。オーナードローが有効になって初めて、MFC側が描画用のコールバック(DrawItem)を呼びます。

MFCでペインをオーナードローにする方法

MFCのCStatusBarにはペインのスタイルを操作するAPIがあります。作成後に、文字色を変えたいペインだけにオーナードローを付けます。

// ステータスバー作成後(例:OnInitDialog / OnCreate など)に呼ぶ
void EnableOwnerDrawPanes(CStatusBar& statusBar)
{
    const int paneCount = statusBar.GetStatusBarCtrl().GetParts(0, nullptr);
    for (int i = 0; i < paneCount; ++i)
    {
        UINT style = statusBar.GetPaneStyle(i);

        // 文字表示ペインはオーナードローにする
        style |= SBPS_OWNERDRAW;

        // 境界線を控えめにしたいなら追加で調整
        // style |= SBPS_NOBORDERS;

        statusBar.SetPaneStyle(i, style);
    }

    statusBar.Invalidate();
    statusBar.UpdateWindow();
}

「ProgressBarやComboBoxを置くペインはオーナードローにしない」などの運用をする場合は、このループ内でペイン番号ごとに条件分岐してください(後述します)。

DrawItemで文字を描く例(白文字+暗背景)

MFCのCStatusBarを派生して、DrawItemをオーバーライドします。ここで背景・文字の最終的な見た目を揃えます。

class CDarkStatusBar : public CStatusBar
{
    // ...(前述のOnEraseBkgnd等は省略)

protected:
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) override
    {
        CDC dc;
        dc.Attach(lpDrawItemStruct->hDC);

        // itemIDはペイン番号(パート番号)として使える
        const int paneIndex = static_cast<int>(lpDrawItemStruct->itemID);

        CRect rc = lpDrawItemStruct->rcItem;

        // 背景(念のためペイン単位でも塗る:境界線やにじみ対策)
        CBrush br(m_clrBack);
        dc.FillRect(&rc, &br);

        // テキスト取得(MFCが内部に保持している文字列を使う)
        CString text;
        GetPaneText(paneIndex, text);

        // 余白(読みやすさが上がる)
        rc.DeflateRect(8, 0);

        // 文字描画設定
        dc.SetBkMode(TRANSPARENT);
        dc.SetTextColor(m_clrText);

        // 縦位置を中央寄せにしつつ、左寄せで描画
        dc.DrawText(text, &rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);

        dc.Detach();
    }
};

これで、背景が暗い+文字が明るい、という最低限のダーク表示が完成します。加えて、DT_END_ELLIPSISを入れておくと、長い文字列が切れたときもUIとして破綻しにくくなります。

「文字以外の見た目」がまだ合わないときに見るポイント

背景と文字を揃えても、Windows 11の見た目に寄せようとすると「枠線」「セパレータ」「サイズグリップ」「周囲の1pxライン」などが気になりがちです。ここはアプリの目的に合わせて、どこまで寄せるかを決めると楽になります。

よくある症状と対処の早見表

症状原因の候補現実的な対処
背景は暗いのに、上に白い線(境界)が残るテーマの境界描画が残っている/拡張スタイルのエッジステータスバーに限ってテーマを外す、または境界線を許容して配色で馴染ませる
ペインの区切り線が明るく浮く既定のペイン枠線が残っているSBPS_NOBORDERSを付ける/ペイン構成を見直す
サイズグリップが不格好/明るい既定グリップの描画がダークに対応していないサイズグリップを非表示にする(SBARS_SIZEGRIPを外す)、または右端ペインで上書き描画する
ちらつきが気になる背景塗りと再描画が競合WS_CLIPCHILDRENの併用、必要ならダブルバッファ(メモリDC)化

サイズグリップを出す/出さないの判断

Windows 11の標準アプリは、サイズ変更ができてもグリップが目立たない(あるいは存在感が薄い)設計になっています。MFCのステータスバーでグリップが目立ちすぎるなら、思い切って非表示にした方が「それっぽい」こともあります。

// ステータスバー生成後にグリップを消したい場合
m_wndStatusBar.ModifyStyle(SBARS_SIZEGRIP, 0);

逆に、あえてグリップを残すなら、背景色とコントラストを極端にしない(完全な黒にしない)ことで違和感が減ります。例えばRGB(32,32,32)〜RGB(48,48,48)あたりは、黒つぶれしにくく実用的です。

ペイン内にProgressBarやComboBoxなど「文字以外」を置きたい場合

ステータスバーのペインは「文字を表示するだけ」と思われがちですが、進捗表示や状態選択を置くと便利です。ただし、ここで一律にオーナードローにすると、子コントロールの配置や再描画が面倒になることがあります。現実的には次のどちらかが扱いやすいです。

文字を描くペインだけオーナードローにする

おすすめはこの方式です。文字表示が必要なペインだけをオーナードローにし、ProgressBar等を置くペインは通常運用にします。背景だけはOnEraseBkgndで暗色塗りしておけば、全体の色味は揃います。

// 例:ペイン0と1は文字、ペイン2はProgressBar用として通常運用
void EnableOwnerDrawSelective(CStatusBar& statusBar)
{
    for (int i = 0; i < 3; ++i)
    {
        UINT style = statusBar.GetPaneStyle(i);

        if (i == 2)
        {
            // コントロールを置くのでオーナードローにしない
            style &= ~SBPS_OWNERDRAW;
        }
        else
        {
            style |= SBPS_OWNERDRAW;
        }

        statusBar.SetPaneStyle(i, style);
    }
}

ペインに子コントロールを配置する例

子コントロールの配置は「ペインの矩形を取得してMoveWindow」が基本です。リサイズ時にも追従させたいので、OnSizeなどで同様に再配置すると崩れません。

void PositionProgressBarInPane(CStatusBar& statusBar, CProgressCtrl& progress, int paneIndex)
{
    CRect rc;
    statusBar.GetItemRect(paneIndex, &rc);

    // 余白を作る(枠線や見切れを避ける)
    rc.DeflateRect(6, 3);

    progress.MoveWindow(&rc);
}

暗色テーマに寄せるなら、子コントロール側も色味がズレないように、可能ならテーマを当てたり(コントロールによって効き方は異なります)、背景色を合わせたりします。すべての共通コントロールが完全にダーク対応しているわけではないので、見た目優先なら「目立たせない配色にする」「境界線を減らす」などの工夫が効きます。

統一感を優先して全ペインをオーナードローにする場合

どうしても統一したい場合は、全ペインをオーナードローにしつつ、ペインごとに「文字を描く/描かない」を分岐します。たとえばProgressBarのあるペインは背景だけ塗って、文字描画をスキップします。これでも運用できますが、子コントロールの再配置やZオーダー、ちらつきなどの調整ポイントが増えるため、最初は「文字ペインだけオーナードロー」から入るのが無難です。

実装がうまくいかないときのチェックリスト

最後に、詰まりやすいポイントをチェックリスト化します。原因切り分けに使ってください。

チェック項目確認方法NGだと起きること
背景塗りをOnEraseBkgndで返り値TRUEにしているかFillRect後にTRUEを返すテーマや既定処理で上書きされ、色が安定しない
文字を描きたいペインにSBPS_OWNERDRAWを付けたかGetPaneStyleで確認DrawItemが呼ばれず、文字色が変わらない
DrawItemでSetBkMode(TRANSPARENT)にしているか描画コードを確認文字の背後が四角く塗られて汚く見える
テキストが長い場合の処理(省略表示)を入れたかDT_END_ELLIPSISの有無文字がはみ出し、隣のペインと重なって見える
子コントロールを置くなら再配置処理があるかリサイズ時にMoveWindowしているかウィンドウサイズ変更で位置がズレる

別案:CMFCStatusBarやVisual Managerで「テーマ側」に寄せる方法

「できるだけ自前描画を減らしたい」「MFCの拡張フレームワークをすでに使っている」という場合は、CMFCStatusBarやVisual Manager(例:Office 2007系の黒基調スタイル)を使って、アプリ全体のテーマを整える選択肢もあります。

ただしこのアプローチは、CFrameWndEx/CMDIFrameWndExなど拡張フレームを前提にした構成になりやすく、CDialogExベースのダイアログアプリでは組み込みコストが上がりがちです。既存アプリの構造によっては「ステータスバーだけダーク化」の方が目的に対して最短距離になります。

まとめ:Windows 11のダークなステータスバーは“自前描画”が最短で確実

Windows 11でMFCのCStatusBarをエクスプローラー風にダーク表示したい場合、SetWindowThemeやSetBkColorだけでは思うようにいかないことが多いです。背景はWM_ERASEBKGNDで確実に塗り、文字はペインをオーナードローにしてDrawItemで描く――この組み合わせが最も再現性が高く、ハマりどころ(描画が呼ばれない問題)も回避できます。

まずは「背景を塗る」→「文字ペインだけオーナードロー」→「境界線やグリップを調整」の順で進めると、ダーク表示が安定しやすいのでぜひ試してみてください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次