Использование визуальных стилей с пользовательскими и нарисованными владельцем элементами управления

В этом разделе описывается, как использовать 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. Для отрисовки текста используйте DrawThemeText или DrawThemeTextEx, базируя координаты на прямоугольнике, возвращаемом GetThemeBackgroundContentRect. Эти функции могут отображать текст в шрифте темы для указанной части элемента управления и состояния, либо в шрифте, выбранном в контексте устройства (DC).
  5. Когда элемент управления получает сообщение WM_DESTROY, вызовите CloseThemeData, чтобы освободить дескриптор темы, возвращенный при вызове OpenThemeData.

В следующем примере кода демонстрируется один из способов рисования элемента управления кнопкой в текущем визуальном стиле.

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 несколько раз для заданного элемента управления и используя соответствующий дескриптор темы для рисования разных частей. Однако в некоторых визуальных стилях некоторые части могут быть несовместимы с другими частями.

Другим подходом к элементам управления отрисовки в активном визуальном стиле является использование системных цветов. Например, можно использовать системные цвета для задания цвета текста при вызове функции DrawThemeTextEx . Большинство системных цветов задаются при применении файла визуального стиля.

Реагирование на изменения темы

Когда элемент управления получает сообщение WM_THEMECHANGED и содержит глобальный дескриптор темы, он должен выполнить следующее:

  • Вызовите CloseThemeData , чтобы закрыть существующий дескриптор темы.
  • Вызовите OpenThemeData , чтобы получить дескриптор темы только что загруженному визуальному стилю.

В следующем примере показаны два вызова.

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

Включение визуальных стилей

Стили визуальных элементов