Übernehmen abgerundeter Ecken in Desktop-Apps für Windows 11

Abgerundete Ecken sind das am unmittelbarsten erkennbare Merkmal der Windows 11-Geometrie. In Windows 11 rundet das System die Ecken von Fenstern der obersten Ebene für alle Posteingangs-Apps, einschließlich aller UWP-Apps, und die meisten anderen Apps automatisch ab. Bei einigen Win32-Apps funktioniert das Abrunden der Ecken jedoch möglicherweise nicht. In diesem Thema wird beschrieben, wie Sie die Ecken der Hauptfenster Ihrer Win32-App abrunden, wenn sie vom System nicht automatisch abgerundet werden.

Hinweis

Standardmäßig werden die Ecken von Apps nicht abgerundet, wenn sie maximiert, eingerastet, auf einem virtuellen Computer (VM), auf einem WVD (Windows Virtual Desktop) oder als Windows Defender Application Guard-Fenster (WDAG) ausgeführt werden.

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

Warum werden die Ecken meiner App nicht abgerundet?

Wenn das Hauptfenster Ihrer App keine automatische Abrundung erfährt, liegt dies daran, dass Sie Ihren Rahmen so angepasst haben, dass dies verhindert wird. Aus der Sicht des DWM (Desktop-Fenster-Manager) teilen sich Apps in drei Hauptkategorien:

  1. Apps, deren Ecken standardmäßig abgerundet werden.

    Dies schließt Apps ein, die einen vollständig vom System bereitgestellten Rahmen und vom System bereitgestellte Beschriftungssteuerelemente (Minimieren/Maximieren/Schließen) verwenden, wie Editor. Es schließt darüber hinaus Apps ein, die dem System genügend Informationen bereitstellen, sodass es sie ordnungsgemäß abrunden kann, etwa durch Festlegen der Fensterstile WS_THICKFRAME und WS_CAPTION oder durch Bereitstellen eines nicht zum Clientbereich gehörenden Rahmens von 1 Pixel Breite, den das System zum Abrunden der Ecken verwenden kann.

  2. Apps, deren Ecken aufgrund der Richtlinie nicht abgerundet werden, aber abgerundet werden können.

    Apps in dieser Kategorie übernehmen in der Regel die Anpassung ihres Fensterrahmens überwiegend selbst, erwarten aber trotzdem den vom System gezeichneten Rahmen und Schatten, wie etwa Microsoft Office. Wenn die Ecken Ihrer App nicht aufgrund einer Richtlinie abgerundet werden, kann dies eine der folgenden Ursachen haben:

    • Fehlende Rahmenformatvorlagen
    • Leerer Nicht-Clientbereich
    • Andere Anpassungen, z. B. zusätzliche nicht untergeordnete Fenster, die für benutzerdefinierte Schatten verwendet werden

    Wenn Sie einen dieser Punkte ändern, wird die automatische Rundung verhindert. Zwar haben wir versucht, mit unserer Systemheuristik möglichst vielen Apps die Abrundung zu ermöglichen, es gibt jedoch einige Kombinationen von Anpassungen, die wir nicht vorhersagen können, sodass wir für diese Fälle eine optionale manuelle API zur Verfügung gestellt haben. Wenn Sie diese Probleme in Ihrer App beheben oder die im folgenden Abschnitt beschriebene optionale API aufrufen, ist es dem System möglich, die Ecken des Fensters Ihrer App abzurunden. Beachten Sie jedoch, dass die API ein Hinweis für das System ist und keine Rundung garantiert, je nach den vorgenommenen Anpassungen.

  3. Apps, deren Ecken in keinem Fall abgerundet werden können, auch nicht durch Aufruf der optionalen API.

    Diese Apps weisen keine Rahmen oder Kanten auf und verfügen in der Regel über eine stark angepasste Benutzeroberfläche. Wenn Ihre App eins der folgenden Verfahren einsetzt, können ihre Ecken nicht abgerundet werden:

    • Alphaschichtung pro Pixel
    • Fensterbereiche

    Eine App kann beispielsweise pixelweise Alphaschichtung verwenden, um transparente Pixel um das Hauptfenster zu zeichnen und so einen benutzerdefinierten Schatteneffekt zu erzielen, wodurch das Fenster kein Rechteck mehr darstellt und daher vom System nicht abgerundet werden kann.

Entscheidung für optionale abgerundete Ecken

Wenn die Ecken Ihrer App nicht aufgrund einer Richtlinie abgerundet werden, können Sie optional diese API verwenden, damit Ihre App auf Wunsch abgerundete Ecken verwenden kann. Geben Sie die gewünschte Option für die Eckenabrundung für Ihre App an, indem Sie einen Wert der Enumeration DWM_WINDOW_CORNER_PREFERENCE (siehe folgende Tabelle) an die Funktion DwmSetWindowAttribute übergeben.

Enumerationswert Beschreibung
DWMWCP_DEFAULT Das System entscheidet, ob die Ecken von Fenstern abgerundet werden.
DWMWCP_DONOTROUND Fensterecken werden nie abgerundet.
DWMWCP_ROUND Ecken werden abgerundet, wenn es angemessen ist.
DWMWCP_ROUNDSMALL Ecken werden mit einem kleinen Radius abgerundet, wenn es angemessen ist.

Ein Zeiger auf den entsprechenden Wert aus dieser Enumeration wird an den dritten Parameter von DwmSetWindowAttribute übergeben. Für den zweiten Parameter, der angibt, welches Attribut Sie festlegen, übergeben Sie den Wert DWMWA_WINDOW_CORNER_PREFERENCE, der in der Enumeration DWMWINDOWATTRIBUTE definiert ist.

Für C#-Apps

DwmSetWindowAttribute ist eine native Win32-API. Sie wird nicht direkt für .NET-Code verfügbar gemacht. Sie müssen die P/Invoke-Implementierung Ihrer Sprache verwenden, um die Funktion zu deklarieren (C#-Code finden Sie im folgenden Beispiel). Alle WinForms- und WPF-Standard-Apps erhalten automatisch abgerundete Ecken. Wenn Sie jedoch Ihren Fensterrahmen anpassen oder ein Framework eines Drittanbieters verwenden, müssen Sie sich möglicherweise explizit für abgerundete Ecken entscheiden. Weitere Informationen finden Sie im Abschnitt „Beispiele“.

Beispiele

Die folgenden Beispiele zeigen, wie Sie DwmSetWindowAttribute oder DwmGetWindowAttribute aufrufen können, um die Benutzeroberfläche Ihrer App mit abgerundeten Ecken zu steuern, wenn die Ecken Ihrer App nicht per Richtlinie abgerundet werden.

Hinweis

Die Fehlerbehandlung wurde aus Gründen der Kürze und der Übersichtlichkeit in diesen Beispielen weggelassen.

Beispiel 1: Abrunden der Ecken des Hauptfensters einer App in C# – WPF

In diesem Beispiel wird veranschaulicht, wie Sie „DwmSetWindowAttribute“ in C# unter Verwendung des Attributs [DllImport] aufrufen. Beachten Sie, dass diese Definition für abgerundete Ecken spezifisch ist. Die Funktion „DwmSetWindowAttribute“ ist so konzipiert, dass abhängig von den bereitgestellten Flags unterschiedliche Parameter verwendet werden. Es handelt sich also nicht um eine allgemeingültige Signatur. Das Beispiel enthält auch Kopien der relevanten Enumerationen aus der Headerdatei „dwmapi.h“. Da die Win32-API einen Zeiger für den dritten Parameter verwendet, achten Sie darauf, das ref-Schlüsselwort zu verwenden, damit Sie die Adresse einer Variable übergeben können, wenn Sie die Funktion aufrufen. Sie können dies in Ihrer MainWindow-Klasse in MainWindow.xaml.cs tun.

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

Erstellen Sie als Nächstes in Ihrem MainWindow-Konstruktor nach dem Aufruf von „InitalizeComponent“ eine neue Instanz der WindowInteropHelper-Klasse, um einen Zeiger auf den zugrunde liegenden HWND (Fensterhandle) zu erhalten. Achten Sie darauf, die Methode EnsureHandle zu verwenden, um zu erzwingen, dass das System ein HWND für das Fenster erstellt, bevor es angezeigt wird, da das System dies normalerweise erst nach dem Beenden des Konstruktors tut.

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

Beispiel 2: Abrunden der Ecken des Hauptfensters einer App in C# – WinForms

Wie bei WPF müssen Sie für eine WinForms-App zuerst dwmapi.dll und die DwmSetWindowAttribute-Funktionssignatur mit P/Invoke importieren. Sie können dies in Ihrer primären Form-Klasse tun.

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

Das Aufrufen von DwmSetWindowAttribute ist ebenfalls gleich wie bei einer WPF-App, aber Sie müssen keine Hilfsklasse verwenden, um das HWND abzurufen, da es sich einfach um eine Eigenschaft des Formulars handelt. Rufen Sie es in Ihrem Formularkonstruktor nach dem Aufruf von InitializeComponent auf.

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

Beispiel 3: Abrunden der Ecken des Hauptfensters einer App in C++

Für eine native C++-App können Sie DwmSetWindowAttribute in Ihrer Nachrichtenverarbeitungsfunktion nach der Fenstererstellung aufrufen, um das System aufzufordern, die Fenster Ihrer Anwendung abzurunden.

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

Beispiel 4: Abrunden der Ecken eines Menüs mit einem kleinen Radius – C++

Standardmäßig sind Menüs Popupfenster, die keine abgerundeten Ecken erhalten. Wenn Ihre App ein benutzerdefiniertes Menü erstellt und Sie möchten, dass es der Abrundungsrichtlinie anderer Standardmenüs folgt, können Sie die API aufrufen, um dem System mitzuteilen, dass die Ecken dieses Fensters abgerundet werden sollen, auch wenn dies nicht der Standardrichtlinie für abgerundete Ecken entspricht.

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