Appliquer des angles arrondis dans des applications de bureau pour Windows 11

Dans l’immédiat, les angles arrondis sont la fonctionnalité la plus notable de l’option Géométrie de Windows 11. Dans Windows 11, le système arrondit automatiquement les angles des fenêtres de niveau supérieur pour toutes les applications de boîte de réception, notamment toutes les applications UWP, et la plupart des autres applications. Toutefois, certaines applications Win32 peuvent ne pas être arrondies. Cette rubrique explique comment arrondir les angles de la fenêtre principale de votre application Win32 si le système ne les arrondit pas automatiquement.

Note

Par défaut, les applications ne sont pas arrondies quand elles sont agrandies, alignées, exécutées sur une machine virtuelle, exécutées sur un bureau WVD (Windows Virtual Desktop) ou exécutées comme fenêtre WDAG (Windows Defender Application Guard).

Capture d’écran de l’application Bloc-notes sur Windows 11 avec des angles arrondis.

Pourquoi mon application n’est-elle pas arrondie ?

Si la fenêtre principale de votre application n’a pas automatiquement des angles arrondis, cela est dû au fait que vous avez personnalisé votre cadre d’une manière qui l’empêche. Les applications sont réparties en trois catégories principales du point de vue du Gestionnaire de fenêtrage (DWM) :

  1. Applications arrondies par défaut.

    Cela comprend les applications qui requièrent un cadre complet fourni par le système et des contrôles de légende (boutons de réduction/agrandissement/fermeture), comme le Bloc-notes. Cela comprend également les applications qui fournissent suffisamment d’informations au système pour lui permettre de les arrondir correctement, par exemple pour définir les styles de fenêtre WS_THICKFRAME et WS_CAPTION ou pour fournir une bordure de zone non-cliente de 1 pixel que le système peut utiliser pour arrondir les angles.

  2. Les applications qui ne sont pas arrondies par stratégie, mais qui peuvent être arrondies.

    En général, les applications de cette catégorie veulent personnaliser la majorité du cadre de la fenêtre tout en gardant la bordure et l’ombre dessinées par le système, comme Microsoft Office. Si votre application n’est pas arrondie par stratégie, cela peut être dû à l’une des raisons suivantes :

    • Manque de styles de cadre
    • Zone non cliente vide
    • Autres personnalisations, comme les fenêtres non enfants supplémentaires utilisées pour les ombres personnalisées

    Si vous modifiez l’un de ces éléments, l’arrondissement automatique s’arrête. Même si nous avons effectivement essayé d’arrondir autant d’applications que possible avec notre heuristique système, il existe certaines combinaisons de personnalisations que nous ne pouvons pas prédire. C’est pour cela que nous avons fourni une API d’adhésion manuelle pour ces cas-là. Si vous traitez ces problèmes dans votre application ou que vous appelez l’API de consentement décrite dans la section suivante, il est possible pour le système d’arrondir la fenêtre de votre application. Notez toutefois que l’API est une indication pour le système et ne garantit pas l’arrondi, selon les personnalisations.

  3. Applications qui ne peuvent jamais être arrondies, même si elles appellent l’API d’adhésion.

    Ces applications n’ont pas de cadre ou de bordure, et ont généralement une interface utilisateur fortement personnalisée. Si votre application effectue l’une des opérations suivantes, elle ne peut pas être arrondie :

    • Superposition alpha par pixel
    • Zones de fenêtre

    Par exemple, une application peut utiliser la superposition alpha par pixel pour dessiner des pixels transparents autour de sa fenêtre principale pour obtenir un effet d’ombre personnalisé. De ce fait, la fenêtre n’est plus un rectangle et, par conséquent, le système ne peut pas l’arrondir.

Comment opter pour les angles arrondis

Si votre application n’est pas arrondie par stratégie, vous pouvez utiliser ces API afin de lui permettre d’opter pour les angles arrondis. Pour spécifier l’option d’arrondi des angles souhaitée pour votre application, passez une valeur de l’énumération DWM_WINDOW_CORNER_PREFERENCE (voir tableau suivant) à la fonction DwmSetWindowAttribute.

Valeur enum Description
DWMWCP_DEFAULT Laisser le système décider s’il faut, ou non, arrondir les angles des fenêtres.
DWMWCP_DONOTROUND Ne jamais arrondir les angles des fenêtres.
DWMWCP_ROUND Arrondir les angles si nécessaire.
DWMWCP_ROUNDSMALL Arrondir les angles si nécessaire, avec un petit rayon.

Un pointeur vers la valeur appropriée de cet enum est passé au troisième paramètre de DwmSetWindowAttribute. Pour le deuxième paramètre, qui spécifie l’attribut que vous définissez, passez la valeur DWMWA_WINDOW_CORNER_PREFERENCE définie dans l’énumération DWMWINDOWATTRIBUTE.

Pour les applications C#

DwmSetWindowAttribute est une API Win32 native qui n’est pas exposée directement au code .NET. Vous devez utiliser l’implémentation dans votre langage de P/Invoke pour déclarer la fonction (le code C# est fourni dans l’exemple ci-dessous). Toutes les applications WinForms et WPF standard sont arrondies automatiquement, mais si vous personnalisez le cadre de votre fenêtre ou si vous utilisez une infrastructure de tiers, vous devrez peut-être donner votre consentement pour les coins arrondis. Pour plus d’informations, consultez la section Exemples.

Examples

Les exemples suivants montrent comment vous pouvez appeler DwmSetWindowAttribute ou DwmGetWindowAttribute pour contrôler l’utilisation de l’arrondi dans votre application si cette dernière n’est pas arrondie par stratégie.

Note

Par souci de concision et de lisibilité, la gestion des erreurs n’a pas été traitée dans ces exemples.

Exemple 1 : Arrondissement de la fenêtre principale d’une application en C# - WPF

Cet exemple montre comment appeler DwmSetWindowAttribute depuis C# en utilisant l’attribut [DllImport]. Notez que cette définition est spécifique aux coins arrondis ; la fonction DwmSetWindowAttribute est conçue pour prendre différents paramètres selon les indicateurs fournis : ce n’est donc pas une signature à usage général. L’exemple comprend également des copies des énumérations pertinentes du fichier d’en-tête dwmapi.h. Comme l’API Win32 prend un pointeur pour le troisième paramètre, veillez à utiliser le mot clé ref pour pouvoir passer l’adresse d’une variable quand vous appelez la fonction. Vous pouvez le faire dans votre classe MainWindow dans MainWindow.xaml.cs.

using System.Runtime.InteropServices;
using System.Windows.Interop;

public partial class MainWindow : Window
{
    // The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
    // Copied from dwmapi.h
    public enum DWMWINDOWATTRIBUTE
    {
        DWMWA_WINDOW_CORNER_PREFERENCE = 33
    }

    // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
    // what value of the enum to set.
    // Copied from dwmapi.h
    public enum DWM_WINDOW_CORNER_PREFERENCE
    {
        DWMWCP_DEFAULT      = 0,
        DWMWCP_DONOTROUND   = 1,
        DWMWCP_ROUND        = 2,
        DWMWCP_ROUNDSMALL   = 3
    }

    // Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
    [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
    internal static extern void DwmSetWindowAttribute(IntPtr hwnd,
                                                     DWMWINDOWATTRIBUTE attribute,
                                                     ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
                                                     uint cbAttribute);
    // ...
    // Various other definitions
    // ...
}

Dans votre constructeur MainWindow, après l’appel de InitializeComponent, créez une nouvelle instance de la classe WindowInteropHelper afin d’acquérir un pointeur vers le HWND (indicateur de fenêtre) sous-jacent. Veillez à utiliser la méthode EnsureHandle pour forcer le système à créer un identificateur HWND pour la fenêtre avant qu’il ne soit affiché, car normalement, le système ne le fait qu’après avoir quitté le constructeur.

public MainWindow()
{
    InitializeComponent();

    IntPtr hWnd = new WindowInteropHelper(GetWindow(this)).EnsureHandle();
    var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
    var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
    DwmSetWindowAttribute(hWnd, attribute, ref preference, sizeof(uint));
    
    // ...
    // Perform any other work necessary
    // ...
}

Exemple 2 : Arrondissement de la fenêtre principale d’une application en C# - WinForms

Comme avec WPF, pour une application WinForms, vous devez d’abord importer dwmapi.dll et la signature de la fonction DwmSetWindowAttribute avec P/Invoke. Vous pouvez le faire dans votre classe de formulaire principale.

using System;
using System.Runtime.InteropServices;

public partial class Form1 : Form
{
    // The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
    // Copied from dwmapi.h
    public enum DWMWINDOWATTRIBUTE
    {
        DWMWA_WINDOW_CORNER_PREFERENCE = 33
    }

    // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
    // what value of the enum to set.
    // Copied from dwmapi.h
    public enum DWM_WINDOW_CORNER_PREFERENCE
    {
        DWMWCP_DEFAULT      = 0,
        DWMWCP_DONOTROUND   = 1,
        DWMWCP_ROUND        = 2,
        DWMWCP_ROUNDSMALL   = 3
    }

    // Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
    [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
    internal static extern void DwmSetWindowAttribute(IntPtr hwnd,
                                                     DWMWINDOWATTRIBUTE attribute, 
                                                     ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute, 
                                                     uint cbAttribute);
    
    // ...
    // Various other definitions
    // ...
}

L’appel de DwmSetWindowAttribute est également identique à celui d’une application WPF, mais vous n’avez pas besoin d’utiliser une classe auxiliaire pour obtenir l’identificateur de fenêtre HWND car il s’agit simplement d’une propriété du formulaire. Appelez-le à partir du constructeur de votre formulaire, après l’appel de InitializeComponent.

public Form1()
{
    InitializeComponent();

    var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
    var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
    DwmSetWindowAttribute(this.Handle, attribute, ref preference, sizeof(uint));
    
    // ...
    // Perform any other work necessary
    // ...
}

Exemple 3 : Arrondissement de la fenêtre principale d’une application en C++

Pour une application C++ native, vous pouvez appeler DwmSetWindowAttribute dans votre fonction de traitement des messages après la création de la fenêtre pour demander au système de créer les arrondis.

LRESULT ExampleWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
    switch (message)
    {
    // ...
    // Handle various window messages...
    // ...

    case WM_CREATE:
        // ...
        // Perform app resource initialization after window creation
        // ...
        
        if(hWnd)
        {
            DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUND;
            DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
        }
        break;

    // ...
    // Handle various other window messages...
    // ...
    }

    return 0;
}

Exemple 4 : Arrondissement des angles d’un menu avec un petit rayon - C++

Par défaut, les menus sont des fenêtres contextuelles qui ne sont pas arrondies. Si votre application crée un menu personnalisé et que vous voulez qu’elle suive la stratégie d’arrondi d’autres menus standard, vous pouvez appeler l’API pour informer le système que cette fenêtre doit être arrondie, même si elle ne semble pas correspondre à la stratégie d’arrondi par défaut.

HWND CreateCustomMenu()
{
    // Call an app-specific helper to make the window, using traditional APIs.
    HWND hWnd = CreateMenuWindowHelper();

    if (hWnd)
    {
        // Make sure we round the window, using the small radius 
        // because menus are auxiliary UI.
        DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUNDSMALL;
        DwmSetWindowAttribute(hWnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference));
    }

    return hWnd;
}