Browse Source

Change visual brush for group box to grid (#20947)

* change visual brush for group box to grid

* remove unused usings

* Addressed review

* cleanup
pull/20953/head
Emmanuel Hansen 4 days ago
committed by GitHub
parent
commit
3f00647223
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      src/Avalonia.Base/Rect.cs
  2. 126
      src/Avalonia.Controls/Converters/BorderGapMaskConverter.cs
  3. 108
      src/Avalonia.Themes.Fluent/Controls/GroupBox.xaml
  4. 106
      src/Avalonia.Themes.Simple/Controls/GroupBox.xaml

1
src/Avalonia.Base/Rect.cs

@ -1,7 +1,6 @@
using System;
using System.Globalization;
using System.Numerics;
using Avalonia.Animation.Animators;
using Avalonia.Utilities;
namespace Avalonia

126
src/Avalonia.Controls/Converters/BorderGapMaskConverter.cs

@ -0,0 +1,126 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Controls.Shapes;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace Avalonia.Controls.Converters
{
// Ported from https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/BorderGapMaskConverter.cs
/// <summary>
/// Converter that generates the visual brush for <see cref="GroupBox"/>
/// </summary>
public class BorderGapMaskConverter : IMultiValueConverter
{
/// <summary>
/// Convert a value.
/// </summary>
/// <param name="values">values as produced by source binding</param>
/// <param name="targetType">target type</param>
/// <param name="parameter">converter parameter</param>
/// <param name="culture">culture information</param>
/// <returns>
/// Converted value.
/// Visual Brush that is used as the opacity mask for the Border
/// in the style for GroupBox.
/// </returns>
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
//
// Parameter Validation
//
if (parameter == null ||
values == null ||
values.Count != 3 ||
values[0] is not double ||
values[1] is not double ||
values[2] is not double)
{
return AvaloniaProperty.UnsetValue;
}
if (parameter is not double && parameter is not string)
{
return AvaloniaProperty.UnsetValue;
}
//
// Conversion
//
double? headerWidth = (double?)values[0];
double? borderWidth = (double?)values[1];
double? borderHeight = (double?)values[2];
// Doesn't make sense to have a Grid
// with 0 as width or height
if (borderWidth == 0
|| borderHeight == 0)
{
return null;
}
// Width of the line to the left of the header
// to be used to set the width of the first column of the Grid
double lineWidth;
if (parameter is string)
{
lineWidth = Double.Parse(((string)parameter), NumberFormatInfo.InvariantInfo);
}
else
{
lineWidth = (double)parameter;
}
Grid grid = new Grid
{
Width = borderWidth ?? 0,
Height = borderHeight ?? 0
};
ColumnDefinition colDef1 = new ColumnDefinition();
ColumnDefinition colDef2 = new ColumnDefinition();
ColumnDefinition colDef3 = new ColumnDefinition();
colDef1.Width = new GridLength(lineWidth);
colDef2.Width = new GridLength(headerWidth ?? 0);
colDef3.Width = new GridLength(1, GridUnitType.Star);
grid.ColumnDefinitions.Add(colDef1);
grid.ColumnDefinitions.Add(colDef2);
grid.ColumnDefinitions.Add(colDef3);
RowDefinition rowDef1 = new RowDefinition();
RowDefinition rowDef2 = new RowDefinition();
rowDef1.Height = new GridLength((borderHeight ?? 0) / 2);
rowDef2.Height = new GridLength(1, GridUnitType.Star);
grid.RowDefinitions.Add(rowDef1);
grid.RowDefinitions.Add(rowDef2);
Rectangle rectColumn1 = new Rectangle();
Rectangle rectColumn2 = new Rectangle();
Rectangle rectColumn3 = new Rectangle();
rectColumn1.Fill = Brushes.Black;
rectColumn2.Fill = Brushes.Black;
rectColumn3.Fill = Brushes.Black;
Grid.SetRowSpan(rectColumn1, 2);
Grid.SetRow(rectColumn1, 0);
Grid.SetColumn(rectColumn1, 0);
Grid.SetRow(rectColumn2, 1);
Grid.SetColumn(rectColumn2, 1);
Grid.SetRowSpan(rectColumn3, 2);
Grid.SetRow(rectColumn3, 0);
Grid.SetColumn(rectColumn3, 2);
grid.Children.Add(rectColumn1);
grid.Children.Add(rectColumn2);
grid.Children.Add(rectColumn3);
return (new VisualBrush(grid));
}
}
}

108
src/Avalonia.Themes.Fluent/Controls/GroupBox.xaml

@ -1,11 +1,13 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
x:ClassModifier="internal">
<Design.PreviewWith>
<GroupBox Padding="10" Margin="10">
<TextBlock Text="Hello World" />
</GroupBox>
</Design.PreviewWith>
<converters:BorderGapMaskConverter x:Key="BorderGapMaskConverter" />
<ControlTheme x:Key="{x:Type GroupBox}"
TargetType="GroupBox">
@ -18,62 +20,60 @@
<ControlTemplate>
<Grid x:Name="RootGrid" UseLayoutRounding="true" ColumnDefinitions="6,Auto,*,6" RowDefinitions="Auto,Auto,*,6">
<!-- Separate Border for the background because if the background is set in the Border with the Header the opacity mask will be applied to the background as well. -->
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
Background="{TemplateBinding Background}"/>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
Background="{TemplateBinding Background}"/>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Canvas Background="Transparent">
<Rectangle Fill="Transparent"
Width="{Binding #Header.Bounds.Width}"
Height="{Binding #Header.Bounds.Height}"
Canvas.Left="{Binding #Header.Bounds.X}"
Canvas.Top="{Binding #Header.Bounds.Y}"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border.OpacityMask>
<MultiBinding Converter="{StaticResource BorderGapMaskConverter}"
ConverterParameter="6">
<Binding ElementName="Header"
Path="Bounds.Width"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="Bounds.Width"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="Bounds.Height"/>
</MultiBinding>
</Border.OpacityMask>
</Border>
<!-- ContentPresenter for the header -->
<Border x:Name="Header"
Padding="3,0,3,0"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1">
<ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
FontSize="{DynamicResource GroupBoxHeaderFontSize}"
Foreground="{DynamicResource GroupBoxHeaderForeground}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}" />
</Border>
<!-- Primary content for GroupBox -->
<ContentPresenter Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
<!-- ContentPresenter for the header -->
<Border x:Name="Header"
Padding="3,0,3,0"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1">
<ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
FontSize="{DynamicResource GroupBoxHeaderFontSize}"
Foreground="{DynamicResource GroupBoxHeaderForeground}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Margin="{TemplateBinding Padding}" />
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}" />
</Border>
<!-- Primary content for GroupBox -->
<ContentPresenter Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Margin="{TemplateBinding Padding}" />
</Grid>
</ControlTemplate>
</Setter>
@ -84,7 +84,7 @@
<Style Selector="^ /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="RecognizesAccessKey" Value="True" />
</Style>
</Style>
</ControlTheme>
</ResourceDictionary>

106
src/Avalonia.Themes.Simple/Controls/GroupBox.xaml

@ -1,11 +1,13 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
x:ClassModifier="internal">
<Design.PreviewWith>
<GroupBox Padding="10" Margin="10">
<TextBlock Text="Hello World" />
</GroupBox>
</Design.PreviewWith>
<converters:BorderGapMaskConverter x:Key="BorderGapMaskConverter" />
<ControlTheme x:Key="{x:Type GroupBox}"
TargetType="GroupBox">
@ -16,60 +18,58 @@
<ControlTemplate>
<Grid x:Name="RootGrid" UseLayoutRounding="true" ColumnDefinitions="6,Auto,*,6" RowDefinitions="Auto,Auto,*,6">
<!-- Separate Border for the background because if the background is set in the Border with the Header the opacity mask will be applied to the background as well. -->
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
Background="{TemplateBinding Background}"/>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="0"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
Background="{TemplateBinding Background}"/>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Canvas Background="Transparent">
<Rectangle Fill="Transparent"
Width="{Binding #Header.Bounds.Width}"
Height="{Binding #Header.Bounds.Height}"
Canvas.Left="{Binding #Header.Bounds.X}"
Canvas.Top="{Binding #Header.Bounds.Y}"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
<Border Grid.Row="1"
Grid.RowSpan="3"
Grid.ColumnSpan="4"
CornerRadius="{TemplateBinding CornerRadius}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border.OpacityMask>
<MultiBinding Converter="{StaticResource BorderGapMaskConverter}"
ConverterParameter="6">
<Binding ElementName="Header"
Path="Bounds.Width"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="Bounds.Width"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="Bounds.Height"/>
</MultiBinding>
</Border.OpacityMask>
</Border>
<!-- ContentPresenter for the header -->
<Border x:Name="Header"
Padding="3,0,3,0"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1">
<ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}" />
</Border>
<!-- Primary content for GroupBox -->
<ContentPresenter Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
<!-- ContentPresenter for the header -->
<Border x:Name="Header"
Padding="3,0,3,0"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1">
<ContentPresenter Name="PART_HeaderPresenter"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Margin="{TemplateBinding Padding}" />
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}" />
</Border>
<!-- Primary content for GroupBox -->
<ContentPresenter Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Name="PART_ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
UseLayoutRounding="{TemplateBinding UseLayoutRounding}"
Margin="{TemplateBinding Padding}" />
</Grid>
</ControlTemplate>
</Setter>
@ -80,6 +80,6 @@
<Style Selector="^ /template/ ContentPresenter#PART_HeaderPresenter">
<Setter Property="RecognizesAccessKey" Value="True" />
</Style>
</ControlTheme>
</Style>
</ControlTheme>
</ResourceDictionary>

Loading…
Cancel
Save