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で描く――この組み合わせが最も再現性が高く、ハマりどころ(描画が呼ばれない問題)も回避できます。
まずは「背景を塗る」→「文字ペインだけオーナードロー」→「境界線やグリップを調整」の順で進めると、ダーク表示が安定しやすいのでぜひ試してみてください。

コメント