MFCのCStatusBarをオーナードローで使っていると、DrawItemで背景まで描いているのに、ペイン間に縦の区切り線(セパレータ)が残ることがあります。これはコントロールが枠線を自動描画しているためで、SBT_NOBORDERSで「最初からボーダーなし」にするのが手堅い解決策です。
現象:自前描画なのにペイン間に縦線が出る
ステータスバーをオーナードローにすると、「文字や背景は自分で描く」ことになります。しかしWindowsのステータスバー(STATUSCLASSNAME)は、ペインごとの枠線や立体的なエッジ、ペイン間の区切り(縦線)などを内部で描画する作りになっています。
そのため、DrawItemで背景を塗りつぶしてテキストやアイコンを描いても、縦の区切り線だけが残ることがあります。これはアプリ側が描いたものではなく、コモンコントロールが「ペインのボーダー」として描いている要素です。
「区切り線だけ消したい」がハマりやすい理由
- DrawItemは「ペインの中身」を描くコールバックで、枠線まで完全に奪うわけではない
- OSテーマ(視覚スタイル)やDPI設定により、縦線の見え方が微妙に変わり「自分の描画が漏れている」ように見える
- MFCのSetPaneTextなどを使うと、内部でSB_SETTEXTが再送され、スタイルが意図せず戻ることがある
結論:SBT_NOBORDERSで「ボーダーなし」を指定する
ペイン間の縦線(セパレータ)を消す最も確実な方法は、ステータスバーに対してSBT_NOBORDERSを指定することです。
ポイントは「描画結果から線を消す」のではなく、コントロールに“枠を描かせない”設定を与えることです。これにより、ペイン間の区切り線も枠線も、最初から描画されなくなります。
| やりたいこと | 正しいアプローチ | 避けたいアプローチ |
|---|---|---|
| ペイン間の縦線を消す | SBT_NOBORDERSを指定して、ボーダー描画自体を抑制 | DrawItemの中で線を上書きする/WM_PAINTを無理やりフックする |
| 区切り線をデザインに合わせて変更したい | SBT_NOBORDERSで消してから、自前で必要な線だけ描く | OS既定の線を「消せないか」と探し続ける |
SBT_NOBORDERSとは何か
SBT_NOBORDERSは、ステータスバーの各ペイン(パーツ)に対して「ボーダーを描かない」ことを指定するフラグです。Win32 APIではSB_SETTEXTメッセージのwParamに組み合わせて渡します。
MFCのCStatusBar周辺では、似た名前としてSBPS_NOBORDERSが登場することがありますが、実体は「ペインのスタイルを指定するための定数」で、最終的にはSB_SETTEXTでSBT_NOBORDERS相当が使われます。コードを書く場所(API)によって名前が違うだけ、と考えると整理しやすいです。
SB_SETTEXTのwParam構造を押さえる
SB_SETTEXTは「どのペインに、どんな形式でテキスト(またはデータ)を設定するか」を指示するメッセージです。wParamにはペイン番号とスタイル(SBT_系フラグ)をORして渡します。
| 要素 | 入れるもの | 例 |
|---|---|---|
| ペイン番号 | 0始まりのインデックス | 0, 1, 2 … |
| 描画タイプ | SBT_NOBORDERS / SBT_POPOUT / SBT_OWNERDRAW など | partIndex | SBT_NOBORDERS |
実装例:Win32 APIでSBT_NOBORDERSを指定する
Win32 APIでステータスバー(HWND)を直接操作する場合、SB_SETTEXTでSBT_NOBORDERSを指定します。これだけで縦の区切り線は消せます。
SendMessage(hStatusBar, SB_SETTEXT,
(WPARAM)(partIndex | SBT_NOBORDERS),
(LPARAM)text);
ここでの注意点は、テキストを更新するたびにSBT_NOBORDERSを維持することです。別の箇所でSB_SETTEXTを送り直すと、そのときのフラグが新しいスタイルとして採用されます。つまり、どこかでSBT_NOBORDERSなしで更新すると、枠が復活します。
オーナードロー(SBT_OWNERDRAW)と併用する場合
すでにオーナードローで描いているなら、フラグはSBT_OWNERDRAW | SBT_NOBORDERSのように併用します。オーナードローの場合、SB_SETTEXTのlParamは文字列ポインタではなく、WM_DRAWITEMに渡される「ユーザーデータ(itemData)」になります。
// オーナードロー + ボーダーなし(区切り線なし)
DWORD_PTR dwData = 0; // 必要なら独自データを入れる
SendMessage(hStatusBar, SB_SETTEXT,
(WPARAM)(partIndex | SBT_OWNERDRAW | SBT_NOBORDERS),
(LPARAM)dwData);
「表示文字列はどこに入れるの?」という点は実装方針によって変わります。たとえば、自前の配列や構造体でペインごとのテキストを保持し、WM_DRAWITEMでそのテキストを参照して描画します。MFCのCStatusBarも内部的には似た発想で、ペイン情報を保持してDrawItemで描きます。
実装例:MFCでSBT_NOBORDERSを指定する
MFCでCStatusBarを使っている場合でも、実体はWin32のステータスバーコントロールなので、最終的にSB_SETTEXTへ落ちます。MFC経由でSBT_NOBORDERSを渡す方法はいくつかあります。
CStatusBarCtrl::SetTextで指定する(コントロールを直接叩く)
最も分かりやすいのは、CStatusBarからCStatusBarCtrlを取り出してSetTextを呼ぶ方法です。第3引数にSBT_NOBORDERSを渡すことで、枠・区切り線が消えます。
m_wndStatusBar.GetStatusBarCtrl().SetText(
_T("Ready"),
partIndex,
SBT_NOBORDERS
);
ただし、そのペインをオーナードローにしている場合は、SBT_NOBORDERSだけだとオーナードロー指定が外れてしまいます。オーナードロー継続が必要なら、SBT_OWNERDRAWも併用します。
// オーナードローを維持しつつ、区切り線を消す
m_wndStatusBar.GetStatusBarCtrl().SetText(
(LPCTSTR)(DWORD_PTR)dwData, // オーナードローの場合は「データ」を渡す
partIndex,
SBT_OWNERDRAW | SBT_NOBORDERS
);
ここはMFCのラッパーが「文字列引数」を要求しているため、キャストが必要になります。安全にやるなら、オーナードロー部分はCStatusBar::SetPaneInfo(SBPS_OWNERDRAW)と組み合わせて、MFCの流儀に合わせる方が事故が減ります。
SBPS_NOBORDERSをペインスタイルとして指定する(MFCらしい方法)
CStatusBarをMFCらしく使うなら、ペインのスタイルにSBPS_NOBORDERSを入れておくのが実用的です。オーナードロー指定(SBPS_OWNERDRAW)とも素直に併用できます。
// 例:ペイン1をオーナードロー + ボーダーなし
m_wndStatusBar.SetPaneInfo(
1, // ペイン番号
ID_INDICATOR_CAPS, // ID
SBPS_OWNERDRAW | SBPS_NOBORDERS,
80 // 幅
);
この方法のメリットは、以後SetPaneTextで更新してもスタイルが保たれやすい点です。CStatusBarは内部でペインスタイルを保持し、更新時にそれを反映してSB_SETTEXTを送るため、「更新のたびにSBT_NOBORDERSを忘れて枠が復活する」問題が起きにくくなります。
サンプル:オーナードローCStatusBarで区切り線を完全に消す
ここでは「オーナードローのペインがあり、かつペイン間の区切り線を消したい」ケースを想定した最小構成を示します。プロジェクト事情に合わせて調整してください。
// MyStatusBar.h
class CMyStatusBar : public CStatusBar
{
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) override;
};
// MyStatusBar.cpp
void CMyStatusBar::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDC dc;
dc.Attach(lpDIS->hDC);
CRect rc(lpDIS->rcItem);
// 背景は必ず自前で塗る(テーマによっては“線”が残ったように見えるのを防ぐ)
dc.FillSolidRect(rc, ::GetSysColor(COLOR_3DFACE));
// itemIDにはペイン番号が入る想定で取得
const int partIndex = (int)lpDIS->itemID;
CString text;
GetPaneText(partIndex, text);
dc.SetBkMode(TRANSPARENT);
rc.DeflateRect(6, 1);
dc.DrawText(text, rc, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
dc.Detach();
}
作成側(フレーム)の例です。ペインスタイルでSBPS_NOBORDERSを指定している点が重要です。
// MainFrm.cpp(例)
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
static UINT indicators[] =
{
ID_SEPARATOR, // 0: メイン表示(伸縮)
ID_INDICATOR_CAPS, // 1
ID_INDICATOR_NUM, // 2
ID_INDICATOR_SCRL // 3
};
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators, _countof(indicators)))
{
return -1;
}
// 区切り線(ボーダー)を消す:全ペインにSBPS_NOBORDERSを付ける
m_wndStatusBar.SetPaneInfo(0, ID_SEPARATOR,
SBPS_STRETCH | SBPS_NOBORDERS, 0);
m_wndStatusBar.SetPaneInfo(1, ID_INDICATOR_CAPS,
SBPS_OWNERDRAW | SBPS_NOBORDERS, 80);
m_wndStatusBar.SetPaneInfo(2, ID_INDICATOR_NUM,
SBPS_OWNERDRAW | SBPS_NOBORDERS, 80);
m_wndStatusBar.SetPaneInfo(3, ID_INDICATOR_SCRL,
SBPS_OWNERDRAW | SBPS_NOBORDERS, 80);
// 表示文字は通常通りSetPaneTextで更新(オーナードローではDrawItem側で参照して描画)
m_wndStatusBar.SetPaneText(0, _T("Ready"));
m_wndStatusBar.SetPaneText(1, _T("CAPS"));
m_wndStatusBar.SetPaneText(2, _T("NUM"));
m_wndStatusBar.SetPaneText(3, _T("SCRL"));
return 0;
}
この構成なら、ステータスバーはペイン間の縦線を描かず、各ペインの中身はDrawItemで自由に描けます。テーマやDPIが変わっても、区切り線が復活しにくいのが利点です。
よくあるつまずきと対処
SetPaneTextを呼ぶと区切り線が戻る
原因の多くは、どこかでSBT_NOBORDERSなしのSB_SETTEXTが送られていることです。MFCの内部更新や、別のヘルパー関数がSetPaneText相当の処理をしているケースもあります。
- ペインスタイルにSBPS_NOBORDERSを含める(基本)
- CStatusBarCtrl::SetTextを使う場合は、更新するすべての箇所でSBT_NOBORDERSを付ける
- オーナードローを使うペインは、SBT_OWNERDRAWが外れないように注意する
縦線は消えたのに“うっすら影”が見える
SBT_NOBORDERSで枠線は消えますが、描画の仕方によっては背景の塗りムラが原因で、境界が影のように見えることがあります。オーナードローの場合は特に、ペインのrcItem全面を確実に塗りつぶすのが効果的です。
- FillSolidRectで背景を塗る
- 余白(DeflateRect)を作り、文字が端に密着して“線”に見えるのを防ぐ
- 背景色はGetSysColor(COLOR_3DFACE)などシステム色に合わせる
特定のペインだけ区切り線を残したい
ペインごとにSBT_NOBORDERSを付けるかどうかを変えられます。デザイン上「メイン表示のペインはフラットに、通知系ペインは区切りを残したい」などの要件なら、対象ペインだけSBT_NOBORDERSを外しても構いません。
ただし混在させると、テーマによっては境界が不揃いに見えることがあります。UIの一貫性を重視するなら、いったん全ペインをSBT_NOBORDERSにしてから、自前で必要な線をDrawItemで描く方がデザインコントロールしやすいです。
サイズグリップだけ消したい/残したい
右下に出る斜線の“サイズグリップ”は、ペイン間の区切り線とは別物です。グリップはステータスバーのスタイル(SBARS_SIZEGRIP)で制御されます。区切り線問題とは独立しているため、「縦線を消したのにグリップが残る(またはその逆)」は正常な挙動です。
手順のチェックリスト
- 区切り線は自前描画ではなく、ステータスバーコントロールが描いていると理解する
- SBT_NOBORDERS(MFCならSBPS_NOBORDERS)で“枠なし”にする
- オーナードローを使うペインは、SBT_OWNERDRAW(SBPS_OWNERDRAW)が外れないようにする
- 更新箇所が複数ある場合は、どこでSB_SETTEXTしているかを洗い出し、フラグを統一する
- オーナードローでは背景の全面塗りつぶしと余白確保を徹底する
まとめ
オーナードローのCStatusBarでペイン間のセパレータを消したい場合、正攻法はSBT_NOBORDERSです。線を消すために描画処理を複雑化させるのではなく、コントロールに「最初から枠線を描かせない」設定を与えることで、安定してフラットなステータスバーを実現できます。
特にMFCの場合は、ペインスタイルとしてSBPS_NOBORDERSを設定しておくと、更新のたびにスタイルが戻る事故を防ぎやすく、オーナードローとも相性が良いです。区切り線が不要なUIでは、まずこの設定を疑ってみてください。

コメント