Applicare angoli arrotondati nelle app desktop per Windows 11

Gli angoli arrotondati sono la funzionalità più evidente di Geometria di Windows 11. In Windows 11, il sistema arrotonda automaticamente gli angoli delle finestre di livello superiore per tutte le app inbox, incluse tutte le app UWP e la maggior parte delle altre app. Tuttavia, alcune app Win32 potrebbero non essere arrotondate. Questo argomento descrive come arrotondare gli angoli della finestra principale dell'applicazione Win32 se il sistema non li arrotonda automaticamente.

Nota

Per impostazione predefinita, le app non vengono arrotondate quando vengono ingrandite, ancorate, eseguite in una macchina virtuale (VM), in un desktop virtuale Windows (WVD) o come finestra di Windows Defender Application Guard (WDAG).

Screenshot dell'app Blocco note su Windows 11 con angoli arrotondati.

Perché l’app non è arrotondata?

Se la finestra principale dell'app non riceve l'arrotondamento automatico, è perché la cornice è stata personalizzata in modo da impedirlo. Le app si dividono in tre categorie principali dal punto di vista del Desktop Window Manager (DWM):

  1. App che sono arrotondate per impostazione predefinita.

    Ciò include le app che richiedono una cornice completa fornita dal sistema e i controlli dei sottotitoli (pulsanti min/max/close), come Blocco note. Include anche app che forniscono informazioni sufficienti al sistema per poterli arrotondare correttamente, come ad esempio l'impostazione degli stili di finestra WS_THICKFRAME e WS_CAPTION o la fornitura di un bordo dell'area non client di 1 pixel che il sistema può usare per arrotondare gli angoli.

  2. App che non sono arrotondate da criteri, ma che possono essere arrotondate.

    Le app di questa categoria generalmente richiedono di personalizzare la maggior parte della cornice della finestra, pur mantenendo il bordo e l'ombreggiatura disegnati dal sistema, come ad esempio Microsoft Office. Se l'app non viene arrotondata in base ai criteri, il problema potrebbe essere causato da uno dei fattori seguenti:

    • Mancanza di stili cornice
    • Area non client vuota
    • Altre personalizzazioni, come ad esempio finestre extra che non sono elementi figlio usate per le ombreggiature personalizzate

    La modifica di uno di questi elementi interromperà l'arrotondamento automatico. Anche se si è tentato di arrotondare il maggior numero possibile di app con l'euristica del sistema, alcune combinazioni di personalizzazioni non possono essere previste. Pertanto, è stato fornito un'API di consenso esplicito manuale per questi casi. Se si affrontano questi problemi nella propria app o si chiama l'API di consenso esplicito descritta nella sezione seguente, è possibile che il sistema arrotondi la finestra dell'app. Si noti, tuttavia, che l'API è un suggerimento per il sistema e non garantisce l'arrotondamento, in base alle personalizzazioni.

  3. Applicazioni che non possono mai essere arrotondate, anche se chiamano l'API di consenso esplicito.

    Queste app non hanno cornici o bordi e in genere hanno un'interfaccia utente fortemente personalizzata. Se l'app esegue una delle operazioni seguenti, non può essere arrotondata:

    • Disposizione su livelli alfa per pixel
    • Aree della finestra

    Ad esempio, un'app potrebbe usare la disposizione su livelli alfa per pixel per disegnare pixel trasparenti intorno alla finestra principale per ottenere un effetto ombreggiatura personalizzato, che rende la finestra non più un rettangolo e quindi il sistema non può arrotondarla.

Come scegliere gli angoli arrotondati

Se l’app non viene arrotondata in base ai criteri, è possibile usare facoltativamente queste API per consentire all’app di acconsentire esplicitamente agli angoli arrotondati. L'opzione di arrotondamento degli angoli desiderata per l'app viene specificata passando un valore dell'enumerazione DWM_WINDOW_CORNER_PREFERENCE (riportata nella tabella seguente) alla funzione DwmSetWindowAttribute.

Valore enumerazione Descrizione
DWMWCP_DEFAULT Lasciare che sia il sistema a decidere se arrotondare o meno gli angoli delle finestre.
DWMWCP_DONOTROUND Non arrotondare mai gli angoli delle finestre.
DWMWCP_ROUND Arrotondare gli angoli, se necessario.
DWMWCP_ROUNDSMALL Arrotondare gli angoli, se necessario, con un raggio ridotto.

Un puntatore al valore appropriato di questa enumerazione viene passato al terzo parametro di DwmSetWindowAttribute. Per il secondo parametro, che specifica quale attributo si imposta, passare il valore DWMWA_WINDOW_CORNER_PREFERENCE definito nell'enumerazione DWMWINDOWATTRIBUTE.

Per le app C#

DwmSetWindowAttribute è un'API nativa di Win32 e non è esposta direttamente al codice .NET. Per dichiarare la funzione è necessario usare l'implementazione del proprio linguaggio di P/Invoke (nell'esempio seguente viene fornito il codice C#). Tutte le app WinForms e WPF Standard vengono arrotondate automaticamente, ma se si personalizza la cornice della finestra o si usa un framework di terze parti, potrebbe essere necessario acconsentire esplicitamente per gli angoli arrotondati. Per altri dettagli, vedere la sezione Esempi.

Esempi

Gli esempi seguenti illustrano come chiamare DwmSetWindowAttribute o DwmGetWindowAttribute per controllare l'esperienza di arrotondamento dell'app, se questa non viene arrotondata in base ai criteri.

Nota

La gestione degli errori è stata tralasciata in questi esempi per brevità e chiarezza.

Esempio 1 - Arrotondamento della finestra principale di un'applicazione in C# - WPF

Questo esempio illustra come chiamare DwmSetWindowAttribute da C# usando l'attributo [DllImport]. Si noti che questa definizione è specifica per gli angoli arrotondati; la funzione DwmSetWindowAttribute è progettata per accettare parametri diversi a seconda dei flag forniti, quindi non si tratta di una firma per utilizzo generico. L'esempio include anche le copie delle enumerazioni pertinenti dal file di intestazione dwmapi.h. Poiché l'API Win32 prende un puntatore per il terzo parametro, assicurarsi di usare la parola chiave ref in modo da passare l'indirizzo di una variabile quando si chiama la funzione. È possibile farlo nella classe MainWindow in 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
    // ...
}

Successivamente, nel costruttore di MainWindow, dopo la chiamata a InitializeComponent, creare una nuova istanza della classe WindowInteropHelper per acquisire un puntatore all'HWND (handle della finestra) sottostante. Assicurarsi di usare il metodo EnsureHandle per forzare il sistema a creare un HWND per la finestra prima che venga visualizzata, perché normalmente il sistema lo fa solo dopo l'uscita dal costruttore.

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

Esempio 2 - Arrotondamento della finestra principale di un'app C# - WinForms

Come per WPF, per un'app WinForms è necessario importare prima dwmapi.dll e la firma della funzione DwmSetWindowAttribute con P/Invoke. È possibile farlo nella classe Form primaria.

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

Anche la chiamata a DwmSetWindowAttribute è identica a quella di un'app WPF, ma non è necessario usare una classe helper per ottenere l'HWND, perché è semplicemente una proprietà del Form. La chiamata dall'interno del costruttore del Form, dopo la chiamata a 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
    // ...
}

Esempio 3 - Arrotondamento della finestra principale di un'app in C++

Per un'app nativa C++, è possibile chiamare DwmSetWindowAttribute nella funzione di elaborazione dei messaggi dopo la creazione della finestra per chiedere al sistema di arrotondare.

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

Esempio 4 – Arrotondamento degli angoli di un menu con un raggio ridotto - C++

Per impostazione predefinita, i menu sono finestre popup che non vengono arrotondate. Se l’app crea un menu personalizzato e si vuole che segua i criteri di arrotondamento degli altri menu standard, è possibile chiamare l'API per far sapere al sistema che questa finestra deve essere arrotondata, anche se non sembra corrispondere ai criteri di arrotondamento predefiniti.

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