Aplicar cantos arredondados em aplicativos da área de trabalho para o Windows 11

Os cantos arredondados são o recurso mais imediatamente perceptível da geometria do Windows 11. No Windows 11, o sistema arredonda automaticamente os cantos da janela de nível superior para todos os aplicativos de caixa de entrada, incluindo todos os aplicativos UWP e a maioria dos outros aplicativos. No entanto, alguns aplicativos Win32 podem não ser arredondados. Este tópico descreve como arredondar os cantos da janela principal do seu aplicativo Win32 se o sistema não os arredondar automaticamente.

Observação

Conforme o design, os aplicativos não são arredondados quando maximizados, ajustados, executados em uma VM (máquina virtual) ou em uma WVD (Área de Trabalho Virtual do Windows) ou executados como uma janela do WDAG (Windows Defender Application Guard).

A screenshot of the Notepad app on Windows 11 with rounded corners.

Por que meu aplicativo não é arredondado?

Se a janela principal do seu aplicativo não receber o arredondamento automático, é porque você personalizou seu quadro de maneira que o impeça. Os aplicativos se enquadram em três categorias principais da perspectiva do DWM (Gerenciador de Janelas da Área de Trabalho):

  1. Aplicativos que são arredondados por padrão.

    Isso inclui aplicativos que querem um quadro e controles de legenda completos fornecidos pelo sistema (botões mín/máx/fechar), como o Bloco de notas. Isso também inclui aplicativos que fornecem informações suficientes ao sistema para que ele possa arredondá-los corretamente, como configurar os estilos de janela WS_THICKFRAME e WS_CAPTION ou fornecer uma borda de área que não é do cliente de 1 pixel que o sistema pode usar para arredondar os cantos.

  2. Aplicativos que não são arredondados pela política, mas podem ser.

    Os aplicativos nessa categoria geralmente desejam personalizar a maioria do quadro de janela, mas ainda desejam a borda e sombra desenhada do sistema, como o Microsoft Office. Se seu aplicativo não for arredondado pela política, isso poderá ser causado por um dos seguintes itens:

    • Falta de estilos do quadro
    • Área que não é de cliente vazia
    • Outras personalizações, como janelas adicionais não filho, usadas para sombras personalizadas

    Alterar uma dessas configurações interrompe o arredondamento automático. Embora tenhamos tentado arredondar o máximo possível de aplicativos com a heurística do sistema, há algumas combinações de personalizações que não podemos prever, portanto, fornecemos uma API de aceitação manual para esses casos. Se você resolver esses problemas no seu aplicativo ou chamar a API de aceitação descrita na seção a seguir, o sistema poderá arredondar a janela do seu aplicativo. No entanto, observe que a API é uma dica para o sistema e não garante o arredondamento, que depende das personalizações.

  3. Aplicativos que não podem ser arredondados nunca, mesmo que chamem a API de aceitação.

    Esses aplicativos não têm quadros ou bordas e, normalmente, têm uma interface do usuário muito personalizada. Se seu aplicativo fizer uma das seguintes opções, ele não poderá ser arredondado:

    • Disposição em camadas alfa por pixel
    • Regiões de janela

    Por exemplo, um aplicativo pode usar a disposição em camadas alfa por pixel para desenhar pixels transparentes em toda a janela principal para obter um efeito de sombra personalizado, o que faz com que a janela não seja mais um retângulo e, portanto, o sistema não possa arredondá-la.

Como aceitar os cantos arredondados

Se o aplicativo não for arredondado pela política, você poderá usar essas APIs para permitir que ele aceite cantos arredondados. Especifique a opção de arredondamento de canto desejada para o aplicativo passando um valor da enumeração DWM_WINDOW_CORNER_PREFERENCE (mostrada na tabela a seguir) para a função DwmSetWindowAttribute.

Valor de enumeração Descrição
DWMWCP_DEFAULT Deixar o sistema decidir se deseja ou não arredondar os cantos da janela.
DWMWCP_DONOTROUND Nunca arredondar os cantos da janela.
DWMWCP_ROUND Arredondar os cantos, se apropriado.
DWMWCP_ROUNDSMALL Arredondar os cantos, se apropriado, com um pequeno raio.

Um ponteiro para o valor apropriado dessa enumeração é passado ao terceiro parâmetro de DwmSetWindowAttribute. Para o segundo parâmetro, que especifica qual atributo você está definindo, passe o valor de DWMWA_WINDOW_CORNER_PREFERENCE definido na enumeração DWMWINDOWATTRIBUTE.

Para aplicativos em C#

DwmSetWindowAttribute é uma API Win32 nativa e não é exposta diretamente ao código .NET. Você precisará usar a implementação de P/Invoke da sua linguagem para declarar a função (o código C# é dado no exemplo abaixo). Todos os aplicativos WinForms e WPF padrão são arredondados automaticamente, mas, se você personalizar o quadro de janela ou usar uma estrutura de terceiros, talvez seja necessário aceitar o uso de cantos arredondados. Confira a seção Exemplos para obter mais detalhes.

Exemplos

Os exemplos a seguir mostram como você pode passar esses valores para DwmSetWindowAttribute ou DwmGetWindowAttribute para controlar a experiência de arredondamento do seu aplicativo se ele não for arredondado por meio de uma política.

Observação

O tratamento de erro fica fora desses exemplos para fins de brevidade e clareza.

Exemplo 1 – como arredondar a janela principal de um aplicativo em C# – WPF

Este exemplo mostra como chamar DwmSetWindowAttribute de C# usando o atributo [DllImport]. Observe que essa definição é específica para cantos arredondados; a função DwmSetWindowAttribute foi projetada para assumir parâmetros diferentes dependendo dos sinalizadores fornecidos, portanto, essa não é uma assinatura de uso geral. O exemplo também inclui cópias das enums relevantes do arquivo de cabeçalho dwmapi.h. Como a API Win32 usa um ponteiro para o terceiro parâmetro, use a palavra-chave ref para poder passar o endereço de uma variável quando chamar a função. Você pode fazer isso em sua classe MainWindow em 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
    // ...
}

Em seguida, no Construtor MainWindow, após a chamada para InitializeComponent, crie uma instância da classe WindowInteropHelper para adquirir um ponteiro para o HWND subjacente (identificador de janela). Certifique-se de usar o método EnsureHandle para forçar o sistema a criar um HWND para a janela antes de ser mostrado, porque normalmente o sistema só faz isso depois de sair do construtor.

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

Exemplo 2 – como arredondar a janela principal de um aplicativo em C# – WinForms

Assim como no WPF, para um aplicativo WinForms, primeiro você precisará importar dwmapi.dll e a assinatura de função DwmSetWindowAttribute com P/Invoke. Você pode fazer isso em sua classe de formulário principal.

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

A chamada a DwmSetWindowAttribute também é a mesma que com um aplicativo do WPF, mas você não precisa usar uma classe auxiliar para obter o HWND porque ele é simplesmente uma propriedade do formulário. Chame-o de dentro de seu construtor de formulário, após a chamada para 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
    // ...
}

Exemplo 3 – como arredondar a janela principal de um aplicativo em C++

Para um aplicativo C++ nativo, você pode chamar DwmSetWindowAttribute em sua função de processamento de mensagens após a criação da janela para solicitar que o sistema a arredonde.

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

Exemplo 4 – como arredondar os cantos de um menu com um raio pequeno – C++

Por padrão, os menus são janelas pop-up, que não são arredondadas. Se seu aplicativo cria um menu personalizado e você deseja que ele siga a política de arredondamento dos outros menus padrão, você pode chamar a API para permitir que o sistema saiba que essa janela deve ser arredondada, mesmo que ela não pareça corresponder à política de arredondamento padrão.

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