共用方式為


在 Windows 11 電腦應用程式中套用圓角

圓角是 Windows 11 Geometry 最明顯的功能。 在 Windows 11 上,系統會為所有收件匣應用程式自動將最上層視窗的角落變圓,包括所有 UWP 應用程式和其他大部分的應用程式。 不過,某些 Win32 應用程式可能無法套用圓角。 本主題會描述若是系統未自動幫 Win32 應用程式的主視窗角落套用圓角時,您該如何套用圓角。

注意

根據設計,應用程式在最大化、貼齊、在虛擬機器 (VM) 中執行、在 Windows 虛擬桌面 (WVD) 上執行或以 Windows Defender 應用程式防護 (WDAG) 視窗執行時不會套用圓角。

Windows 11 圓角記事本應用程式的螢幕擷取畫面。

為什麼我的應用程式沒有套用圓角?

如果您的應用程式主視窗未自動被套用圓角,這是因為您使用了防止框架的方式自訂框架。 從桌面視窗管理員 (DWM) 的觀點來看,應用程式分為三個主要類別:

  1. 依預設套用圓角的應用程式。

    這包括想要有完全由系統提供的框架和字幕控制項的應用程式 (最小值/最大值/關閉按鈕),例如記事本。 它也包括提供足夠資訊給系統使其可以套用圓角的應用程式,例如設定 WS_THICKFRAME和WS_CAPTION 視窗樣式,或提供系統可用來套用圓角的 1 圖元非工作區框線。

  2. 未依原則套用圓角,但可以套用圓角的的應用程式。

    這類的應用程式通常想要自訂大部分的視窗框架,但仍想要系統繪製的框線和陰影,例如 Microsoft Office。 如果您的應用程式未依原則套用圓角,可能是由於:

    • 缺少框架樣式
    • 非工作區空白
    • 其他自訂專案,例如用於自訂陰影的額外非子視窗

    變更其中一個專案將會中斷自動圓角。 雖然我們確實嘗試盡可能使用系統啟發學習法來圓角應用程式,但有些自訂的組合無法預測,因此我們為這些案例提供了手動選擇加入 API。 如果您在應用程式中解決這些問題,或呼叫選擇加入 API,如下一節所述,則系統可能會在您的應用程式的視窗套用圓角。 不過請注意,API 是系統的提示,並不保證會根據自訂專案套用圓角。

  3. 即使呼叫選擇加入 API,也無法套用圓角的應用程式。

    這些應用程式沒有框架或框線,而且通常具有高度自訂的 UI。 如果您的應用程式正在執行下列其中一項動作,就無法套用圓角:

    • 每個像素 Alpha 分層
    • 視窗區域

    例如,應用程式可能會使用每個像素 Alpha 分層在其主視窗周圍繪製透明像素,以達到自訂陰影效果,這會使得視窗不再是矩形,因此系統無法套用圓角。

如何選擇加入套用圓角

如果您的應用程式未依原則套用圓角,您可以選擇性地使用這些 API,讓您的應用程式選擇加入圓角。 您可以藉由將 DWM_WINDOW_CORNER_PREFERENCE 列舉的值傳遞至 DwmSetWindowAttribute 函式,以指定應用程式想要的套用圓角的選項。

列舉值 描述
DWMWCP_DEFAULT 讓系統決定是否要為視窗套用圓角。
DWMWCP_DONOTROUND 永遠不要套用圓角。
DWMWCP_ROUND 如果適當的話,套用圓角。
DWMWCP_ROUNDSMALL 如果適當且半徑很小,請套用圓角。

這個列舉中適當值的指標會傳遞至 DwmSetWindowAttribute 的第三個參數。 針對第二個參數,指定您要設定的屬性,傳遞 DWMWINDOWATTRIBUTE 列舉中定義的 DWMWA_WINDOW_CORNER_PREFERENCE 值。

針對 C# 應用程式

DwmSetWindowAttribute 是原生 WIN32 API,不會直接公開至 .NET 程式碼。 您必須使用語言的 P/Invoke 實作來宣告函式 (下列範例中會提供 C# 程式碼)。 所有標準 WinForms 和 WPF 應用程式都會自動套用圓角,但如果您自訂視窗框架或使用協力廠商架構,您可能需要選擇加入圓角。 如需進一步的詳細資料,請參閱 [範例] 章節。

範例

下列範例示範如何呼叫 DwmSetWindowAttributeDwmGetWindowAttribute,以在應用程式根據原則套用圓角時,控制應用程式的圓角體驗。

注意

這些範例已排除錯誤處理,以求簡潔明瞭。

範例 1 - 在 C# 應用程式的主要視窗中套用圓角 - WPF

此範例會示範如何使用 [DllImport] 屬性,從 C# 呼叫 DwmSetWindowAttribute。 請注意,此定義專屬於圓角;DwmSetWindowAttribute 函式的設計目的是根據提供的旗標採用不同的參數,因此這不是一般用途的簽章。 此範例也包含 dwmapi.h 標頭檔中相關列舉的複本。 因為 WIN32 API 會採用第三個參數的指標,所以請務必使用 ref 關鍵字,以便在呼叫函式時傳遞變數的位址。 您可以在 MainWindow.xaml.cs 的 MainWindow 類別中執行此動作。

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

接下來,在 MainWindow 建構函式中,在呼叫 InitializeComponent 之後,建立 WindowInteropHelper 類別的新執行個體,以取得基礎 HWND 的指標 (視窗控制碼)。 請務必使用 EnsureHandle 方法來強制系統在顯示視窗之前建立 HWND,因為通常系統只會在結束建構函式之後執行此動作。

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

範例 2 - 在 C# 應用程式的主要視窗中套用圓角 - WinForms

如同 WPF,針對 WinForms 應用程式,您必須先使用 P/Invoke 匯入 dwmapi.dll 和 DwmSetWindowAttribute 函式簽章。 您可以在主要 Form 類別中執行此動作。

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

呼叫 DwmSetWindowAttribute 與 WPF 應用程式相同,但您不需要使用協助程式類別來取得 HWND,因為它只是 Form 的屬性。 在呼叫 InitializeComponent 之後,從 Form 建構函式內呼叫它。

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

範例 3 - 在 C# 應用程式的主要視窗中套用圓角

針對原生 C++ 應用程式,您可以在視窗建立之後,在訊息處理函式中呼叫 DwmSetWindowAttribute,以要求系統套用圓角。

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

範例 4 – 幫小半徑的功能表套用圓角 - C++

根據預設,功能表是快顯視窗,不會套用圓角。 如果您的應用程式建立自訂功能表,而且您想要它遵循其他標準功能表的圓角原則,您可以呼叫 API,讓系統知道,即使它似乎不符合預設圓角原則,此視窗仍應該套用圓角。

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