Utilisation de styles visuels avec des contrôles personnalisés et Owner-Drawn

Cette rubrique explique comment utiliser l’API styles visuels pour appliquer des styles visuels à des contrôles personnalisés ou des contrôles dessinés par le propriétaire.

Contrôles de dessin avec des styles visuels

Les styles visuels sont pris en charge par ComCtrl32.dll version 6 et ultérieure. Si votre application est configurée pour utiliser ComCtrl32.dll version 6 et ultérieure, et si cette version est disponible sur le système, les styles visuels actuels sont automatiquement appliqués à tous les contrôles courants de votre application. Toutefois, les styles visuels actuels ne sont pas appliqués automatiquement aux contrôles personnalisés ou aux contrôles dessinés par le propriétaire. Votre application doit inclure du code qui vérifie si les styles visuels sont disponibles et, le cas échéant, utilise l’API styles visuels pour appliquer les styles visuels actuellement sélectionnés à vos contrôles personnalisés et dessinés par le propriétaire.

Pour case activée si des styles visuels sont disponibles, appelez la fonction IsAppThemed. Si les styles visuels ne sont pas disponibles, utilisez le code de secours pour dessiner le contrôle.

Si des styles visuels sont disponibles, vous pouvez utiliser des fonctions de styles visuels telles que DrawThemeText pour restituer votre contrôle. Notez que DrawThemeTextEx vous permet de personnaliser l’apparence du texte, en conservant certaines propriétés de la police de thème tout en modifiant d’autres.

Pour dessiner un contrôle dans le style visuel actuel

  1. Appelez OpenThemeData, en passant le hwnd du contrôle auquel vous souhaitez appliquer des styles visuels et une liste de classes qui décrit le type du contrôle. Les classes sont définies dans Vssym32.h. OpenThemeData renvoie un handle HTHEME, mais si le gestionnaire de styles visuels est désactivé ou si le style visuel actuel ne fournit pas d’informations spécifiques pour un contrôle donné, la fonction retourne NULL. Si la valeur de retour est NULL, utilisez des fonctions de dessin non visuelles.
  2. Pour dessiner l’arrière-plan du contrôle, appelez DrawThemeBackground ou DrawThemeBackgroundEx.
  3. Pour déterminer l’emplacement du rectangle de contenu, appelez GetThemeBackgroundContentRect.
  4. Pour afficher du texte, utilisez DrawThemeText ou DrawThemeTextEx, en basant les coordonnées sur le rectangle retourné par GetThemeBackgroundContentRect. Ces fonctions peuvent restituer du texte dans la police du thème pour une partie de contrôle et un état spécifiés, ou dans la police actuellement sélectionnée dans le contexte de l’appareil (DC).
  5. Lorsque votre contrôle reçoit un message WM_DESTROY , appelez CloseThemeData pour libérer le handle de thème qui a été retourné lorsque vous avez appelé OpenThemeData.

L’exemple de code suivant montre une façon de dessiner un contrôle de bouton dans le style visuel actuel.

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.
    }
}

L’exemple de code suivant se trouve dans le gestionnaire de messages WM_PAINT pour un contrôle bouton sous-classé. Le texte du contrôle est dessiné dans la police des styles visuels, mais la couleur est définie par l’application en fonction de l’état du contrôle.

// 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.
}

Vous pouvez utiliser des parties d’autres contrôles et afficher chaque partie séparément. Par exemple, pour un contrôle de calendrier qui se compose d’une grille, vous pouvez traiter chaque carré formé par la grille comme un bouton de barre d’outils, en obtenant le handle de thème comme suit :

OpenThemeData(hwnd, L"Toolbar");

Vous pouvez mélanger et faire correspondre des parties de contrôle en appelant OpenThemeData plusieurs fois pour un contrôle donné et en utilisant la poignée de thème appropriée pour dessiner différentes parties. Toutefois, dans certains styles visuels, certaines parties peuvent ne pas être compatibles avec d’autres parties.

Une autre approche du rendu des contrôles dans le style visuel actif consiste à utiliser les couleurs système. Par exemple, vous pouvez utiliser les couleurs système pour définir la couleur du texte lors de l’appel de la fonction DrawThemeTextEx . La plupart des couleurs système sont définies lorsqu’un fichier de style visuel est appliqué.

Réponse aux modifications de thème

Lorsque votre contrôle reçoit un message WM_THEMECHANGED et qu’il contient un handle global pour le thème, il doit effectuer les opérations suivantes :

  • Appelez CloseThemeData pour fermer le handle de thème existant.
  • Appelez OpenThemeData pour obtenir le handle de thème dans le style visuel nouvellement chargé.

L’exemple suivant illustre les deux appels.

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

Activation des styles visuels

Styles visuels