Другое

Как убрать острые края в заголовке окна Avalonia UI

Узнайте, как исправить острые края в вашем пользовательском заголовке окна Avalonia UI. Полное руководство с примерами кода и правильной конфигурацией окна для профессионального вида скругленных углов.

Как убрать острые края из пользовательского TitleBar в Avalonia UI?

Я разрабатываю приложение с использованием Avalonia UI и столкнулся с трудностями при удалении острых прозрачных краев из моего пользовательского заголовка окна. Я хочу создать пользовательский заголовок окна, который будет функционировать аналогично стандартному, но я не могу избавиться от этих острых краев.

Вот моя текущая реализация:

xml
<Window xmlns="https://github.com/avaloniaui"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:CrossUtilities.MVVM.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    x:Class="CrossUtilities.MVVM.Views.ShellWindow.ShellWindow"
    x:DataType="vm:ShellWindowViewModel"
    Icon="/Assets/avalonia-logo.ico"
    CanResize="True"
    ExtendClientAreaToDecorationsHint="False"
    ExtendClientAreaChromeHints="NoChrome"
    TransparencyLevelHint="AcrylicBlur"
    WindowStartupLocation="CenterScreen"
    Background="Transparent"
    TransparencyBackgroundFallback="Transparent"
    ExtendClientAreaTitleBarHeightHint="-1"
    SystemDecorations="None"
    BorderBrush="Transparent"
    BorderThickness="0">

<Design.DataContext>
    <vm:ShellWindowViewModel/>
</Design.DataContext>

<Border Background="White"  CornerRadius="20" ClipToBounds="True" PointerPressed="TitleBar_PointerPressed">
    <Grid x:Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="70"/>
            <RowDefinition Height="70"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="149"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Grid Grid.Row="0" Grid.ColumnSpan="5">
            <Border Background="White" BorderThickness="0" BorderBrush="Transparent" CornerRadius="15">
                <Grid x:Name="Header" Background="Transparent">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="70"/>
                    </Grid.RowDefinitions>

                    <Border Grid.Row="0" Background="#FFCC00" CornerRadius="15,15,0,0" ClipToBounds="True" >
                        <Grid x:Name="HeaderGrid" Background="Transparent" Margin="0,5,10,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>

                            <Grid Grid.Column="1">
                                <Label Content="Hello" HorizontalAlignment="Right" VerticalAlignment="Center"/>
                            </Grid>

                            <Grid Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Center">
                                <TextBlock Text="-" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>

                            <Grid Grid.Column="3" VerticalAlignment="Center" HorizontalAlignment="Center">
                                <TextBlock Text="▢" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>

                            <Grid Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Center">
                                <TextBlock Text="X" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>

                        </Grid>
                    </Border>
                </Grid>
            </Border>
        </Grid>
    </Grid>
</Border>

Что я делаю не так в этой реализации, что приводит к появлению острых краев? Как правильно создать пользовательский заголовок окна со скругленными углами в Avalonia UI?

Содержание

Понимание проблемы

Острые углы, которые вы наблюдаете, возникают потому, что системное оформление окна все еще присутствует, в то время как ваш пользовательский заголовок окна не правильно обрезает фон окна. При использовании SystemDecorations="None" вам нужно самостоятельно обрабатывать визуальное оформление окна, включая скругленные углы, которые предоставляет современная Windows.

Согласно документации Avalonia Border, свойство CornerRadius можно установить для создания скругленных углов, но необходимо обеспечить правильную обрезку, чтобы предотвратить отображение острых углов системы по умолчанию.

Правильная конфигурация окна

Ваше окно требует определенных свойств для правильной обработки пользовательских заголовков окон и прозрачности:

xml
<Window xmlns="https://github.com/avaloniaui"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:CrossUtilities.MVVM.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
    x:Class="CrossUtilities.MVVM.Views.ShellWindow.ShellWindow"
    x:DataType="vm:ShellWindowViewModel"
    Icon="/Assets/avalonia-logo.ico"
    CanResize="True"
    ExtendClientAreaToDecorationsHint="True" <!-- Изменено на True -->
    ExtendClientAreaChromeHints="NoChrome"
    TransparencyLevelHint="AcrylicBlur"
    WindowStartupLocation="CenterScreen"
    Background="Transparent"
    TransparencyBackgroundFallback="Transparent"
    ExtendClientAreaTitleBarHeightHint="70" <!-- Установите высоту вашего заголовка окна -->
    SystemDecorations="None"
    BorderBrush="Transparent"
    BorderThickness="0"
    UseLayoutRounding="True">

Ключевые изменения:

  • ExtendClientAreaToDecorationsHint="True" - Это расширяет область содержимого, включая область заголовка окна
  • ExtendClientAreaTitleBarHeightHint="70" - Установите это значение, соответствующее высоте вашего заголовка окна
  • UseLayoutRounding="True" - Обеспечивает четкое отображение скругленных углов

Правильная реализация границы

Проблема с вашей текущей структурой Border заключается в наличии нескольких вложенных Border с разными радиусами углов, что может вызывать конфликты при отрисовке. Вот исправленный подход:

xml
<Border Background="White" CornerRadius="20" ClipToBounds="True">
    <Grid x:Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="70"/> <!-- Высота заголовка окна -->
            <RowDefinition Height="*"/> <!-- Область содержимого -->
        </Grid.RowDefinitions>
        
        <!-- Содержимое заголовка окна -->
        <Border Grid.Row="0" Background="#FFCC00" CornerRadius="20,20,0,0" ClipToBounds="True">
            <Grid x:Name="HeaderGrid">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>

                <!-- Заголовок окна -->
                <TextBlock Grid.Column="0" 
                           Text="Мое приложение" 
                           VerticalAlignment="Center" 
                           Margin="20,0"
                           FontSize="14"
                           FontWeight="Medium"/>

                <!-- Кнопка сворачивания -->
                <Button Grid.Column="1" 
                        Click="Minimize_Click"
                        Width="46" Height="32"
                        Background="Transparent"
                        BorderThickness="0"
                        Margin="0,0,5,0">
                    <TextBlock Text="─" FontSize="14" FontWeight="Bold"/>
                </Button>

                <!-- Кнопка разворачивания/восстановления -->
                <Button Grid.Column="2" 
                        Click="Maximize_Click"
                        Width="46" Height="32"
                        Background="Transparent"
                        BorderThickness="0"
                        Margin="0,0,5,0">
                    <TextBlock Text="▢" FontSize="14" FontWeight="Bold"/>
                </Button>

                <!-- Кнопка закрытия -->
                <Button Grid.Column="3" 
                        Click="Close_Click"
                        Width="46" Height="32"
                        Background="Transparent"
                        BorderThickness="0">
                    <TextBlock Text="✕" FontSize="14" FontWeight="Bold"/>
                </Button>
            </Grid>
        </Border>

        <!-- Основная область содержимого -->
<Grid Grid.Row="1">
    <!-- Содержимое вашего приложения здесь -->
</Grid>

Полный рабочий пример

Вот полная реализация, которая решает проблему острых углов:

xml
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:CrossUtilities.MVVM.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
        x:Class="CrossUtilities.MVVM.Views.ShellWindow.ShellWindow"
        x:DataType="vm:ShellWindowViewModel"
        Icon="/Assets/avalonia-logo.ico"
        CanResize="True"
        ExtendClientAreaToDecorationsHint="True"
        ExtendClientAreaChromeHints="NoChrome"
        TransparencyLevelHint="AcrylicBlur"
        WindowStartupLocation="CenterScreen"
        Background="Transparent"
        TransparencyBackgroundFallback="Transparent"
        ExtendClientAreaTitleBarHeightHint="70"
        SystemDecorations="None"
        BorderBrush="Transparent"
        BorderThickness="0"
        UseLayoutRounding="True"
        WindowState="Normal">

    <Design.DataContext>
        <vm:ShellWindowViewModel/>
    </Design.DataContext>

    <!-- Основная граница со скругленными углами -->
    <Border Background="White" 
            CornerRadius="20" 
            ClipToBounds="True"
            BoxShadow="0 4 20 0 rgba(0,0,0,0.1)">
        
        <Grid x:Name="MainGrid">
            <Grid.RowDefinitions>
                <RowDefinition Height="70"/> <!-- Высота заголовка окна -->
                <RowDefinition Height="*"/> <!-- Область содержимого -->
            </Grid.RowDefinitions>
            
            <!-- Заголовок окна -->
            <Border Grid.Row="0" 
                    Background="#FFCC00" 
                    CornerRadius="20,20,0,0" 
                    ClipToBounds="True">
                
                <Grid x:Name="HeaderGrid">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>

                    <!-- Заголовок окна -->
                    <TextBlock Grid.Column="0" 
                               Text="Мое пользовательское приложение" 
                               VerticalAlignment="Center" 
                               Margin="20,0"
                               FontSize="14"
                               FontWeight="Medium"/>

                    <!-- Кнопка сворачивания -->
                    <Button Grid.Column="1" 
                            Click="Minimize_Click"
                            Width="46" Height="32"
                            Background="Transparent"
                            BorderThickness="0"
                            Margin="0,0,5,0"
                            Classes="titlebar-button">
                        <TextBlock Text="─" FontSize="14" FontWeight="Bold"/>
                    </Button>

                    <!-- Кнопка разворачивания -->
                    <Button Grid.Column="2" 
                            Click="Maximize_Click"
                            Width="46" Height="32"
                            Background="Transparent"
                            BorderThickness="0"
                            Margin="0,0,5,0"
                            Classes="titlebar-button">
                        <TextBlock Text="▢" FontSize="14" FontWeight="Bold"/>
                    </Button>

                    <!-- Кнопка закрытия -->
                    <Button Grid.Column="3" 
                            Click="Close_Click"
                            Width="46" Height="32"
                            Background="Transparent"
                            BorderThickness="0"
                            Classes="titlebar-button close-button">
                        <TextBlock Text="✕" FontSize="14" FontWeight="Bold"/>
                    </Button>
                </Grid>
            </Border>

            <!-- Основное содержимое -->
            <Grid Grid.Row="1" Background="#F5F5F5">
                <!-- Содержимое вашего приложения здесь -->
                <TextBlock Text="Основная область содержимого" 
                           HorizontalAlignment="Center" 
                           VerticalAlignment="Center"
                           FontSize="24"/>
            </Grid>
        </Grid>
    </Border>

    <!-- Поведение перетаскивания окна -->
    <Border Background="Transparent"
            PointerPressed="TitleBar_PointerPressed"
            Grid.Row="0"
            Grid.ColumnSpan="4"
            Height="70"
            VerticalAlignment="Top"/>
</Window>

Дополнительные советы для пользовательских заголовков окон

  1. Используйте ClipToBounds: Всегда устанавливайте ClipToBounds="True" на основном Border, чтобы предотвратить выход содержимого за пределы скругленных углов.

  2. Обрабатывайте операции с окном: Реализуйте перетаскивание окна и действия кнопок:

csharp
private void TitleBar_PointerPressed(object sender, PointerPressedEventArgs e)
{
    if (e.ClickCount == 1 && e.GetCurrentPoint(null).Properties.IsLeftButtonPressed)
    {
        BeginMoveDrag(e);
    }
}

private void Minimize_Click(object sender, RoutedEventArgs e)
{
    WindowState = WindowState.Minimized;
}

private void Maximize_Click(object sender, RoutedEventArgs e)
{
    WindowState = WindowState == WindowState.Maximized ? 
        WindowState.Normal : WindowState.Maximized;
}

private void Close_Click(object sender, RoutedEventArgs e)
{
    Close();
}
  1. Добавьте визуальную обратную связь: Стилизуйте кнопки заголовка окна с эффектами при наведении:
xml
<Style Selector="Button.titlebar-button">
    <Setter Property="CornerRadius" Value="5"/>
    <Setter Property="Template">
        <ControlTemplate>
            <Border Background="{TemplateBinding Background}"
                    CornerRadius="{TemplateBinding CornerRadius}"
                    BoxShadow="0 2 4 0 rgba(0,0,0,0.1)">
                <ContentPresenter Content="{TemplateBinding Content}"
                                  Margin="0"
                                  VerticalAlignment="Center"
                                  HorizontalAlignment="Center"/>
            </Border>
        </ControlTemplate>
    </Setter>
</Style>

<Style Selector="Button.titlebar-button:pointerover">
    <Setter Property="Background" Value="#FFD700"/>
</Style>

<Style Selector="Button.close-button:pointerover">
    <Setter Property="Background" Value="#FF5252"/>
</Style>
  1. Учитывайте регион окна: Для идеальных скругленных углов может потребоваться программно установить регион окна:
csharp
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
    base.OnAttachedToVisualTree(e);
    
    // Установка региона окна для идеальных скругленных углов
    var radius = 20;
    var path = new PathGeometry();
    path.AddRoundedRectangle(new Rect(0, 0, Bounds.Width, Bounds.Height), 
                           new CornerRadius(radius));
    
    PlatformImpl?.SetWindowClip(path);
}

Ключ к удалению острых углов из вашего пользовательского заголовка окна - это правильная конфигурация окна с ExtendClientAreaToDecorationsHint="True", использование ClipToBounds для предотвращения переполнения содержимого и поддержание согласованных радиусов углов во всей структуре границы.

Источники

  1. Stack Overflow - Как удалить острые края пользовательского заголовка окна в Avalonia UI?
  2. Документация Avalonia - Элемент Border
  3. GitHub - Avalonia-CustomTitleBarTemplate
  4. Обсуждение GitHub - Настраиваемый заголовок окна
  5. GitHub Issue - Скругленный радиус угла границы обрезает контур границы

Заключение

Чтобы успешно удалить острые углы из вашего пользовательского заголовка окна в Avalonia UI:

  • Установите ExtendClientAreaToDecorationsHint="True", чтобы расширить область содержимого до области заголовка окна
  • Используйте согласованные значения CornerRadius и ClipToBounds="True" на основном Border
  • Настройте ExtendClientAreaTitleBarHeightHint в соответствии с высотой вашего заголовка окна
  • Реализуйте правильные обработчики операций с окном для сворачивания, разворачивания и закрытия
  • Добавьте визуальную обратную связь для лучшего пользовательского опыта
  • Рассмотрите возможность программной установки региона окна для идеальных скругленных углов

Эти настройки устранят острые углы и дадут вам профессиональный вид пользовательского заголовка окна с правильными скругленными углами, который бесшовно интегрируется с рабочим столом Windows.

Авторы
Проверено модерацией
Модерация