カスタム コントロールと所有者描画コントロールでビジュアル スタイルを使用する

このトピックでは、ビジュアル スタイル API を使用して、カスタム コントロールまたはオーナー描画コントロールにビジュアル スタイルを適用する方法について説明します。

ビジュアル スタイルを使用した描画コントロール

ビジュアル スタイルは、ComCtrl32.dll バージョン 6 以降によりサポートされます。 アプリケーションが ComCtrl32.dll バージョン 6 以降を使用するよう構成されていて、そのバージョンがシステムで利用可能な場合、現在のビジュアル スタイルは、アプリケーション内のすべての共通コントロールに自動的に適用されます。 ただし、現在のビジュアル スタイルは、カスタム コントロールまたはオーナー描画コントロールには自動的には適用されません。 アプリケーションには、ビジュアル スタイルが利用可能かどうかを確認するコードを含める必要があります。利用可能な場合、ビジュアル スタイル API を使用して、現在選択されているビジュアル スタイルをカスタム コントロールとオーナー描画コントロールに適用します。

ビジュアル スタイルを使用できるかどうかを確認するには、IsAppThemed 関数を呼び出します。 ビジュアル スタイルを使用できない場合、フォールバック コードを使用してコントロールを描画します。

ビジュアル スタイルを使用できる場合、DrawThemeText などのビジュアル スタイル関数を使用してコントロールをレンダリングできます。 DrawThemeTextEx を使用すると、テキストの外観をカスタマイズでき、テーマ フォントの一部のプロパティを保持しながら他のプロパティを変更できます。

現在のビジュアル スタイルでコントロールを描画するには

  1. OpenThemeData を呼び出し、ビジュアル スタイルを適用するコントロールの hwnd と、コントロールの種類を記述するクラス リストを渡します。 クラスは Vssym32.h で定義されます。 OpenThemeData は HTHEME ハンドルを返しますが、ビジュアル スタイル マネージャーが無効になっているか、現在のビジュアル スタイルが特定のコントロールの特定の情報を提供しない場合、関数は NULL を返します。 戻り値が NULL の場合、ビジュアル スタイル以外の描画関数を使用します。
  2. コントロールの背景を描画するには、DrawThemeBackground または DrawThemeBackgroundEx を呼び出します。
  3. コンテンツの四角形の位置を確認するには、GetThemeBackgroundContentRect を呼び出します。
  4. テキストをレンダリングするには、GetThemeBackgroundContentRect によって返される四角形の座標に基づいて DrawThemeText または DrawThemeTextEx を使用することができます。 これらの関数は、指定されたコントロール パーツと状態のテーマのフォント、またはデバイス コンテキスト (DC) で現在選択されているフォントでテキストをレンダリングできます。
  5. コントロールが WM_DESTROY メッセージを受信したら、CloseThemeData を呼び出して、OpenThemeData を呼び出したときに返されたテーマ ハンドルを解放します。

次のコード例は、現在のビジュアル スタイルでボタン コントロールを描画する 1 つの方法を示しています。

HTHEME hTheme = NULL;

hTheme = OpenThemeData(hwndButton, L"Button");
// ...
DrawMyControl(hDC, hwndButton, hTheme, iState);
// ...
if (hTheme)
{
    CloseThemeData(hTheme);
}


void DrawMyControl(HDC hDC, HWND hwndButton, HTHEME hTheme, int iState)
{
    RECT rc, rcContent;
    TCHAR szButtonText[255];
    HRESULT hr;
    size_t cch;

    GetWindowRect(hwndButton, &rc);
    GetWindowText(hwndButton, szButtonText,
                  (sizeof(szButtonText) / sizeof(szButtonText[0])+1));
    hr = StringCchLength(szButtonText,
         (sizeof(szButtonText) / sizeof(szButtonText[0])), &cch);
    if (hTheme)
    {
        hr = DrawThemeBackground(hTheme, hDC, BP_PUSHBUTTON, iState, &rc, 0);
        if (SUCCEEDED(hr))
        {
            hr = GetThemeBackgroundContentRect(hTheme, hDC, BP_PUSHBUTTON, 
                    iState, &rc, &rcContent);
        }

        if (SUCCEEDED(hr))
        {
            hr = DrawThemeText(hTheme, hDC, BP_PUSHBUTTON, iState, 
                    szButtonText, cch,
                    DT_CENTER | DT_VCENTER | DT_SINGLELINE,
                    0, &rcContent);
        }

    }
    else
    {
        // Draw the control without using visual styles.
    }
}

次のコード例は、サブクラス化されたボタン コントロールの WM_PAINT メッセージ ハンドラーにあります。 コントロールのテキストはビジュアル スタイル フォントで描画されますが、色はコントロールの状態に応じてアプリケーションによって定義されます。

// textColor is a COLORREF whose value has been set according to whether the button is "hot".
// paint is the PAINTSTRUCT whose members are filled in by BeginPaint.
HTHEME theme = OpenThemeData(hWnd, L"button");
if (theme)
{
    DTTOPTS opts = { 0 };
    opts.dwSize = sizeof(opts);
    opts.crText = textColor;
    opts.dwFlags |= DTT_TEXTCOLOR;
    WCHAR caption[255];
    size_t cch;
    GetWindowText(hWnd, caption, 255);
    StringCchLength(caption, 255, &cch);
    DrawThemeTextEx(theme, paint.hdc, BP_PUSHBUTTON, CBS_UNCHECKEDNORMAL, 
        caption, cch, DT_CENTER | DT_VCENTER | DT_SINGLELINE, 
        &paint.rcPaint, &opts);
    CloseThemeData(theme);
}
else
{
    // Draw the control without using visual styles.
}

他のコントロールのパーツを使用し、各パーツを個別にレンダリングできます。 たとえば、グリッドで構成されるカレンダー コントロールの場合、次のようにテーマ ハンドルを取得することにより、グリッドによって形成された各四角形をツール バー ボタンとして扱うことができます。

OpenThemeData(hwnd, L"Toolbar");

特定のコントロールに対して OpenThemeData を複数回呼び出し、適切なテーマ ハンドルを使用して異なる部分を描画することにより、コントロール パーツを組み合わせることができます。 ただし、一部のビジュアル スタイルでは、特定のパーツが他のパーツと互換性がない場合があります。

アクティブなビジュアル スタイルでコントロールをレンダリングするもう 1 つの方法として、システムの色を使用できます。 たとえば、DrawThemeTextEx 関数を呼び出すときに、システムの色を使用してテキストの色を設定できます。 ほとんどのシステム カラーは、ビジュアル スタイル ファイルが適用されるときに設定されます。

テーマの変更への対応

コントロールが WM_THEMECHANGED メッセージを受信し、テーマへのグローバル ハンドルを保持している場合は、次の操作を行う必要があります。

  • CloseThemeData を呼び出して、既存のテーマ ハンドルを閉じます。
  • OpenThemeData を呼び出して、新しく読み込まれたビジュアル スタイルのテーマ ハンドルを取得します。

次の例は、その 2 つの呼び出しを示しています。

case WM_THEMECHANGED:
     CloseThemeData (g_hTheme);
     g_hTheme = OpenThemeData (hwnd, L"MyClassName");

視覚スタイルを有効にする

ビジュアル スタイル