Windows XPでVisualStyleが有効な場合、コモンコントロールに含まれているコントロールは、WS_EX_CLIENTEDGEを指定するとVisualStyle対応のボーダーを引いてくれます(デフォルトのテーマだと青い線)。ところがカスタムコントロールの場合には、ボーダーの部分はクライアントエリアではないにも関わらず、OS側で面倒をみてくれません。このため、昔ながらの凹んだエッジが描かれます。
これをVisualStyleっぽくするためには自前で描く必要があるのですが、結構厄介で色々はまったのでメモです。
まず、ボーダー自体はDrawThemeBackgroundで描きます。DrawThemeEdgeというそれっぽい関数は、ボーダーではなくてエッジを描くため、普通の凹んだエッジが描かれてしまいます。
そもそもボーダーとエッジの明確な定義が良くわからないので、少し実験してみました。実験の対象はTreeViewコントロールです。スタイルを組み合わせていくつか作ってみると、
という結果になりました。これらを元に調べていくと、
らしいということが分かります。WS_EX_CLIENTEDGEだけを指定された場合、青い枠の幅は1ドットでエッジは2ドットあるため、内側の1ドットは背景色で塗りつぶされます。WS_BORDER + WS_EX_CLIENTEDGEの場合、元々外側1ドットに黒い枠が、その内側2ドット分に凹みが描画されているのですが、外側2ドット分がVisualStyleによって上書きされるため、実際には外側から、青い枠、背景色、凹みの残骸の順番で1ドットずつ描かれているということになっています。
テーマの設定でOSのエッジの幅(デフォルトでは2ドット)よりも幅の広いボーダーが指定されるとどうなるんだろうという疑問がわきますが、おそらくクリップされるんでしょう(試してません)。
というわけで、この動作をまねるためにはこんな感じにすれば良いのではないかと思われます。
case WM_NCPAINT:
// まずは普通にOSに描かせる
::DefWindowProc(hwnd, uMsg, wParam, lParam);
if (::GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_CLIENTEDGE && ::IsThemeActive(hTheme)) {
// ウィンドウのサイズを取得して左上を0に補正
RECT rect;
::GetWindowRect(hwnd, &rect);
rect.right -= rect.left;
rect.left = 0;
rect.bottom -= rect.top;
rect.top = 0;
HDC hdc = ::GetWindowDC(hwnd);
// OSのエッジの幅だけウィンドウの色で塗りつぶす
int nEdgeWidth = ::GetSystemMetrics(SM_CXEDGE);
int nEdgeHeight = ::GetSystemMetrics(SM_CYEDGE);
::ExcludeClipRect(hdc, nEdgeWidth, nEdgeHeight,
rect.right - nEdgeWidth, rect.bottom - nEdgeHeight);
::SetBkColor(hdc, ::GetSysColor(COLOR_WINDOW));
::ExtTextOut(hdc, rect.left, rect.top,
ETO_CLIPPED | ETO_OPAQUE, &rect, _T(""), 0, 0);
// テーマで設定されたボーダーの幅でクリップして背景を描く
int nBorderWidth = ::GetThemeSysSize(hTheme, SM_CXBORDER);
int nBorderHeight = ::GetThemeSysSize(hTheme, SM_CYBORDER);
::ExcludeClipRect(hdc, nBorderWidth, nBorderHeight,
rect.right - nBorderWidth, rect.bottom - nBorderHeight);
::DrawThemeBackground(hdc, EP_EDITTEXT, 0, &rect, 0);
::ReleaseDC(hwnd, hdc);
}
素のAPIだけで書くように書き換えたので微妙に間違っているかもしれません。エラー処理はご随意に。
ちなみにここでは、EP_EDITTEXTを指定していますが、おおよそ似ているコントロールを使えば良いと思われます。GP_BORDERというばっちりな定義がヘルプに書かれていますが、tmschema.hに含まれていませんので使えません(ベータの時にはあった?)。
それから、テーマ系のAPIのヘルプは重要なところが抜けているのでuxtheme.hのコメントを見たほうが有益です。