Bagikan melalui


Menerapkan sudut bulat di aplikasi desktop untuk Windows 11

Sudut bulat adalah fitur geometri Windows 11 yang paling segera terlihat. Pada Windows 11, sistem secara otomatis membulatkan sudut jendela tingkat atas untuk semua aplikasi kotak masuk, termasuk semua aplikasi UWP, dan sebagian besar aplikasi lainnya. Namun, beberapa aplikasi Win32 mungkin tidak dibulatkan. Topik ini menjelaskan cara membulatkan sudut jendela utama aplikasi Win32 Anda jika sistem tidak membulatkannya secara otomatis.

Catatan

Secara desain, aplikasi tidak dibulatkan saat dimaksimalkan, di-snap, berjalan di Komputer Virtual (VM), berjalan pada Windows Virtual Desktop (WVD), atau berjalan sebagai jendela Windows Defender Application Guard (WDAG).

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

Mengapa aplikasi saya tidak dibulatkan?

Jika jendela utama aplikasi Anda tidak menerima pembulatan otomatis, itu karena Anda telah menyesuaikan bingkai dengan cara yang mencegahnya. Aplikasi termasuk dalam tiga kategori utama dari perspektif Desktop Window Manager (DWM):

  1. Aplikasi yang dibulatkan secara default.

    Ini termasuk aplikasi yang menginginkan bingkai lengkap yang disediakan sistem dan kontrol keterangan (tombol min/maks/tutup), seperti Notepad. Ini juga mencakup aplikasi yang memberikan informasi yang cukup kepada sistem sehingga dapat membulatkannya dengan benar, seperti mengatur gaya jendela WS_THICKFRAME dan WS_CAPTION atau menyediakan batas area non-klien 1 piksel yang dapat digunakan sistem untuk membulatkan sudut.

  2. Aplikasi yang tidak dibulatkan oleh kebijakan, tetapi dapat dibulatkan.

    Aplikasi dalam kategori ini umumnya ingin mengkustomisasi sebagian besar bingkai jendela tetapi masih menginginkan batas dan bayangan yang digambar sistem, seperti Microsoft Office. Jika aplikasi Anda tidak dibulatkan oleh kebijakan, itu bisa disebabkan oleh salah satu hal berikut:

    • Kurangnya gaya bingkai
    • Area non-klien kosong
    • Penyesuaian lainnya, seperti jendela non-anak tambahan yang digunakan untuk bayangan kustom

    Mengubah salah satu hal ini akan memecah pembulatan otomatis. Meskipun kami mencoba membulatkan aplikasi sebanyak mungkin dengan heuristik sistem kami, ada beberapa kombinasi penyesuaian yang tidak dapat kami prediksi sehingga kami menyediakan API keikutsertaan manual untuk kasus-kasus tersebut. Jika Anda mengatasi masalah ini di aplikasi atau memanggil API keikutsertaan, dijelaskan di bagian berikut, sistem dapat membulatkan jendela aplikasi Anda. Namun, perhatikan bahwa API adalah petunjuk untuk sistem dan tidak menjamin pembulatan, tergantung pada kustomisasi.

  3. Aplikasi yang tidak pernah dapat dibulatkan, bahkan jika mereka memanggil API keikutsertaan.

    Aplikasi ini tidak memiliki bingkai atau batas, dan biasanya memiliki UI yang sangat disesuaikan. Jika aplikasi Anda melakukan salah satu hal berikut, aplikasi tersebut tidak dapat dibulatkan:

    • Lapisan alfa per piksel
    • Wilayah jendela

    Misalnya, aplikasi mungkin menggunakan lapisan alfa per piksel untuk menggambar piksel transparan di sekitar jendela utamanya untuk mencapai efek bayangan kustom, yang membuat jendela tidak lagi persegi panjang dan oleh karena itu sistem tidak dapat membulatkannya.

Cara ikut serta ke sudut bulat

Jika aplikasi Anda tidak dibulatkan oleh kebijakan, Anda dapat secara opsional menggunakan API ini untuk memungkinkan aplikasi Anda ikut serta ke sudut bulat. Anda menentukan opsi pembulatan sudut yang Anda inginkan untuk aplikasi Anda dengan meneruskan nilai enumerasi DWM_WINDOW_CORNER_PREFERENCE (ditampilkan dalam tabel berikut) ke fungsi DwmSetWindowAttribute.

Nilai enum Deskripsi
DWMWCP_DEFAULT Biarkan sistem memutuskan apakah akan membulatkan sudut jendela atau tidak.
DWMWCP_DONOTROUND Jangan pernah sudut jendela bulat.
DWMWCP_ROUND Bulatkan sudut jika sesuai.
DWMWCP_ROUNDSMALL Bulatkan sudut jika sesuai, dengan radius kecil.

Penunjuk ke nilai yang sesuai dari enum ini diteruskan ke parameter ketiga DwmSetWindowAttribute. Untuk parameter kedua, yang menentukan atribut mana yang Anda atur, berikan nilai DWMWA_WINDOW_CORNER_PREFERENCE yang ditentukan dalam enumerasi DWMWINDOWATTRIBUTE.

Untuk aplikasi C#

DwmSetWindowAttribute adalah API Win32 asli dan tidak diekspos langsung ke kode .NET. Anda harus menggunakan implementasi P/Invoke bahasa Anda untuk mendeklarasikan fungsi (kode C# diberikan dalam contoh di bawah). Semua aplikasi WinForms dan WPF standar dibulatkan secara otomatis, tetapi jika Anda menyesuaikan bingkai jendela atau menggunakan kerangka kerja pihak ketiga, Anda mungkin perlu ikut serta ke sudut bulat. Lihat bagian Contoh untuk detail lebih lanjut.

Contoh

Contoh berikut menunjukkan bagaimana Anda dapat memanggil DwmSetWindowAttribute atau DwmGetWindowAttribute untuk mengontrol pengalaman pembulatan aplikasi Anda jika aplikasi Anda tidak dibulatkan oleh kebijakan.

Catatan

Penanganan kesalahan telah dibiarkan dari contoh-contoh ini untuk keringkasan dan kejelasan.

Contoh 1 - Membulatkan jendela utama aplikasi di C# - WPF

Contoh ini menunjukkan cara memanggil DwmSetWindowAttribute dari C# dengan menggunakan atribut [DllImport ]. Perhatikan bahwa definisi ini khusus untuk sudut bulat; Fungsi DwmSetWindowAttribute dirancang untuk mengambil parameter yang berbeda tergantung pada bendera yang disediakan, jadi ini bukan tanda tangan tujuan umum. Contohnya juga mencakup salinan enum yang relevan dari file header dwmapi.h. Karena API Win32 mengambil penunjuk untuk parameter ketiga, pastikan untuk menggunakan kata kunci ref sehingga Anda dapat meneruskan alamat variabel saat Anda memanggil fungsi. Anda dapat melakukan ini di kelas MainWindow Anda di 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
    // ...
}

Selanjutnya, di konstruktor MainWindow Anda, setelah panggilan ke InitializeComponent, buat instans baru kelas WindowInteropHelper untuk memperoleh pointer ke HWND (handel jendela) yang mendasar. Pastikan untuk menggunakan metode EnsureHandle untuk memaksa sistem membuat HWND untuk jendela sebelum ditampilkan, karena biasanya sistem hanya melakukannya setelah keluar dari konstruktor.

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

Contoh 2 - Membulatkan jendela utama aplikasi di C# - WinForms

Seperti halnya WPF, untuk aplikasi WinForms Anda harus terlebih dahulu mengimpor dwmapi.dll dan tanda tangan fungsi DwmSetWindowAttribute dengan P/Invoke. Anda dapat melakukan ini di kelas Formulir utama Anda.

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

Memanggil DwmSetWindowAttribute juga sama dengan aplikasi WPF, tetapi Anda tidak perlu menggunakan kelas pembantu untuk mendapatkan HWND karena itu hanya properti Formulir. Panggil dari dalam konstruktor Formulir Anda, setelah panggilan ke 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
    // ...
}

Contoh 3 - Membulatkan jendela utama aplikasi di C++

Untuk aplikasi C++ asli, Anda dapat memanggil DwmSetWindowAttribute dalam fungsi pemrosesan pesan Anda setelah pembuatan jendela untuk meminta sistem membulatkan Anda.

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

Contoh 4 - Membulatkan sudut menu dengan radius kecil - C++

Secara default, menu adalah jendela pop-up, yang tidak dibulatkan. Jika aplikasi Anda membuat menu kustom dan Anda ingin mengikuti kebijakan pembulatan menu standar lainnya, Anda dapat memanggil API untuk memberi tahu sistem bahwa jendela ini harus dibulatkan, meskipun tampaknya tidak cocok dengan kebijakan pembulatan default.

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