在 Windows 11 電腦應用程式中套用圓角
圓角是 Windows 11 Geometry 最明顯的功能。 在 Windows 11 上,系統會為所有收件匣應用程式自動將最上層視窗的角落變圓,包括所有 UWP 應用程式和其他大部分的應用程式。 不過,某些 Win32 應用程式可能無法套用圓角。 本主題會描述若是系統未自動幫 Win32 應用程式的主視窗角落套用圓角時,您該如何套用圓角。
注意
根據設計,應用程式在最大化、貼齊、在虛擬機器 (VM) 中執行、在 Windows 虛擬桌面 (WVD) 上執行或以 Windows Defender 應用程式防護 (WDAG) 視窗執行時不會套用圓角。
為什麼我的應用程式沒有套用圓角?
如果您的應用程式主視窗未自動被套用圓角,這是因為您使用了防止框架的方式自訂框架。 從桌面視窗管理員 (DWM) 的觀點來看,應用程式分為三個主要類別:
依預設套用圓角的應用程式。
這包括想要有完全由系統提供的框架和字幕控制項的應用程式 (最小值/最大值/關閉按鈕),例如記事本。 它也包括提供足夠資訊給系統使其可以套用圓角的應用程式,例如設定 WS_THICKFRAME和WS_CAPTION 視窗樣式,或提供系統可用來套用圓角的 1 圖元非工作區框線。
未依原則套用圓角,但可以套用圓角的的應用程式。
這類的應用程式通常想要自訂大部分的視窗框架,但仍想要系統繪製的框線和陰影,例如 Microsoft Office。 如果您的應用程式未依原則套用圓角,可能是由於:
- 缺少框架樣式
- 非工作區空白
- 其他自訂專案,例如用於自訂陰影的額外非子視窗
變更其中一個專案將會中斷自動圓角。 雖然我們確實嘗試盡可能使用系統啟發學習法來圓角應用程式,但有些自訂的組合無法預測,因此我們為這些案例提供了手動選擇加入 API。 如果您在應用程式中解決這些問題,或呼叫選擇加入 API,如下一節所述,則系統可能會在您的應用程式的視窗套用圓角。 不過請注意,API 是系統的提示,並不保證會根據自訂專案套用圓角。
即使呼叫選擇加入 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 應用程式都會自動套用圓角,但如果您自訂視窗框架或使用協力廠商架構,您可能需要選擇加入圓角。 如需進一步的詳細資料,請參閱 [範例] 章節。
範例
下列範例示範如何呼叫 DwmSetWindowAttribute 或 DwmGetWindowAttribute,以在應用程式根據原則套用圓角時,控制應用程式的圓角體驗。
注意
這些範例已排除錯誤處理,以求簡潔明瞭。
範例 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;
}