Применение скругленных углов в классических приложениях для Windows 11
Скругленные углы — это самая заметная особенность в геометрическом оформлении интерфейса Windows 11. В Windows 11 система автоматически скругляет углы окон верхнего уровня для всех предустановленных приложений, включая все приложения UWP, и большинства других приложений. Но в некоторых приложениях Win32 окна могут не скругляться. В этой статье объясняется, как применить скругление углов главного окна приложения Win32, если система не делает это автоматически.
Примечание.
По умолчанию скругление в приложениях не применяется при максимальном развертывании и прикреплении окон, а также при работе на виртуальной машине, в Виртуальном рабочем столе Windows (WVD) и в окне Application Guard в Windows Defender (WDAG).
Почему в моем приложении не скруглены углы?
Если углы основного окна вашего приложения не скругляются автоматически, это связано с тем, что настроенные вами параметры рамки препятствуют скруглению. В контексте диспетчера окон рабочего стола (DWM), приложения делятся на три основные категории:
Приложения, в которых скругление применяется по умолчанию.
К ним относятся приложения, которым требуется полноценная предоставленная системой рамка и элементы управления заголовками (кнопки свертывания, развертывания и закрытия), например "Блокнот". Сюда также входят приложения, которые предоставляют системе достаточно информации, чтобы она могла правильно скруглить углы, например задать стили окна WS_THICKFRAME и WS_CAPTION или предоставить границу неклиентской области шириной в 1 пиксель, которую система может использовать для скругления углов.
Приложения, в которых скругление не применено в соответствии с политикой, но в которых это можно сделать.
Приложения этой категории обычно самостоятельно настраивают большую часть рамки окна, но им по-прежнему требуется отображаемая системой граница и тень (например, Microsoft Office). Если в вашем приложении скругление не применяется в соответствии с политикой, это может быть вызвано одной из следующих причин:
- нехватка стилей рамок;
- пустая область, не относящаяся к клиенту;
- другие настройки, такие как дополнительные окна, не являющиеся дочерними элементами и используемые для настраиваемых теней.
Изменение в одном из этих пунктов приведет к нарушению автоматического скругления. Хотя мы старались скруглить углы как можно большего количества приложений с помощью нашей системной эвристики, есть некоторые комбинации настроек, которые мы не можем предсказать. Для таких случаев мы предусмотрели API, позволяющий предоставить согласие вручную. Если вы решите эти проблемы в своем приложении или вызовете API подписки, описанный в следующем разделе, система может применить скругление для вашего приложения. Но обратите внимание, что API является указанием для системы и не гарантирует скругление в зависимости от настроек.
Приложения, для которых нельзя применить скругление, даже если они вызывают API предоставления согласия.
У таких приложений нет рамки или границ, а их пользовательский интерфейс обычно имеет широкие возможности настройки. Если в приложении выполняется одно из следующих действий, в нем нельзя применить скругление:
- альфа-наложение для каждого пикселя;
- применение областей окон.
Например, приложение может размещать прозрачные пиксели вокруг главного окна путем альфа-наложения для каждого пикселя для достижения эффекта настраиваемой тени. В результате окно теряет прямоугольную форму и система не может скруглить углы.
Как предоставить согласие на скругление углов
Если приложение не округляется по политике, вы можете использовать эти API, чтобы разрешить приложению обходить круглые углы. Вы указываете параметр округления угла, который требуется для приложения, передав значение перечисления DWM_WINDOW_CORNER_PREFERENCE (показанное в следующей таблице) в функцию DwmSetWindowAttribute.
Значение перечисления | Description |
---|---|
DWMWCP_DEFAULT | Система определяет, следует ли скруглять углы окон. |
DWMWCP_DONOTROUND | Скругление не применяется. |
DWMWCP_ROUND | Скругление применяется, если это уместно. |
DWMWCP_ROUNDSMALL | Скругление с небольшим радиусом применяется, если это уместно. |
Указатель на соответствующее значение из этой перечисления передается третьему параметру DwmSetWindowAttribute. Для второго параметра, указывающего, какой атрибут задан, передайте значение DWMWA_WINDOW_CORNER_PREFERENCE, определенное в перечислении DWMWINDOWATTRIBUTE.
Приложения C#
DwmSetWindowAttribute — это собственный API Win32, который не предоставляется непосредственно коду .NET. Для объявления функции необходимо использовать реализацию языка P/Invoke (код C#, приведенный в приведенном ниже примере). Во всех стандартных приложениях WinForms и WPF углы скругляются автоматически. Но если вы настраиваете рамку окна или используете стороннюю платформу, вам может потребоваться предоставить согласие на применение скругления углов. Дополнительные сведения см. в разделе с примерами.
Примеры
В приведенных ниже примерах показано, как вызвать DwmSetWindowAttribute или DwmGetWindowAttribute, чтобы управлять скруглением углов в приложении, если скругление не применяется в соответствии с политикой.
Примечание.
Для краткости и ясности в этих примерах обработка ошибок исключена.
Пример 1. Скругление углов главного окна приложения в C# — WPF
В этом примере показано, как вызвать DwmSetWindowAttribute из C# с помощью атрибута [DllImport]. Обратите внимание, что это определение относится к скругленным углам. Функция DwmSetWindowAttribute предназначена для приема различных параметров в зависимости от предоставленных флагов, поэтому это не сигнатура общего назначения. Пример также включает копии соответствующих перечислений из заголовочного файла dwmapi.h. Так как API Win32 принимает указатель на третий параметр, обязательно используйте ключевое слово ref, чтобы можно было передать адрес переменной при вызове функции. Это можно сделать в классе MainWindow в 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
// ...
}
Затем в конструкторе 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 сначала необходимо импортировать dwmapi.dll и сигнатуру функции DwmSetWindowAttribute с использованием P/Invoke. Это можно сделать в основном классе 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. Вызовите эту функцию из конструктора Form после вызова 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
// ...
}
Пример 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. Скругление углов меню с небольшим радиусом — С++
По умолчанию меню — это всплывающие окна, углы которых не скругляются. Если приложение создает пользовательское меню и вы хотите, чтобы оно соответствовало политике скругления для других стандартных меню, можно вызвать 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;
}
Windows developer