Browse Source

Merge branch 'master' into master

pull/9674/head
M. Tomecki 3 years ago
committed by GitHub
parent
commit
cc038f8537
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .editorconfig
  2. 89
      samples/ControlCatalog/Pages/CompositionPage.axaml
  3. 172
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  4. 2
      src/Avalonia.Base/Input/KeyEventArgs.cs
  5. 2
      src/Avalonia.Base/Input/TextInputEventArgs.cs
  6. 5
      src/Avalonia.Base/Media/Brush.cs
  7. 4
      src/Avalonia.Base/Media/BrushExtensions.cs
  8. 282
      src/Avalonia.Base/Media/Brushes.cs
  9. 2
      src/Avalonia.Base/Media/ConicGradientBrush.cs
  10. 8
      src/Avalonia.Base/Media/DashStyle.cs
  11. 4
      src/Avalonia.Base/Media/GradientBrush.cs
  12. 2
      src/Avalonia.Base/Media/IDashStyle.cs
  13. 9
      src/Avalonia.Base/Media/IImmutableBrush.cs
  14. 4
      src/Avalonia.Base/Media/IMutableBrush.cs
  15. 9
      src/Avalonia.Base/Media/ISolidColorBrush.cs
  16. 4
      src/Avalonia.Base/Media/ImageBrush.cs
  17. 373
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  18. 4
      src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs
  19. 2
      src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs
  20. 4
      src/Avalonia.Base/Media/Immutable/ImmutablePen.cs
  21. 2
      src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs
  22. 2
      src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
  23. 6
      src/Avalonia.Base/Media/KnownColors.cs
  24. 2
      src/Avalonia.Base/Media/LinearGradientBrush.cs
  25. 2
      src/Avalonia.Base/Media/RadialGradientBrush.cs
  26. 4
      src/Avalonia.Base/Media/SolidColorBrush.cs
  27. 4
      src/Avalonia.Base/Media/VisualBrush.cs
  28. 45
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  29. 53
      src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
  30. 53
      src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
  31. 2
      src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs
  32. 2
      src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs
  33. 37
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs
  34. 65
      src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs
  35. 2
      src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
  36. 6
      src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs
  37. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  38. 16
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  39. 44
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  40. 82
      src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs
  41. 2
      src/Avalonia.Base/Rendering/Composition/VisualCollection.cs
  42. 3
      src/Avalonia.Base/Visual.cs
  43. 7
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  44. 10
      src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs
  45. 14
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  46. 9
      src/Avalonia.Controls/NativeMenuBar.cs
  47. 7
      src/Avalonia.Controls/NativeMenuItemSeparator.cs
  48. 2
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  49. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  50. 37
      src/Avalonia.Controls/Window.cs
  51. 57
      src/Avalonia.Controls/WindowClosingEventArgs.cs
  52. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  53. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  54. 2
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  55. 4
      src/Avalonia.Native/WindowImpl.cs
  56. 17
      src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
  57. 21
      src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
  58. 4
      src/Avalonia.X11/X11Window.cs
  59. 9
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  60. 2
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  61. 3
      src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs
  62. 6
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  63. 17
      src/Windows/Avalonia.Win32/WindowImpl.cs
  64. 3
      tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
  65. 115
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  66. 44
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  67. 8
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  68. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs
  69. 7
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
  70. 8
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

2
.editorconfig

@ -140,6 +140,8 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CS1591: Missing XML comment for publicly visible type or member # CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion dotnet_diagnostic.CS1591.severity = suggestion
# CS0162: Remove unreachable code
dotnet_diagnostic.CS0162.severity = error
# CA1304: Specify CultureInfo # CA1304: Specify CultureInfo
dotnet_diagnostic.CA1304.severity = warning dotnet_diagnostic.CA1304.severity = warning
# CA1802: Use literals where appropriate # CA1802: Use literals where appropriate

89
samples/ControlCatalog/Pages/CompositionPage.axaml

@ -2,44 +2,57 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="using:ControlCatalog.Pages" xmlns:pages="using:ControlCatalog.Pages"
x:Class="ControlCatalog.Pages.CompositionPage"> x:Class="ControlCatalog.Pages.CompositionPage">
<StackPanel> <TabControl>
<TextBlock Classes="h1">Implicit animations</TextBlock> <TabItem Header="Implicit animations">
<StackPanel>
<Grid ColumnDefinitions="*,10,40" Margin="0 0 40 0"> <Grid ColumnDefinitions="*,10,40" Margin="0 0 40 0">
<ItemsControl x:Name="Items"> <ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<WrapPanel/> <WrapPanel/>
</ItemsPanelTemplate> </ItemsPanelTemplate>
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates> <ItemsControl.DataTemplates>
<DataTemplate DataType="pages:CompositionPageColorItem"> <DataTemplate DataType="pages:CompositionPageColorItem">
<Border <Border
pages:CompositionPage.EnableAnimations="True" pages:CompositionPage.EnableAnimations="True"
Padding="10" BorderBrush="Gray" BorderThickness="2" Padding="10" BorderBrush="Gray" BorderThickness="2"
Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10"> Background="{Binding ColorBrush}" Width="100" Height="100" Margin="10">
<TextBlock Text="{Binding ColorHexValue}"/> <TextBlock Text="{Binding ColorHexValue}"/>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.DataTemplates> </ItemsControl.DataTemplates>
</ItemsControl> </ItemsControl>
<GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray" <GridSplitter Margin="2" BorderThickness="1" BorderBrush="Gray"
Background="#e0e0e0" Grid.Column="1" Background="#e0e0e0" Grid.Column="1"
ResizeDirection="Columns" ResizeBehavior="PreviousAndNext" ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"
/> />
<Border Grid.Column="2"> <Border Grid.Column="2">
<LayoutTransformControl <LayoutTransformControl HorizontalAlignment="Center" MinWidth="30">
HorizontalAlignment="Center" <LayoutTransformControl.LayoutTransform>
<RotateTransform Angle="90"/>
MinWidth="30"> </LayoutTransformControl.LayoutTransform>
<LayoutTransformControl.LayoutTransform> <TextBlock>Resize me</TextBlock>
<RotateTransform Angle="90"/> </LayoutTransformControl>
</LayoutTransformControl.LayoutTransform> </Border>
<TextBlock>Resize me</TextBlock>
</LayoutTransformControl>
</Border>
</Grid> </Grid>
</StackPanel>
</TabItem>
</StackPanel> <TabItem Header="Animation">
<DockPanel>
<Button DockPanel.Dock="Top" Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
<Control x:Name="SolidVisualHost" />
</DockPanel>
</TabItem>
<TabItem Header="Custom">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Margin="10" Click="ButtonThreadSleep">Thread.Sleep(10000);</Button>
<Button Margin="10" Click="ButtonStartCustomVisual">Start</Button>
<Button Margin="10" Click="ButtonStopCustomVisual">Stop</Button>
</StackPanel>
<Control x:Name="CustomVisualHost" />
</DockPanel>
</TabItem>
</TabControl>
</UserControl> </UserControl>

172
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@ -1,28 +1,39 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using System.Threading;
using Avalonia; using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Animations;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Math = System.Math;
namespace ControlCatalog.Pages; namespace ControlCatalog.Pages;
public partial class CompositionPage : UserControl public partial class CompositionPage : UserControl
{ {
private ImplicitAnimationCollection? _implicitAnimations; private ImplicitAnimationCollection? _implicitAnimations;
private CompositionCustomVisual? _customVisual;
private CompositionSolidColorVisual? _solidVisual;
public CompositionPage() public CompositionPage()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
AttachAnimatedSolidVisual(this.FindControl<Control>("SolidVisualHost")!);
AttachCustomVisual(this.FindControl<Control>("CustomVisualHost")!);
} }
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
this.Get<ItemsControl>("Items").Items = CreateColorItems(); this.Get<ItemsControl>("Items").Items = CreateColorItems();
} }
private static List<CompositionPageColorItem> CreateColorItems() private static List<CompositionPageColorItem> CreateColorItems()
@ -126,6 +137,167 @@ public partial class CompositionPage : UserControl
compositionVisual.ImplicitAnimations = page._implicitAnimations; compositionVisual.ImplicitAnimations = page._implicitAnimations;
} }
} }
void AttachAnimatedSolidVisual(Visual v)
{
void Update()
{
if(_solidVisual == null)
return;
_solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
_solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
}
v.AttachedToVisualTree += delegate
{
var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
if(compositor == null || _solidVisual?.Compositor == compositor)
return;
_solidVisual = compositor.CreateSolidColorVisual();
ElementComposition.SetElementChildVisual(v, _solidVisual);
_solidVisual.Color = Colors.Red;
var animation = _solidVisual.Compositor.CreateColorKeyFrameAnimation();
animation.InsertKeyFrame(0, Colors.Red);
animation.InsertKeyFrame(0.5f, Colors.Blue);
animation.InsertKeyFrame(1, Colors.Green);
animation.Duration = TimeSpan.FromSeconds(5);
animation.IterationBehavior = AnimationIterationBehavior.Forever;
animation.Direction = PlaybackDirection.Alternate;
_solidVisual.StartAnimation("Color", animation);
_solidVisual.AnchorPoint = new Vector2(0, 0);
var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
scale.Duration = TimeSpan.FromSeconds(5);
scale.IterationBehavior = AnimationIterationBehavior.Forever;
scale.InsertKeyFrame(0, new Vector3(1, 1, 0));
scale.InsertKeyFrame(0.5f, new Vector3(1.5f, 1.5f, 0));
scale.InsertKeyFrame(1, new Vector3(1, 1, 0));
_solidVisual.StartAnimation("Scale", scale);
var center =
_solidVisual.Compositor.CreateExpressionAnimation(
"Vector3(this.Target.Size.X * 0.5, this.Target.Size.Y * 0.5, 1)");
_solidVisual.StartAnimation("CenterPoint", center);
Update();
};
v.PropertyChanged += (_, a) =>
{
if (a.Property == BoundsProperty)
Update();
};
}
void AttachCustomVisual(Visual v)
{
void Update()
{
if (_customVisual == null)
return;
var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
_customVisual.Size = new Vector2((float)v.Bounds.Width, h);
_customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
}
v.AttachedToVisualTree += delegate
{
var compositor = ElementComposition.GetElementVisual(v)?.Compositor;
if(compositor == null || _customVisual?.Compositor == compositor)
return;
_customVisual = compositor.CreateCustomVisual(new CustomVisualHandler());
ElementComposition.SetElementChildVisual(v, _customVisual);
_customVisual.SendHandlerMessage(CustomVisualHandler.StartMessage);
Update();
};
v.PropertyChanged += (_, a) =>
{
if (a.Property == BoundsProperty)
Update();
};
}
class CustomVisualHandler : CompositionCustomVisualHandler
{
private TimeSpan _animationElapsed;
private TimeSpan? _lastServerTime;
private bool _running;
public static readonly object StopMessage = new(), StartMessage = new();
public override void OnRender(ImmediateDrawingContext drawingContext)
{
if (_running)
{
if (_lastServerTime.HasValue) _animationElapsed += (CompositionNow - _lastServerTime.Value);
_lastServerTime = CompositionNow;
}
const int cnt = 20;
var maxPointSizeX = EffectiveSize.X / (cnt * 1.6);
var maxPointSizeY = EffectiveSize.Y / 4;
var pointSize = Math.Min(maxPointSizeX, maxPointSizeY);
var animationLength = TimeSpan.FromSeconds(4);
var animationStage = _animationElapsed.TotalSeconds / animationLength.TotalSeconds;
var sinOffset = Math.Cos(_animationElapsed.TotalSeconds) * 1.5;
for (var c = 0; c < cnt; c++)
{
var stage = (animationStage + (double)c / cnt) % 1;
var colorStage =
(animationStage + (Math.Sin(_animationElapsed.TotalSeconds * 2) + 1) / 2 + (double)c / cnt) % 1;
var posX = (EffectiveSize.X + pointSize * 3) * stage - pointSize;
var posY = (EffectiveSize.Y - pointSize) * (1 + Math.Sin(stage * 3.14 * 3 + sinOffset)) / 2 + pointSize / 2;
var opacity = Math.Sin(stage * 3.14);
drawingContext.DrawEllipse(new ImmutableSolidColorBrush(Color.FromArgb(
255,
(byte)(255 - 255 * colorStage),
(byte)(255 * Math.Abs(0.5 - colorStage) * 2),
(byte)(255 * colorStage)
), opacity), null,
new Point(posX, posY), pointSize / 2, pointSize / 2);
}
}
public override void OnMessage(object message)
{
if (message == StartMessage)
{
_running = true;
_lastServerTime = null;
RegisterForNextAnimationFrameUpdate();
}
else if (message == StopMessage)
_running = false;
}
public override void OnAnimationFrameUpdate()
{
if (_running)
{
Invalidate();
RegisterForNextAnimationFrameUpdate();
}
}
}
private void ButtonThreadSleep(object? sender, RoutedEventArgs e)
{
Thread.Sleep(10000);
}
private void ButtonStartCustomVisual(object? sender, RoutedEventArgs e)
{
_customVisual?.SendHandlerMessage(CustomVisualHandler.StartMessage);
}
private void ButtonStopCustomVisual(object? sender, RoutedEventArgs e)
{
_customVisual?.SendHandlerMessage(CustomVisualHandler.StopMessage);
}
} }
public class CompositionPageColorItem public class CompositionPageColorItem

2
src/Avalonia.Base/Input/KeyEventArgs.cs

@ -5,7 +5,7 @@ namespace Avalonia.Input
{ {
public class KeyEventArgs : RoutedEventArgs public class KeyEventArgs : RoutedEventArgs
{ {
internal KeyEventArgs() public KeyEventArgs()
{ {
} }

2
src/Avalonia.Base/Input/TextInputEventArgs.cs

@ -4,7 +4,7 @@ namespace Avalonia.Input
{ {
public class TextInputEventArgs : RoutedEventArgs public class TextInputEventArgs : RoutedEventArgs
{ {
internal TextInputEventArgs() public TextInputEventArgs()
{ {
} }

5
src/Avalonia.Base/Media/Brush.cs

@ -10,7 +10,7 @@ namespace Avalonia.Media
/// Describes how an area is painted. /// Describes how an area is painted.
/// </summary> /// </summary>
[TypeConverter(typeof(BrushConverter))] [TypeConverter(typeof(BrushConverter))]
public abstract class Brush : Animatable, IMutableBrush public abstract class Brush : Animatable
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Opacity"/> property. /// Defines the <see cref="Opacity"/> property.
@ -92,9 +92,6 @@ namespace Avalonia.Media
throw new FormatException($"Invalid brush string: '{s}'."); throw new FormatException($"Invalid brush string: '{s}'.");
} }
/// <inheritdoc/>
public abstract IBrush ToImmutable();
/// <summary> /// <summary>
/// Marks a property as affecting the brush's visual representation. /// Marks a property as affecting the brush's visual representation.
/// </summary> /// </summary>

4
src/Avalonia.Base/Media/BrushExtensions.cs

@ -16,11 +16,11 @@ namespace Avalonia.Media
/// The result of calling <see cref="IMutableBrush.ToImmutable"/> if the brush is mutable, /// The result of calling <see cref="IMutableBrush.ToImmutable"/> if the brush is mutable,
/// otherwise <paramref name="brush"/>. /// otherwise <paramref name="brush"/>.
/// </returns> /// </returns>
public static IBrush ToImmutable(this IBrush brush) public static IImmutableBrush ToImmutable(this IBrush brush)
{ {
_ = brush ?? throw new ArgumentNullException(nameof(brush)); _ = brush ?? throw new ArgumentNullException(nameof(brush));
return (brush as IMutableBrush)?.ToImmutable() ?? brush; return (brush as IMutableBrush)?.ToImmutable() ?? (IImmutableBrush)brush;
} }
/// <summary> /// <summary>

282
src/Avalonia.Base/Media/Brushes.cs

@ -8,706 +8,706 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets an <see cref="Colors.AliceBlue"/> colored brush. /// Gets an <see cref="Colors.AliceBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush(); public static IImmutableSolidColorBrush AliceBlue => KnownColor.AliceBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.AntiqueWhite"/> colored brush. /// Gets an <see cref="Colors.AntiqueWhite"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush(); public static IImmutableSolidColorBrush AntiqueWhite => KnownColor.AntiqueWhite.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Aqua"/> colored brush. /// Gets an <see cref="Colors.Aqua"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Aqua => KnownColor.Aqua.ToBrush(); public static IImmutableSolidColorBrush Aqua => KnownColor.Aqua.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Aquamarine"/> colored brush. /// Gets an <see cref="Colors.Aquamarine"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush(); public static IImmutableSolidColorBrush Aquamarine => KnownColor.Aquamarine.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Azure"/> colored brush. /// Gets an <see cref="Colors.Azure"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Azure => KnownColor.Azure.ToBrush(); public static IImmutableSolidColorBrush Azure => KnownColor.Azure.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Beige"/> colored brush. /// Gets an <see cref="Colors.Beige"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Beige => KnownColor.Beige.ToBrush(); public static IImmutableSolidColorBrush Beige => KnownColor.Beige.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Bisque"/> colored brush. /// Gets an <see cref="Colors.Bisque"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Bisque => KnownColor.Bisque.ToBrush(); public static IImmutableSolidColorBrush Bisque => KnownColor.Bisque.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Black"/> colored brush. /// Gets an <see cref="Colors.Black"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Black => KnownColor.Black.ToBrush(); public static IImmutableSolidColorBrush Black => KnownColor.Black.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.BlanchedAlmond"/> colored brush. /// Gets an <see cref="Colors.BlanchedAlmond"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush(); public static IImmutableSolidColorBrush BlanchedAlmond => KnownColor.BlanchedAlmond.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Blue"/> colored brush. /// Gets an <see cref="Colors.Blue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Blue => KnownColor.Blue.ToBrush(); public static IImmutableSolidColorBrush Blue => KnownColor.Blue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.BlueViolet"/> colored brush. /// Gets an <see cref="Colors.BlueViolet"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush(); public static IImmutableSolidColorBrush BlueViolet => KnownColor.BlueViolet.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Brown"/> colored brush. /// Gets an <see cref="Colors.Brown"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Brown => KnownColor.Brown.ToBrush(); public static IImmutableSolidColorBrush Brown => KnownColor.Brown.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.BurlyWood"/> colored brush. /// Gets an <see cref="Colors.BurlyWood"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush(); public static IImmutableSolidColorBrush BurlyWood => KnownColor.BurlyWood.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.CadetBlue"/> colored brush. /// Gets an <see cref="Colors.CadetBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush(); public static IImmutableSolidColorBrush CadetBlue => KnownColor.CadetBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Chartreuse"/> colored brush. /// Gets an <see cref="Colors.Chartreuse"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush(); public static IImmutableSolidColorBrush Chartreuse => KnownColor.Chartreuse.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Chocolate"/> colored brush. /// Gets an <see cref="Colors.Chocolate"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush(); public static IImmutableSolidColorBrush Chocolate => KnownColor.Chocolate.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Coral"/> colored brush. /// Gets an <see cref="Colors.Coral"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Coral => KnownColor.Coral.ToBrush(); public static IImmutableSolidColorBrush Coral => KnownColor.Coral.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.CornflowerBlue"/> colored brush. /// Gets an <see cref="Colors.CornflowerBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush(); public static IImmutableSolidColorBrush CornflowerBlue => KnownColor.CornflowerBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Cornsilk"/> colored brush. /// Gets an <see cref="Colors.Cornsilk"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush(); public static IImmutableSolidColorBrush Cornsilk => KnownColor.Cornsilk.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Crimson"/> colored brush. /// Gets an <see cref="Colors.Crimson"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Crimson => KnownColor.Crimson.ToBrush(); public static IImmutableSolidColorBrush Crimson => KnownColor.Crimson.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Cyan"/> colored brush. /// Gets an <see cref="Colors.Cyan"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Cyan => KnownColor.Cyan.ToBrush(); public static IImmutableSolidColorBrush Cyan => KnownColor.Cyan.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkBlue"/> colored brush. /// Gets an <see cref="Colors.DarkBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush(); public static IImmutableSolidColorBrush DarkBlue => KnownColor.DarkBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkCyan"/> colored brush. /// Gets an <see cref="Colors.DarkCyan"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush(); public static IImmutableSolidColorBrush DarkCyan => KnownColor.DarkCyan.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkGoldenrod"/> colored brush. /// Gets an <see cref="Colors.DarkGoldenrod"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush(); public static IImmutableSolidColorBrush DarkGoldenrod => KnownColor.DarkGoldenrod.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkGray"/> colored brush. /// Gets an <see cref="Colors.DarkGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush(); public static IImmutableSolidColorBrush DarkGray => KnownColor.DarkGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkGreen"/> colored brush. /// Gets an <see cref="Colors.DarkGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush(); public static IImmutableSolidColorBrush DarkGreen => KnownColor.DarkGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkKhaki"/> colored brush. /// Gets an <see cref="Colors.DarkKhaki"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush(); public static IImmutableSolidColorBrush DarkKhaki => KnownColor.DarkKhaki.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkMagenta"/> colored brush. /// Gets an <see cref="Colors.DarkMagenta"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush(); public static IImmutableSolidColorBrush DarkMagenta => KnownColor.DarkMagenta.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkOliveGreen"/> colored brush. /// Gets an <see cref="Colors.DarkOliveGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush(); public static IImmutableSolidColorBrush DarkOliveGreen => KnownColor.DarkOliveGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkOrange"/> colored brush. /// Gets an <see cref="Colors.DarkOrange"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush(); public static IImmutableSolidColorBrush DarkOrange => KnownColor.DarkOrange.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkOrchid"/> colored brush. /// Gets an <see cref="Colors.DarkOrchid"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush(); public static IImmutableSolidColorBrush DarkOrchid => KnownColor.DarkOrchid.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkRed"/> colored brush. /// Gets an <see cref="Colors.DarkRed"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush(); public static IImmutableSolidColorBrush DarkRed => KnownColor.DarkRed.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkSalmon"/> colored brush. /// Gets an <see cref="Colors.DarkSalmon"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush(); public static IImmutableSolidColorBrush DarkSalmon => KnownColor.DarkSalmon.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkSeaGreen"/> colored brush. /// Gets an <see cref="Colors.DarkSeaGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush(); public static IImmutableSolidColorBrush DarkSeaGreen => KnownColor.DarkSeaGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkSlateBlue"/> colored brush. /// Gets an <see cref="Colors.DarkSlateBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush(); public static IImmutableSolidColorBrush DarkSlateBlue => KnownColor.DarkSlateBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkSlateGray"/> colored brush. /// Gets an <see cref="Colors.DarkSlateGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush(); public static IImmutableSolidColorBrush DarkSlateGray => KnownColor.DarkSlateGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkTurquoise"/> colored brush. /// Gets an <see cref="Colors.DarkTurquoise"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush(); public static IImmutableSolidColorBrush DarkTurquoise => KnownColor.DarkTurquoise.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DarkViolet"/> colored brush. /// Gets an <see cref="Colors.DarkViolet"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush(); public static IImmutableSolidColorBrush DarkViolet => KnownColor.DarkViolet.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DeepPink"/> colored brush. /// Gets an <see cref="Colors.DeepPink"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush(); public static IImmutableSolidColorBrush DeepPink => KnownColor.DeepPink.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DeepSkyBlue"/> colored brush. /// Gets an <see cref="Colors.DeepSkyBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush(); public static IImmutableSolidColorBrush DeepSkyBlue => KnownColor.DeepSkyBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DimGray"/> colored brush. /// Gets an <see cref="Colors.DimGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DimGray => KnownColor.DimGray.ToBrush(); public static IImmutableSolidColorBrush DimGray => KnownColor.DimGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.DodgerBlue"/> colored brush. /// Gets an <see cref="Colors.DodgerBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush(); public static IImmutableSolidColorBrush DodgerBlue => KnownColor.DodgerBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Firebrick"/> colored brush. /// Gets an <see cref="Colors.Firebrick"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush(); public static IImmutableSolidColorBrush Firebrick => KnownColor.Firebrick.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.FloralWhite"/> colored brush. /// Gets an <see cref="Colors.FloralWhite"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush(); public static IImmutableSolidColorBrush FloralWhite => KnownColor.FloralWhite.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.ForestGreen"/> colored brush. /// Gets an <see cref="Colors.ForestGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush(); public static IImmutableSolidColorBrush ForestGreen => KnownColor.ForestGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Fuchsia"/> colored brush. /// Gets an <see cref="Colors.Fuchsia"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush(); public static IImmutableSolidColorBrush Fuchsia => KnownColor.Fuchsia.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Gainsboro"/> colored brush. /// Gets an <see cref="Colors.Gainsboro"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush(); public static IImmutableSolidColorBrush Gainsboro => KnownColor.Gainsboro.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.GhostWhite"/> colored brush. /// Gets an <see cref="Colors.GhostWhite"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush(); public static IImmutableSolidColorBrush GhostWhite => KnownColor.GhostWhite.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Gold"/> colored brush. /// Gets an <see cref="Colors.Gold"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Gold => KnownColor.Gold.ToBrush(); public static IImmutableSolidColorBrush Gold => KnownColor.Gold.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Goldenrod"/> colored brush. /// Gets an <see cref="Colors.Goldenrod"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush(); public static IImmutableSolidColorBrush Goldenrod => KnownColor.Goldenrod.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Gray"/> colored brush. /// Gets an <see cref="Colors.Gray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Gray => KnownColor.Gray.ToBrush(); public static IImmutableSolidColorBrush Gray => KnownColor.Gray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Green"/> colored brush. /// Gets an <see cref="Colors.Green"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Green => KnownColor.Green.ToBrush(); public static IImmutableSolidColorBrush Green => KnownColor.Green.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.GreenYellow"/> colored brush. /// Gets an <see cref="Colors.GreenYellow"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush(); public static IImmutableSolidColorBrush GreenYellow => KnownColor.GreenYellow.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Honeydew"/> colored brush. /// Gets an <see cref="Colors.Honeydew"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush(); public static IImmutableSolidColorBrush Honeydew => KnownColor.Honeydew.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.HotPink"/> colored brush. /// Gets an <see cref="Colors.HotPink"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush HotPink => KnownColor.HotPink.ToBrush(); public static IImmutableSolidColorBrush HotPink => KnownColor.HotPink.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.IndianRed"/> colored brush. /// Gets an <see cref="Colors.IndianRed"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush(); public static IImmutableSolidColorBrush IndianRed => KnownColor.IndianRed.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Indigo"/> colored brush. /// Gets an <see cref="Colors.Indigo"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Indigo => KnownColor.Indigo.ToBrush(); public static IImmutableSolidColorBrush Indigo => KnownColor.Indigo.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Ivory"/> colored brush. /// Gets an <see cref="Colors.Ivory"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Ivory => KnownColor.Ivory.ToBrush(); public static IImmutableSolidColorBrush Ivory => KnownColor.Ivory.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Khaki"/> colored brush. /// Gets an <see cref="Colors.Khaki"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Khaki => KnownColor.Khaki.ToBrush(); public static IImmutableSolidColorBrush Khaki => KnownColor.Khaki.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Lavender"/> colored brush. /// Gets an <see cref="Colors.Lavender"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Lavender => KnownColor.Lavender.ToBrush(); public static IImmutableSolidColorBrush Lavender => KnownColor.Lavender.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LavenderBlush"/> colored brush. /// Gets an <see cref="Colors.LavenderBlush"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush(); public static IImmutableSolidColorBrush LavenderBlush => KnownColor.LavenderBlush.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LawnGreen"/> colored brush. /// Gets an <see cref="Colors.LawnGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush(); public static IImmutableSolidColorBrush LawnGreen => KnownColor.LawnGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LemonChiffon"/> colored brush. /// Gets an <see cref="Colors.LemonChiffon"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush(); public static IImmutableSolidColorBrush LemonChiffon => KnownColor.LemonChiffon.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightBlue"/> colored brush. /// Gets an <see cref="Colors.LightBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush(); public static IImmutableSolidColorBrush LightBlue => KnownColor.LightBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightCoral"/> colored brush. /// Gets an <see cref="Colors.LightCoral"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush(); public static IImmutableSolidColorBrush LightCoral => KnownColor.LightCoral.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightCyan"/> colored brush. /// Gets an <see cref="Colors.LightCyan"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush(); public static IImmutableSolidColorBrush LightCyan => KnownColor.LightCyan.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightGoldenrodYellow"/> colored brush. /// Gets an <see cref="Colors.LightGoldenrodYellow"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush(); public static IImmutableSolidColorBrush LightGoldenrodYellow => KnownColor.LightGoldenrodYellow.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightGray"/> colored brush. /// Gets an <see cref="Colors.LightGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightGray => KnownColor.LightGray.ToBrush(); public static IImmutableSolidColorBrush LightGray => KnownColor.LightGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightGreen"/> colored brush. /// Gets an <see cref="Colors.LightGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush(); public static IImmutableSolidColorBrush LightGreen => KnownColor.LightGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightPink"/> colored brush. /// Gets an <see cref="Colors.LightPink"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightPink => KnownColor.LightPink.ToBrush(); public static IImmutableSolidColorBrush LightPink => KnownColor.LightPink.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightSalmon"/> colored brush. /// Gets an <see cref="Colors.LightSalmon"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush(); public static IImmutableSolidColorBrush LightSalmon => KnownColor.LightSalmon.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightSeaGreen"/> colored brush. /// Gets an <see cref="Colors.LightSeaGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush(); public static IImmutableSolidColorBrush LightSeaGreen => KnownColor.LightSeaGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightSkyBlue"/> colored brush. /// Gets an <see cref="Colors.LightSkyBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush(); public static IImmutableSolidColorBrush LightSkyBlue => KnownColor.LightSkyBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightSlateGray"/> colored brush. /// Gets an <see cref="Colors.LightSlateGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush(); public static IImmutableSolidColorBrush LightSlateGray => KnownColor.LightSlateGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightSteelBlue"/> colored brush. /// Gets an <see cref="Colors.LightSteelBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush(); public static IImmutableSolidColorBrush LightSteelBlue => KnownColor.LightSteelBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LightYellow"/> colored brush. /// Gets an <see cref="Colors.LightYellow"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush(); public static IImmutableSolidColorBrush LightYellow => KnownColor.LightYellow.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Lime"/> colored brush. /// Gets an <see cref="Colors.Lime"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Lime => KnownColor.Lime.ToBrush(); public static IImmutableSolidColorBrush Lime => KnownColor.Lime.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.LimeGreen"/> colored brush. /// Gets an <see cref="Colors.LimeGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush(); public static IImmutableSolidColorBrush LimeGreen => KnownColor.LimeGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Linen"/> colored brush. /// Gets an <see cref="Colors.Linen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Linen => KnownColor.Linen.ToBrush(); public static IImmutableSolidColorBrush Linen => KnownColor.Linen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Magenta"/> colored brush. /// Gets an <see cref="Colors.Magenta"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Magenta => KnownColor.Magenta.ToBrush(); public static IImmutableSolidColorBrush Magenta => KnownColor.Magenta.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Maroon"/> colored brush. /// Gets an <see cref="Colors.Maroon"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Maroon => KnownColor.Maroon.ToBrush(); public static IImmutableSolidColorBrush Maroon => KnownColor.Maroon.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumAquamarine"/> colored brush. /// Gets an <see cref="Colors.MediumAquamarine"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush(); public static IImmutableSolidColorBrush MediumAquamarine => KnownColor.MediumAquamarine.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumBlue"/> colored brush. /// Gets an <see cref="Colors.MediumBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush(); public static IImmutableSolidColorBrush MediumBlue => KnownColor.MediumBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumOrchid"/> colored brush. /// Gets an <see cref="Colors.MediumOrchid"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush(); public static IImmutableSolidColorBrush MediumOrchid => KnownColor.MediumOrchid.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumPurple"/> colored brush. /// Gets an <see cref="Colors.MediumPurple"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush(); public static IImmutableSolidColorBrush MediumPurple => KnownColor.MediumPurple.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumSeaGreen"/> colored brush. /// Gets an <see cref="Colors.MediumSeaGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush(); public static IImmutableSolidColorBrush MediumSeaGreen => KnownColor.MediumSeaGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumSlateBlue"/> colored brush. /// Gets an <see cref="Colors.MediumSlateBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush(); public static IImmutableSolidColorBrush MediumSlateBlue => KnownColor.MediumSlateBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumSpringGreen"/> colored brush. /// Gets an <see cref="Colors.MediumSpringGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush(); public static IImmutableSolidColorBrush MediumSpringGreen => KnownColor.MediumSpringGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumTurquoise"/> colored brush. /// Gets an <see cref="Colors.MediumTurquoise"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush(); public static IImmutableSolidColorBrush MediumTurquoise => KnownColor.MediumTurquoise.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MediumVioletRed"/> colored brush. /// Gets an <see cref="Colors.MediumVioletRed"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush(); public static IImmutableSolidColorBrush MediumVioletRed => KnownColor.MediumVioletRed.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MidnightBlue"/> colored brush. /// Gets an <see cref="Colors.MidnightBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush(); public static IImmutableSolidColorBrush MidnightBlue => KnownColor.MidnightBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MintCream"/> colored brush. /// Gets an <see cref="Colors.MintCream"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MintCream => KnownColor.MintCream.ToBrush(); public static IImmutableSolidColorBrush MintCream => KnownColor.MintCream.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.MistyRose"/> colored brush. /// Gets an <see cref="Colors.MistyRose"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush(); public static IImmutableSolidColorBrush MistyRose => KnownColor.MistyRose.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Moccasin"/> colored brush. /// Gets an <see cref="Colors.Moccasin"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush(); public static IImmutableSolidColorBrush Moccasin => KnownColor.Moccasin.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.NavajoWhite"/> colored brush. /// Gets an <see cref="Colors.NavajoWhite"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush(); public static IImmutableSolidColorBrush NavajoWhite => KnownColor.NavajoWhite.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Navy"/> colored brush. /// Gets an <see cref="Colors.Navy"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Navy => KnownColor.Navy.ToBrush(); public static IImmutableSolidColorBrush Navy => KnownColor.Navy.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.OldLace"/> colored brush. /// Gets an <see cref="Colors.OldLace"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush OldLace => KnownColor.OldLace.ToBrush(); public static IImmutableSolidColorBrush OldLace => KnownColor.OldLace.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Olive"/> colored brush. /// Gets an <see cref="Colors.Olive"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Olive => KnownColor.Olive.ToBrush(); public static IImmutableSolidColorBrush Olive => KnownColor.Olive.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.OliveDrab"/> colored brush. /// Gets an <see cref="Colors.OliveDrab"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush(); public static IImmutableSolidColorBrush OliveDrab => KnownColor.OliveDrab.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Orange"/> colored brush. /// Gets an <see cref="Colors.Orange"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Orange => KnownColor.Orange.ToBrush(); public static IImmutableSolidColorBrush Orange => KnownColor.Orange.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.OrangeRed"/> colored brush. /// Gets an <see cref="Colors.OrangeRed"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush(); public static IImmutableSolidColorBrush OrangeRed => KnownColor.OrangeRed.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Orchid"/> colored brush. /// Gets an <see cref="Colors.Orchid"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Orchid => KnownColor.Orchid.ToBrush(); public static IImmutableSolidColorBrush Orchid => KnownColor.Orchid.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PaleGoldenrod"/> colored brush. /// Gets an <see cref="Colors.PaleGoldenrod"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush(); public static IImmutableSolidColorBrush PaleGoldenrod => KnownColor.PaleGoldenrod.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PaleGreen"/> colored brush. /// Gets an <see cref="Colors.PaleGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush(); public static IImmutableSolidColorBrush PaleGreen => KnownColor.PaleGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PaleTurquoise"/> colored brush. /// Gets an <see cref="Colors.PaleTurquoise"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush(); public static IImmutableSolidColorBrush PaleTurquoise => KnownColor.PaleTurquoise.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PaleVioletRed"/> colored brush. /// Gets an <see cref="Colors.PaleVioletRed"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush(); public static IImmutableSolidColorBrush PaleVioletRed => KnownColor.PaleVioletRed.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PapayaWhip"/> colored brush. /// Gets an <see cref="Colors.PapayaWhip"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush(); public static IImmutableSolidColorBrush PapayaWhip => KnownColor.PapayaWhip.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PeachPuff"/> colored brush. /// Gets an <see cref="Colors.PeachPuff"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush(); public static IImmutableSolidColorBrush PeachPuff => KnownColor.PeachPuff.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Peru"/> colored brush. /// Gets an <see cref="Colors.Peru"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Peru => KnownColor.Peru.ToBrush(); public static IImmutableSolidColorBrush Peru => KnownColor.Peru.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Pink"/> colored brush. /// Gets an <see cref="Colors.Pink"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Pink => KnownColor.Pink.ToBrush(); public static IImmutableSolidColorBrush Pink => KnownColor.Pink.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Plum"/> colored brush. /// Gets an <see cref="Colors.Plum"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Plum => KnownColor.Plum.ToBrush(); public static IImmutableSolidColorBrush Plum => KnownColor.Plum.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.PowderBlue"/> colored brush. /// Gets an <see cref="Colors.PowderBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush(); public static IImmutableSolidColorBrush PowderBlue => KnownColor.PowderBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Purple"/> colored brush. /// Gets an <see cref="Colors.Purple"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Purple => KnownColor.Purple.ToBrush(); public static IImmutableSolidColorBrush Purple => KnownColor.Purple.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Red"/> colored brush. /// Gets an <see cref="Colors.Red"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Red => KnownColor.Red.ToBrush(); public static IImmutableSolidColorBrush Red => KnownColor.Red.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.RosyBrown"/> colored brush. /// Gets an <see cref="Colors.RosyBrown"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush(); public static IImmutableSolidColorBrush RosyBrown => KnownColor.RosyBrown.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.RoyalBlue"/> colored brush. /// Gets an <see cref="Colors.RoyalBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush(); public static IImmutableSolidColorBrush RoyalBlue => KnownColor.RoyalBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SaddleBrown"/> colored brush. /// Gets an <see cref="Colors.SaddleBrown"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush(); public static IImmutableSolidColorBrush SaddleBrown => KnownColor.SaddleBrown.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Salmon"/> colored brush. /// Gets an <see cref="Colors.Salmon"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Salmon => KnownColor.Salmon.ToBrush(); public static IImmutableSolidColorBrush Salmon => KnownColor.Salmon.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SandyBrown"/> colored brush. /// Gets an <see cref="Colors.SandyBrown"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush(); public static IImmutableSolidColorBrush SandyBrown => KnownColor.SandyBrown.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SeaGreen"/> colored brush. /// Gets an <see cref="Colors.SeaGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush(); public static IImmutableSolidColorBrush SeaGreen => KnownColor.SeaGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SeaShell"/> colored brush. /// Gets an <see cref="Colors.SeaShell"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush(); public static IImmutableSolidColorBrush SeaShell => KnownColor.SeaShell.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Sienna"/> colored brush. /// Gets an <see cref="Colors.Sienna"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Sienna => KnownColor.Sienna.ToBrush(); public static IImmutableSolidColorBrush Sienna => KnownColor.Sienna.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Silver"/> colored brush. /// Gets an <see cref="Colors.Silver"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Silver => KnownColor.Silver.ToBrush(); public static IImmutableSolidColorBrush Silver => KnownColor.Silver.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SkyBlue"/> colored brush. /// Gets an <see cref="Colors.SkyBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush(); public static IImmutableSolidColorBrush SkyBlue => KnownColor.SkyBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SlateBlue"/> colored brush. /// Gets an <see cref="Colors.SlateBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush(); public static IImmutableSolidColorBrush SlateBlue => KnownColor.SlateBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SlateGray"/> colored brush. /// Gets an <see cref="Colors.SlateGray"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush(); public static IImmutableSolidColorBrush SlateGray => KnownColor.SlateGray.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Snow"/> colored brush. /// Gets an <see cref="Colors.Snow"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Snow => KnownColor.Snow.ToBrush(); public static IImmutableSolidColorBrush Snow => KnownColor.Snow.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SpringGreen"/> colored brush. /// Gets an <see cref="Colors.SpringGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush(); public static IImmutableSolidColorBrush SpringGreen => KnownColor.SpringGreen.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.SteelBlue"/> colored brush. /// Gets an <see cref="Colors.SteelBlue"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush(); public static IImmutableSolidColorBrush SteelBlue => KnownColor.SteelBlue.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Tan"/> colored brush. /// Gets an <see cref="Colors.Tan"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Tan => KnownColor.Tan.ToBrush(); public static IImmutableSolidColorBrush Tan => KnownColor.Tan.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Teal"/> colored brush. /// Gets an <see cref="Colors.Teal"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Teal => KnownColor.Teal.ToBrush(); public static IImmutableSolidColorBrush Teal => KnownColor.Teal.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Thistle"/> colored brush. /// Gets an <see cref="Colors.Thistle"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Thistle => KnownColor.Thistle.ToBrush(); public static IImmutableSolidColorBrush Thistle => KnownColor.Thistle.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Tomato"/> colored brush. /// Gets an <see cref="Colors.Tomato"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Tomato => KnownColor.Tomato.ToBrush(); public static IImmutableSolidColorBrush Tomato => KnownColor.Tomato.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Transparent"/> colored brush. /// Gets an <see cref="Colors.Transparent"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Transparent => KnownColor.Transparent.ToBrush(); public static IImmutableSolidColorBrush Transparent => KnownColor.Transparent.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Turquoise"/> colored brush. /// Gets an <see cref="Colors.Turquoise"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush(); public static IImmutableSolidColorBrush Turquoise => KnownColor.Turquoise.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Violet"/> colored brush. /// Gets an <see cref="Colors.Violet"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Violet => KnownColor.Violet.ToBrush(); public static IImmutableSolidColorBrush Violet => KnownColor.Violet.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Wheat"/> colored brush. /// Gets an <see cref="Colors.Wheat"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Wheat => KnownColor.Wheat.ToBrush(); public static IImmutableSolidColorBrush Wheat => KnownColor.Wheat.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.White"/> colored brush. /// Gets an <see cref="Colors.White"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush White => KnownColor.White.ToBrush(); public static IImmutableSolidColorBrush White => KnownColor.White.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.WhiteSmoke"/> colored brush. /// Gets an <see cref="Colors.WhiteSmoke"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush(); public static IImmutableSolidColorBrush WhiteSmoke => KnownColor.WhiteSmoke.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.Yellow"/> colored brush. /// Gets an <see cref="Colors.Yellow"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush Yellow => KnownColor.Yellow.ToBrush(); public static IImmutableSolidColorBrush Yellow => KnownColor.Yellow.ToBrush();
/// <summary> /// <summary>
/// Gets an <see cref="Colors.YellowGreen"/> colored brush. /// Gets an <see cref="Colors.YellowGreen"/> colored brush.
/// </summary> /// </summary>
public static ISolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush(); public static IImmutableSolidColorBrush YellowGreen => KnownColor.YellowGreen.ToBrush();
} }
} }

2
src/Avalonia.Base/Media/ConicGradientBrush.cs

@ -47,7 +47,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() public override IImmutableBrush ToImmutable()
{ {
return new ImmutableConicGradientBrush(this); return new ImmutableConicGradientBrush(this);
} }

8
src/Avalonia.Base/Media/DashStyle.cs

@ -17,8 +17,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Defines the <see cref="Dashes"/> property. /// Defines the <see cref="Dashes"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<AvaloniaList<double>> DashesProperty = public static readonly StyledProperty<AvaloniaList<double>?> DashesProperty =
AvaloniaProperty.Register<DashStyle, AvaloniaList<double>>(nameof(Dashes)); AvaloniaProperty.Register<DashStyle, AvaloniaList<double>?>(nameof(Dashes));
/// <summary> /// <summary>
/// Defines the <see cref="Offset"/> property. /// Defines the <see cref="Offset"/> property.
@ -83,7 +83,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the length of alternating dashes and gaps. /// Gets or sets the length of alternating dashes and gaps.
/// </summary> /// </summary>
public AvaloniaList<double> Dashes public AvaloniaList<double>? Dashes
{ {
get => GetValue(DashesProperty); get => GetValue(DashesProperty);
set => SetValue(DashesProperty, value); set => SetValue(DashesProperty, value);
@ -98,7 +98,7 @@ namespace Avalonia.Media
set => SetValue(OffsetProperty, value); set => SetValue(OffsetProperty, value);
} }
IReadOnlyList<double> IDashStyle.Dashes => Dashes; IReadOnlyList<double>? IDashStyle.Dashes => Dashes;
/// <summary> /// <summary>
/// Raised when the dash style changes. /// Raised when the dash style changes.

4
src/Avalonia.Base/Media/GradientBrush.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Base class for brushes that draw with a gradient. /// Base class for brushes that draw with a gradient.
/// </summary> /// </summary>
public abstract class GradientBrush : Brush, IGradientBrush public abstract class GradientBrush : Brush, IGradientBrush, IMutableBrush
{ {
/// <summary> /// <summary>
/// Defines the <see cref="SpreadMethod"/> property. /// Defines the <see cref="SpreadMethod"/> property.
@ -92,5 +92,7 @@ namespace Avalonia.Media
{ {
RaiseInvalidated(EventArgs.Empty); RaiseInvalidated(EventArgs.Empty);
} }
public abstract IImmutableBrush ToImmutable();
} }
} }

2
src/Avalonia.Base/Media/IDashStyle.cs

@ -12,7 +12,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the length of alternating dashes and gaps. /// Gets or sets the length of alternating dashes and gaps.
/// </summary> /// </summary>
IReadOnlyList<double> Dashes { get; } IReadOnlyList<double>? Dashes { get; }
/// <summary> /// <summary>
/// Gets or sets how far in the dash sequence the stroke will start. /// Gets or sets how far in the dash sequence the stroke will start.

9
src/Avalonia.Base/Media/IImmutableBrush.cs

@ -0,0 +1,9 @@
namespace Avalonia.Media;
/// <summary>
/// Represents an immutable brush which can be safely used with various threading contexts
/// </summary>
public interface IImmutableBrush : IBrush
{
}

4
src/Avalonia.Base/Media/IMutableBrush.cs

@ -7,12 +7,12 @@ namespace Avalonia.Media
/// Represents a mutable brush which can return an immutable clone of itself. /// Represents a mutable brush which can return an immutable clone of itself.
/// </summary> /// </summary>
[NotClientImplementable] [NotClientImplementable]
public interface IMutableBrush : IBrush, IAffectsRender internal interface IMutableBrush : IBrush, IAffectsRender
{ {
/// <summary> /// <summary>
/// Creates an immutable clone of the brush. /// Creates an immutable clone of the brush.
/// </summary> /// </summary>
/// <returns>The immutable clone.</returns> /// <returns>The immutable clone.</returns>
IBrush ToImmutable(); internal IImmutableBrush ToImmutable();
} }
} }

9
src/Avalonia.Base/Media/ISolidColorBrush.cs

@ -13,4 +13,13 @@ namespace Avalonia.Media
/// </summary> /// </summary>
Color Color { get; } Color Color { get; }
} }
/// <summary>
/// Fills an area with a solid color.
/// </summary>
[NotClientImplementable]
public interface IImmutableSolidColorBrush : ISolidColorBrush, IImmutableBrush
{
}
} }

4
src/Avalonia.Base/Media/ImageBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Paints an area with an <see cref="IBitmap"/>. /// Paints an area with an <see cref="IBitmap"/>.
/// </summary> /// </summary>
public class ImageBrush : TileBrush, IImageBrush public class ImageBrush : TileBrush, IImageBrush, IMutableBrush
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Visual"/> property. /// Defines the <see cref="Visual"/> property.
@ -45,7 +45,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() public IImmutableBrush ToImmutable()
{ {
return new ImmutableImageBrush(this); return new ImmutableImageBrush(this);
} }

373
src/Avalonia.Base/Media/ImmediateDrawingContext.cs

@ -0,0 +1,373 @@
using System;
using System.Collections.Generic;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
using Avalonia.Media.Immutable;
namespace Avalonia.Media
{
public sealed class ImmediateDrawingContext : IDisposable, IOptionalFeatureProvider
{
private readonly bool _ownsImpl;
private int _currentLevel;
private static ThreadSafeObjectPool<Stack<PushedState>> StateStackPool { get; } =
ThreadSafeObjectPool<Stack<PushedState>>.Default;
private static ThreadSafeObjectPool<Stack<TransformContainer>> TransformStackPool { get; } =
ThreadSafeObjectPool<Stack<TransformContainer>>.Default;
private Stack<PushedState>? _states = StateStackPool.Get();
private Stack<TransformContainer>? _transformContainers = TransformStackPool.Get();
readonly struct TransformContainer
{
public readonly Matrix LocalTransform;
public readonly Matrix ContainerTransform;
public TransformContainer(Matrix localTransform, Matrix containerTransform)
{
LocalTransform = localTransform;
ContainerTransform = containerTransform;
}
}
internal ImmediateDrawingContext(IDrawingContextImpl impl, bool ownsImpl)
{
_ownsImpl = ownsImpl;
PlatformImpl = impl;
_currentContainerTransform = impl.Transform;
}
public IDrawingContextImpl PlatformImpl { get; }
private Matrix _currentTransform = Matrix.Identity;
private Matrix _currentContainerTransform;
/// <summary>
/// Gets the current transform of the drawing context.
/// </summary>
public Matrix CurrentTransform
{
get { return _currentTransform; }
private set
{
_currentTransform = value;
var transform = _currentTransform * _currentContainerTransform;
PlatformImpl.Transform = transform;
}
}
/// <summary>
/// Draws an bitmap.
/// </summary>
/// <param name="source">The bitmap.</param>
/// <param name="rect">The rect in the output to draw to.</param>
public void DrawBitmap(IBitmap source, Rect rect)
{
_ = source ?? throw new ArgumentNullException(nameof(source));
DrawBitmap(source, new Rect(source.Size), rect);
}
/// <summary>
/// Draws an image.
/// </summary>
/// <param name="source">The bitmap.</param>
/// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param>
/// <param name="bitmapInterpolationMode">The bitmap interpolation mode.</param>
public void DrawBitmap(IBitmap source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
{
_ = source ?? throw new ArgumentNullException(nameof(source));
PlatformImpl.DrawBitmap(source.PlatformImpl, 1, sourceRect, destRect, bitmapInterpolationMode);
}
/// <summary>
/// Draws a line.
/// </summary>
/// <param name="pen">The stroke pen.</param>
/// <param name="p1">The first point of the line.</param>
/// <param name="p2">The second point of the line.</param>
public void DrawLine(ImmutablePen pen, Point p1, Point p2)
{
if (PenIsVisible(pen))
{
PlatformImpl.DrawLine(pen, p1, p2);
}
}
/// <summary>
/// Draws a rectangle with the specified Brush and Pen.
/// </summary>
/// <param name="brush">The brush used to fill the rectangle, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the rectangle, or <c>null</c> for no stroke.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="radiusX">The radius in the X dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Width/2
/// </param>
/// <param name="radiusY">The radius in the Y dimension of the rounded corners.
/// This value will be clamped to the range of 0 to Height/2
/// </param>
/// <param name="boxShadows">Box shadow effect parameters</param>
/// <remarks>
/// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks>
public void DrawRectangle(IImmutableBrush? brush, ImmutablePen? pen, Rect rect, double radiusX = 0, double radiusY = 0,
BoxShadows boxShadows = default)
{
if (brush == null && !PenIsVisible(pen))
{
return;
}
if (!MathUtilities.IsZero(radiusX))
{
radiusX = Math.Min(radiusX, rect.Width / 2);
}
if (!MathUtilities.IsZero(radiusY))
{
radiusY = Math.Min(radiusY, rect.Height / 2);
}
PlatformImpl.DrawRectangle(brush, pen, new RoundedRect(rect, radiusX, radiusY), boxShadows);
}
/// <summary>
/// Draws the outline of a rectangle.
/// </summary>
/// <param name="pen">The pen.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
public void DrawRectangle(ImmutablePen pen, Rect rect, float cornerRadius = 0.0f)
{
DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
}
/// <summary>
/// Draws an ellipse with the specified Brush and Pen.
/// </summary>
/// <param name="brush">The brush used to fill the ellipse, or <c>null</c> for no fill.</param>
/// <param name="pen">The pen used to stroke the ellipse, or <c>null</c> for no stroke.</param>
/// <param name="center">The location of the center of the ellipse.</param>
/// <param name="radiusX">The horizontal radius of the ellipse.</param>
/// <param name="radiusY">The vertical radius of the ellipse.</param>
/// <remarks>
/// The brush and the pen can both be null. If the brush is null, then no fill is performed.
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks>
public void DrawEllipse(IImmutableBrush? brush, ImmutablePen? pen, Point center, double radiusX, double radiusY)
{
if (brush == null && !PenIsVisible(pen))
{
return;
}
var originX = center.X - radiusX;
var originY = center.Y - radiusY;
var width = radiusX * 2;
var height = radiusY * 2;
PlatformImpl.DrawEllipse(brush, pen, new Rect(originX, originY, width, height));
}
/// <summary>
/// Draws a glyph run.
/// </summary>
/// <param name="foreground">The foreground brush.</param>
/// <param name="glyphRun">The glyph run.</param>
public void DrawGlyphRun(IImmutableBrush foreground, GlyphRun glyphRun)
{
_ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
PlatformImpl.DrawGlyphRun(foreground, glyphRun);
}
/// <summary>
/// Draws a filled rectangle.
/// </summary>
/// <param name="brush">The brush.</param>
/// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param>
public void FillRectangle(IImmutableBrush brush, Rect rect, float cornerRadius = 0.0f)
{
DrawRectangle(brush, null, rect, cornerRadius, cornerRadius);
}
public readonly record struct PushedState : IDisposable
{
private readonly int _level;
private readonly ImmediateDrawingContext _context;
private readonly Matrix _matrix;
private readonly PushedStateType _type;
public enum PushedStateType
{
None,
Matrix,
Opacity,
Clip,
MatrixContainer,
GeometryClip,
OpacityMask,
}
internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
{
if (context._states is null)
throw new ObjectDisposedException(nameof(ImmediateDrawingContext));
_context = context;
_type = type;
_matrix = matrix;
_level = context._currentLevel += 1;
context._states.Push(this);
}
public void Dispose()
{
if (_type == PushedStateType.None)
return;
if (_context._states is null || _context._transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
if (_context._currentLevel != _level)
throw new InvalidOperationException("Wrong Push/Pop state order");
_context._currentLevel--;
_context._states.Pop();
if (_type == PushedStateType.Matrix)
_context.CurrentTransform = _matrix;
else if (_type == PushedStateType.Clip)
_context.PlatformImpl.PopClip();
else if (_type == PushedStateType.Opacity)
_context.PlatformImpl.PopOpacity();
else if (_type == PushedStateType.GeometryClip)
_context.PlatformImpl.PopGeometryClip();
else if (_type == PushedStateType.OpacityMask)
_context.PlatformImpl.PopOpacityMask();
else if (_type == PushedStateType.MatrixContainer)
{
var cont = _context._transformContainers.Pop();
_context._currentContainerTransform = cont.ContainerTransform;
_context.CurrentTransform = cont.LocalTransform;
}
}
}
public PushedState PushClip(RoundedRect clip)
{
PlatformImpl.PushClip(clip);
return new PushedState(this, PushedState.PushedStateType.Clip);
}
/// <summary>
/// Pushes a clip rectangle.
/// </summary>
/// <param name="clip">The clip rectangle.</param>
/// <returns>A disposable used to undo the clip rectangle.</returns>
public PushedState PushClip(Rect clip)
{
PlatformImpl.PushClip(clip);
return new PushedState(this, PushedState.PushedStateType.Clip);
}
/// <summary>
/// Pushes an opacity value.
/// </summary>
/// <param name="opacity">The opacity.</param>
/// <returns>A disposable used to undo the opacity.</returns>
public PushedState PushOpacity(double opacity)
//TODO: Eliminate platform-specific push opacity call
{
PlatformImpl.PushOpacity(opacity);
return new PushedState(this, PushedState.PushedStateType.Opacity);
}
/// <summary>
/// Pushes an opacity mask.
/// </summary>
/// <param name="mask">The opacity mask.</param>
/// <param name="bounds">
/// The size of the brush's target area. TODO: Are we sure this is needed?
/// </param>
/// <returns>A disposable to undo the opacity mask.</returns>
public PushedState PushOpacityMask(IImmutableBrush mask, Rect bounds)
{
PlatformImpl.PushOpacityMask(mask, bounds);
return new PushedState(this, PushedState.PushedStateType.OpacityMask);
}
/// <summary>
/// Pushes a matrix post-transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
/// <summary>
/// Pushes a matrix pre-transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
/// <summary>
/// Sets the current matrix transformation.
/// </summary>
/// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushSetTransform(Matrix matrix)
{
var oldMatrix = CurrentTransform;
CurrentTransform = matrix;
return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
}
/// <summary>
/// Pushes a new transform context.
/// </summary>
/// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushTransformContainer()
{
if (_transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
_transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
_currentContainerTransform = CurrentTransform * _currentContainerTransform;
_currentTransform = Matrix.Identity;
return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
}
/// <summary>
/// Disposes of any resources held by the <see cref="DrawingContext"/>.
/// </summary>
public void Dispose()
{
if (_states is null || _transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
while (_states.Count != 0)
_states.Peek().Dispose();
StateStackPool.Return(_states);
_states = null;
if (_transformContainers.Count != 0)
throw new InvalidOperationException("Transform container stack is non-empty");
TransformStackPool.Return(_transformContainers);
_transformContainers = null;
if (_ownsImpl)
PlatformImpl.Dispose();
}
private static bool PenIsVisible(IPen? pen)
{
return pen?.Brush != null && pen.Thickness > 0;
}
public object? TryGetFeature(Type type) => PlatformImpl.GetFeature(type);
}
}

4
src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs

@ -17,7 +17,7 @@ namespace Avalonia.Media.Immutable
/// </summary> /// </summary>
/// <param name="dashes">The dashes collection.</param> /// <param name="dashes">The dashes collection.</param>
/// <param name="offset">The dash sequence offset.</param> /// <param name="offset">The dash sequence offset.</param>
public ImmutableDashStyle(IEnumerable<double> dashes, double offset) public ImmutableDashStyle(IEnumerable<double>? dashes, double offset)
{ {
_dashes = dashes?.ToArray() ?? Array.Empty<double>(); _dashes = dashes?.ToArray() ?? Array.Empty<double>();
Offset = offset; Offset = offset;
@ -69,7 +69,7 @@ namespace Avalonia.Media.Immutable
return hashCode; return hashCode;
} }
private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double> right) private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double>? right)
{ {
if (ReferenceEquals(left, right)) if (ReferenceEquals(left, right))
{ {

2
src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary> /// <summary>
/// A brush that draws with a gradient. /// A brush that draws with a gradient.
/// </summary> /// </summary>
public abstract class ImmutableGradientBrush : IGradientBrush public abstract class ImmutableGradientBrush : IGradientBrush, IImmutableBrush
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class. /// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class.

4
src/Avalonia.Base/Media/Immutable/ImmutablePen.cs

@ -38,15 +38,13 @@ namespace Avalonia.Media.Immutable
/// <param name="lineJoin">The line join.</param> /// <param name="lineJoin">The line join.</param>
/// <param name="miterLimit">The miter limit.</param> /// <param name="miterLimit">The miter limit.</param>
public ImmutablePen( public ImmutablePen(
IBrush? brush, IImmutableBrush? brush,
double thickness = 1.0, double thickness = 1.0,
ImmutableDashStyle? dashStyle = null, ImmutableDashStyle? dashStyle = null,
PenLineCap lineCap = PenLineCap.Flat, PenLineCap lineCap = PenLineCap.Flat,
PenLineJoin lineJoin = PenLineJoin.Miter, PenLineJoin lineJoin = PenLineJoin.Miter,
double miterLimit = 10.0) double miterLimit = 10.0)
{ {
Debug.Assert(!(brush is IMutableBrush));
Brush = brush; Brush = brush;
Thickness = thickness; Thickness = thickness;
LineCap = lineCap; LineCap = lineCap;

2
src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary> /// <summary>
/// Fills an area with a solid color. /// Fills an area with a solid color.
/// </summary> /// </summary>
public class ImmutableSolidColorBrush : ISolidColorBrush, IEquatable<ImmutableSolidColorBrush> public class ImmutableSolidColorBrush : IImmutableSolidColorBrush, IEquatable<ImmutableSolidColorBrush>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImmutableSolidColorBrush"/> class. /// Initializes a new instance of the <see cref="ImmutableSolidColorBrush"/> class.

2
src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs

@ -5,7 +5,7 @@ namespace Avalonia.Media.Immutable
/// <summary> /// <summary>
/// A brush which displays a repeating image. /// A brush which displays a repeating image.
/// </summary> /// </summary>
public abstract class ImmutableTileBrush : ITileBrush public abstract class ImmutableTileBrush : ITileBrush, IImmutableBrush
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBrush"/> class. /// Initializes a new instance of the <see cref="ImageBrush"/> class.

6
src/Avalonia.Base/Media/KnownColors.cs

@ -10,7 +10,7 @@ namespace Avalonia.Media
private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames; private static readonly IReadOnlyDictionary<string, KnownColor> _knownColorNames;
private static readonly IReadOnlyDictionary<uint, string> _knownColors; private static readonly IReadOnlyDictionary<uint, string> _knownColors;
#if !BUILDTASK #if !BUILDTASK
private static readonly Dictionary<KnownColor, ISolidColorBrush> _knownBrushes; private static readonly Dictionary<KnownColor, IImmutableSolidColorBrush> _knownBrushes;
#endif #endif
[GenerateEnumValueDictionary()] [GenerateEnumValueDictionary()]
@ -39,7 +39,7 @@ namespace Avalonia.Media
_knownColors = knownColors; _knownColors = knownColors;
#if !BUILDTASK #if !BUILDTASK
_knownBrushes = new Dictionary<KnownColor, ISolidColorBrush>(); _knownBrushes = new ();
#endif #endif
} }
@ -72,7 +72,7 @@ namespace Avalonia.Media
} }
#if !BUILDTASK #if !BUILDTASK
public static ISolidColorBrush ToBrush(this KnownColor color) public static IImmutableSolidColorBrush ToBrush(this KnownColor color)
{ {
lock (_knownBrushes) lock (_knownBrushes)
{ {

2
src/Avalonia.Base/Media/LinearGradientBrush.cs

@ -47,7 +47,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() public override IImmutableBrush ToImmutable()
{ {
return new ImmutableLinearGradientBrush(this); return new ImmutableLinearGradientBrush(this);
} }

2
src/Avalonia.Base/Media/RadialGradientBrush.cs

@ -67,7 +67,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() public override IImmutableBrush ToImmutable()
{ {
return new ImmutableRadialGradientBrush(this); return new ImmutableRadialGradientBrush(this);
} }

4
src/Avalonia.Base/Media/SolidColorBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Fills an area with a solid color. /// Fills an area with a solid color.
/// </summary> /// </summary>
public class SolidColorBrush : Brush, ISolidColorBrush public class SolidColorBrush : Brush, ISolidColorBrush, IMutableBrush
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Color"/> property. /// Defines the <see cref="Color"/> property.
@ -80,7 +80,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() public IImmutableBrush ToImmutable()
{ {
return new ImmutableSolidColorBrush(this); return new ImmutableSolidColorBrush(this);
} }

4
src/Avalonia.Base/Media/VisualBrush.cs

@ -6,7 +6,7 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Paints an area with an <see cref="Visual"/>. /// Paints an area with an <see cref="Visual"/>.
/// </summary> /// </summary>
public class VisualBrush : TileBrush, IVisualBrush public class VisualBrush : TileBrush, IVisualBrush, IMutableBrush
{ {
/// <summary> /// <summary>
/// Defines the <see cref="Visual"/> property. /// Defines the <see cref="Visual"/> property.
@ -45,7 +45,7 @@ namespace Avalonia.Media
} }
/// <inheritdoc/> /// <inheritdoc/>
public override IBrush ToImmutable() IImmutableBrush IMutableBrush.ToImmutable()
{ {
return new ImmutableVisualBrush(this); return new ImmutableVisualBrush(this);
} }

45
src/Avalonia.Base/PropertyStore/BindingEntryBase.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Threading;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -116,26 +117,42 @@ namespace Avalonia.PropertyStore
private void SetValue(BindingValue<TValue> value) private void SetValue(BindingValue<TValue> value)
{ {
if (Frame.Owner is null) static void Execute(BindingEntryBase<TValue, TSource> instance, BindingValue<TValue> value)
return; {
if (instance.Frame.Owner is null)
return;
LoggingUtils.LogIfNecessary(Frame.Owner.Owner, Property, value); LoggingUtils.LogIfNecessary(instance.Frame.Owner.Owner, instance.Property, value);
if (value.HasValue) if (value.HasValue)
{ {
if (!_hasValue || !EqualityComparer<TValue>.Default.Equals(_value, value.Value)) if (!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, value.Value))
{
instance._value = value.Value;
instance._hasValue = true;
if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
}
}
else if (value.Type != BindingValueType.DoNothing)
{ {
_value = value.Value; instance.ClearValue();
_hasValue = true; if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
if (_subscription is not null && _subscription != s_creatingQuiet) instance.Frame.Owner?.OnBindingValueCleared(instance.Property, instance.Frame.Priority);
Frame.Owner?.OnBindingValueChanged(this, Frame.Priority);
} }
} }
else if (value.Type != BindingValueType.DoNothing)
if (Dispatcher.UIThread.CheckAccess())
{ {
ClearValue(); Execute(this, value);
if (_subscription is not null && _subscription != s_creatingQuiet) }
Frame.Owner?.OnBindingValueCleared(Property, Frame.Priority); else
{
// To avoid allocating closure in the outer scope we need to capture variables
// locally. This allows us to skip most of the allocations when on UI thread.
var instance = this;
var newValue = value;
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
} }
} }

53
src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs

@ -1,5 +1,6 @@
using System; using System;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Threading;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -40,20 +41,56 @@ namespace Avalonia.PropertyStore
public void OnNext(T value) public void OnNext(T value)
{ {
if (Property.ValidateValue?.Invoke(value) != false) static void Execute(ValueStore owner, StyledPropertyBase<T> property, T value)
_owner.SetValue(Property, value, BindingPriority.LocalValue); {
if (property.ValidateValue?.Invoke(value) != false)
owner.SetValue(property, value, BindingPriority.LocalValue);
else
owner.ClearLocalValue(property);
}
if (Dispatcher.UIThread.CheckAccess())
{
Execute(_owner, Property, value);
}
else else
_owner.ClearLocalValue(Property); {
// To avoid allocating closure in the outer scope we need to capture variables
// locally. This allows us to skip most of the allocations when on UI thread.
var instance = _owner;
var property = Property;
var newValue = value;
Dispatcher.UIThread.Post(() => Execute(instance, property, newValue));
}
} }
public void OnNext(BindingValue<T> value) public void OnNext(BindingValue<T> value)
{ {
LoggingUtils.LogIfNecessary(_owner.Owner, Property, value); static void Execute(LocalValueBindingObserver<T> instance, BindingValue<T> value)
{
var owner = instance._owner;
var property = instance.Property;
LoggingUtils.LogIfNecessary(owner.Owner, property, value);
if (value.HasValue) if (value.HasValue)
_owner.SetValue(Property, value.Value, BindingPriority.LocalValue); owner.SetValue(property, value.Value, BindingPriority.LocalValue);
else if (value.Type != BindingValueType.DataValidationError) else if (value.Type != BindingValueType.DataValidationError)
_owner.ClearLocalValue(Property); owner.ClearLocalValue(property);
}
if (Dispatcher.UIThread.CheckAccess())
{
Execute(this, value);
}
else
{
// To avoid allocating closure in the outer scope we need to capture variables
// locally. This allows us to skip most of the allocations when on UI thread.
var instance = this;
var newValue = value;
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
} }
} }
} }

53
src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs

@ -1,5 +1,7 @@
using System; using System;
using System.Security.Cryptography;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Threading;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -34,28 +36,47 @@ namespace Avalonia.PropertyStore
public void OnNext(object? value) public void OnNext(object? value)
{ {
if (value is BindingNotification n) static void Execute(LocalValueUntypedBindingObserver<T> instance, object? value)
{ {
value = n.Value; var owner = instance._owner;
LoggingUtils.LogIfNecessary(_owner.Owner, Property, n); var property = instance.Property;
}
if (value == AvaloniaProperty.UnsetValue) if (value is BindingNotification n)
{ {
_owner.ClearLocalValue(Property); value = n.Value;
} LoggingUtils.LogIfNecessary(owner.Owner, property, n);
else if (value == BindingOperations.DoNothing) }
{
// Do nothing! if (value == AvaloniaProperty.UnsetValue)
{
owner.ClearLocalValue(property);
}
else if (value == BindingOperations.DoNothing)
{
// Do nothing!
}
else if (UntypedValueUtils.TryConvertAndValidate(property, value, out var typedValue))
{
owner.SetValue(property, typedValue, BindingPriority.LocalValue);
}
else
{
owner.ClearLocalValue(property);
LoggingUtils.LogInvalidValue(owner.Owner, property, typeof(T), value);
}
} }
else if (UntypedValueUtils.TryConvertAndValidate(Property, value, out var typedValue))
if (Dispatcher.UIThread.CheckAccess())
{ {
_owner.SetValue(Property, typedValue, BindingPriority.LocalValue); Execute(this, value);
} }
else else if (value != BindingOperations.DoNothing)
{ {
_owner.ClearLocalValue(Property); // To avoid allocating closure in the outer scope we need to capture variables
LoggingUtils.LogInvalidValue(_owner.Owner, Property, typeof(T), value); // locally. This allows us to skip most of the allocations when on UI thread.
var instance = this;
var newValue = value;
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
} }
} }
} }

2
src/Avalonia.Base/Rendering/Composition/Animations/AnimationInstanceBase.cs

@ -80,4 +80,6 @@ internal abstract class AnimationInstanceBase : IAnimationInstance
_invalidated = true; _invalidated = true;
TargetObject.NotifyAnimatedValueChanged(Property); TargetObject.NotifyAnimatedValueChanged(Property);
} }
public void OnTick() => Invalidate();
} }

2
src/Avalonia.Base/Rendering/Composition/Animations/IAnimationInstance.cs

@ -6,7 +6,7 @@ using Avalonia.Rendering.Composition.Server;
namespace Avalonia.Rendering.Composition.Animations namespace Avalonia.Rendering.Composition.Animations
{ {
internal interface IAnimationInstance internal interface IAnimationInstance : IServerClockItem
{ {
ServerObject TargetObject { get; } ServerObject TargetObject { get; }
ExpressionVariant Evaluate(TimeSpan now, ExpressionVariant currentValue); ExpressionVariant Evaluate(TimeSpan now, ExpressionVariant currentValue);

37
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisual.cs

@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Numerics;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
namespace Avalonia.Rendering.Composition;
public class CompositionCustomVisual : CompositionContainerVisual
{
private List<object>? _messages;
internal CompositionCustomVisual(Compositor compositor, CompositionCustomVisualHandler handler)
: base(compositor, new ServerCompositionCustomVisual(compositor.Server, handler))
{
}
public void SendHandlerMessage(object message)
{
(_messages ??= new()).Add(message);
RegisterForSerialization();
}
private protected override void SerializeChangesCore(BatchStreamWriter writer)
{
base.SerializeChangesCore(writer);
if (_messages == null || _messages.Count == 0)
writer.Write(0);
else
{
writer.Write(_messages.Count);
foreach (var m in _messages)
writer.WriteObject(m);
_messages.Clear();
}
}
}

65
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs

@ -0,0 +1,65 @@
using System;
using System.Numerics;
using Avalonia.Media;
using Avalonia.Rendering.Composition.Server;
namespace Avalonia.Rendering.Composition;
public abstract class CompositionCustomVisualHandler
{
private ServerCompositionCustomVisual? _host;
public virtual void OnMessage(object message)
{
}
public virtual void OnAnimationFrameUpdate()
{
}
public abstract void OnRender(ImmediateDrawingContext drawingContext);
void VerifyAccess()
{
if (_host == null)
throw new InvalidOperationException("Object is not yet attached to the compositor");
_host.Compositor.VerifyAccess();
}
protected Vector2 EffectiveSize
{
get
{
VerifyAccess();
return _host!.Size;
}
}
protected TimeSpan CompositionNow
{
get
{
VerifyAccess();
return _host!.Compositor.ServerNow;
}
}
public virtual Rect GetRenderBounds() =>
new(0, 0, EffectiveSize.X, EffectiveSize.Y);
internal void Attach(ServerCompositionCustomVisual visual) => _host = visual;
protected void Invalidate()
{
VerifyAccess();
_host!.HandlerInvalidate();
}
protected void RegisterForNextAnimationFrameUpdate()
{
VerifyAccess();
_host!.HandlerRegisterForNextAnimationFrameUpdate();
}
}

2
src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs

@ -33,4 +33,6 @@ public partial class Compositor
public CompositionSolidColorVisual CreateSolidColorVisual() => public CompositionSolidColorVisual CreateSolidColorVisual() =>
new(this, new ServerCompositionSolidColorVisual(Server)); new(this, new ServerCompositionSolidColorVisual(Server));
public CompositionCustomVisual CreateCustomVisual(CompositionCustomVisualHandler handler) => new(this, handler);
} }

6
src/Avalonia.Base/Rendering/Composition/Server/IServerClockItem.cs

@ -0,0 +1,6 @@
namespace Avalonia.Rendering.Composition.Server;
internal interface IServerClockItem
{
void OnTick();
}

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -87,8 +87,6 @@ namespace Avalonia.Rendering.Composition.Server
} }
_renderTarget ??= _compositor.CreateRenderTarget(_surfaces()); _renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
Compositor.UpdateServerTime();
if(_dirtyRect.IsDefault && !_redrawRequested) if(_dirtyRect.IsDefault && !_redrawRequested)
return; return;

16
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -216,13 +216,29 @@ namespace Avalonia.Rendering.Composition.Server
partial void OnRootChanging() partial void OnRootChanging()
{ {
if (Root != null) if (Root != null)
{
Root.RemoveVisual(this); Root.RemoveVisual(this);
OnDetachedFromRoot(Root);
}
}
protected virtual void OnDetachedFromRoot(ServerCompositionTarget target)
{
} }
partial void OnRootChanged() partial void OnRootChanged()
{ {
if (Root != null) if (Root != null)
{
Root.AddVisual(this); Root.AddVisual(this);
OnAttachedToRoot(Root);
}
}
protected virtual void OnAttachedToRoot(ServerCompositionTarget target)
{
} }
protected override void ValuesInvalidated() protected override void ValuesInvalidated()

44
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Animations;
@ -26,11 +27,12 @@ namespace Avalonia.Rendering.Composition.Server
public Stopwatch Clock { get; } = Stopwatch.StartNew(); public Stopwatch Clock { get; } = Stopwatch.StartNew();
public TimeSpan ServerNow { get; private set; } public TimeSpan ServerNow { get; private set; }
private List<ServerCompositionTarget> _activeTargets = new(); private List<ServerCompositionTarget> _activeTargets = new();
private HashSet<IAnimationInstance> _activeAnimations = new(); private HashSet<IServerClockItem> _clockItems = new();
private List<IAnimationInstance> _animationsToUpdate = new(); private List<IServerClockItem> _clockItemsToUpdate = new();
internal BatchStreamObjectPool<object?> BatchObjectPool; internal BatchStreamObjectPool<object?> BatchObjectPool;
internal BatchStreamMemoryPool BatchMemoryPool; internal BatchStreamMemoryPool BatchMemoryPool;
private object _lock = new object(); private object _lock = new object();
private Thread? _safeThread;
public PlatformRenderInterfaceContextManager RenderInterface { get; } public PlatformRenderInterfaceContextManager RenderInterface { get; }
internal static readonly object RenderThreadJobsStartMarker = new(); internal static readonly object RenderThreadJobsStartMarker = new();
internal static readonly object RenderThreadJobsEndMarker = new(); internal static readonly object RenderThreadJobsEndMarker = new();
@ -129,22 +131,31 @@ namespace Avalonia.Rendering.Composition.Server
{ {
lock (_lock) lock (_lock)
{ {
RenderCore(); try
{
_safeThread = Thread.CurrentThread;
RenderCore();
}
finally
{
_safeThread = null;
}
} }
} }
private void RenderCore() private void RenderCore()
{ {
UpdateServerTime();
ApplyPendingBatches(); ApplyPendingBatches();
CompletePendingBatches(); CompletePendingBatches();
foreach(var animation in _activeAnimations) foreach(var animation in _clockItems)
_animationsToUpdate.Add(animation); _clockItemsToUpdate.Add(animation);
foreach(var animation in _animationsToUpdate) foreach (var animation in _clockItemsToUpdate)
animation.Invalidate(); animation.OnTick();
_animationsToUpdate.Clear(); _clockItemsToUpdate.Clear();
try try
{ {
@ -168,16 +179,23 @@ namespace Avalonia.Rendering.Composition.Server
_activeTargets.Remove(target); _activeTargets.Remove(target);
} }
public void AddToClock(IAnimationInstance animationInstance) => public void AddToClock(IServerClockItem item) =>
_activeAnimations.Add(animationInstance); _clockItems.Add(item);
public void RemoveFromClock(IAnimationInstance animationInstance) => public void RemoveFromClock(IServerClockItem item) =>
_activeAnimations.Remove(animationInstance); _clockItems.Remove(item);
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{ {
using (RenderInterface.EnsureCurrent()) using (RenderInterface.EnsureCurrent())
return RenderInterface.CreateRenderTarget(surfaces); return RenderInterface.CreateRenderTarget(surfaces);
} }
public bool CheckAccess() => _safeThread == Thread.CurrentThread;
public void VerifyAccess()
{
if (!CheckAccess())
throw new InvalidOperationException("This object can be only accessed under compositor lock");
}
} }
} }

82
src/Avalonia.Base/Rendering/Composition/Server/ServerCustomCompositionVisual.cs

@ -0,0 +1,82 @@
using System;
using System.Numerics;
using Avalonia.Logging;
using Avalonia.Media;
using Avalonia.Rendering.Composition.Transport;
namespace Avalonia.Rendering.Composition.Server;
internal class ServerCompositionCustomVisual : ServerCompositionContainerVisual, IServerClockItem
{
private readonly CompositionCustomVisualHandler _handler;
private bool _wantsNextAnimationFrameAfterTick;
internal ServerCompositionCustomVisual(ServerCompositor compositor, CompositionCustomVisualHandler handler) : base(compositor)
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
_handler.Attach(this);
}
protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt)
{
base.DeserializeChangesCore(reader, commitedAt);
var count = reader.Read<int>();
for (var c = 0; c < count; c++)
{
try
{
_handler.OnMessage(reader.ReadObject()!);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(_handler, $"Exception in {_handler.GetType().Name}.{nameof(CompositionCustomVisualHandler.OnMessage)} {{0}}", e);
}
}
}
public void OnTick()
{
_wantsNextAnimationFrameAfterTick = false;
_handler.OnAnimationFrameUpdate();
if (!_wantsNextAnimationFrameAfterTick)
Compositor.RemoveFromClock(this);
}
public override Rect OwnContentBounds => _handler.GetRenderBounds();
protected override void OnAttachedToRoot(ServerCompositionTarget target)
{
if (_wantsNextAnimationFrameAfterTick)
Compositor.AddToClock(this);
base.OnAttachedToRoot(target);
}
protected override void OnDetachedFromRoot(ServerCompositionTarget target)
{
Compositor.RemoveFromClock(this);
base.OnDetachedFromRoot(target);
}
internal void HandlerInvalidate() => ValuesInvalidated();
internal void HandlerRegisterForNextAnimationFrameUpdate()
{
_wantsNextAnimationFrameAfterTick = true;
if (Root != null)
Compositor.AddToClock(this);
}
protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{
using var context = new ImmediateDrawingContext(canvas, false);
try
{
_handler.OnRender(context);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(_handler, $"Exception in {_handler.GetType().Name}.{nameof(CompositionCustomVisualHandler.OnRender)} {{0}}", e);
}
}
}

2
src/Avalonia.Base/Rendering/Composition/VisualCollection.cs

@ -69,7 +69,7 @@ namespace Avalonia.Rendering.Composition
{ {
if (item.Parent != null) if (item.Parent != null)
throw new InvalidOperationException("Visual already has a parent"); throw new InvalidOperationException("Visual already has a parent");
item.Parent = item; item.Parent = _owner;
} }
} }
} }

3
src/Avalonia.Base/Visual.cs

@ -540,6 +540,9 @@ namespace Avalonia
OnDetachedFromVisualTree(e); OnDetachedFromVisualTree(e);
if (CompositionVisual != null) if (CompositionVisual != null)
{ {
if (ChildCompositionVisual != null)
CompositionVisual.Children.Remove(ChildCompositionVisual);
CompositionVisual.DrawList = null; CompositionVisual.DrawList = null;
CompositionVisual = null; CompositionVisual = null;
} }

7
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -50,10 +50,13 @@ namespace Avalonia.Controls
InheritsWidth = true; InheritsWidth = true;
} }
internal DataGrid OwningGrid /// <summary>
/// Gets the <see cref="T:Avalonia.Controls.DataGrid"/> control that contains this column.
/// </summary>
protected internal DataGrid OwningGrid
{ {
get; get;
set; internal set;
} }
internal int Index internal int Index

10
src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs

@ -22,14 +22,18 @@ namespace Avalonia.Controls
return DefaultValueConverter.Instance.Convert(value, targetType, parameter, culture); return DefaultValueConverter.Instance.Convert(value, targetType, parameter, culture);
} }
// This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
// comparison, but in this case we want to explicitly check for Empty and not Null.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ {
if (targetType != null && targetType.IsNullableType()) if (targetType != null && targetType.IsNullableType())
{ {
var strValue = value as string; var strValue = value as string;
if (string.IsNullOrEmpty(strValue))
// This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
// comparison, but in this case we want to explicitly check for Empty and not Null.
#pragma warning disable CA1820
if (strValue == string.Empty)
#pragma warning restore CA1820
{ {
return null; return null;
} }

14
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -83,12 +83,12 @@ namespace Avalonia.Controls.ApplicationLifetimes
public void Shutdown(int exitCode = 0) public void Shutdown(int exitCode = 0)
{ {
DoShutdown(new ShutdownRequestedEventArgs(), true, exitCode); DoShutdown(new ShutdownRequestedEventArgs(), true, true, exitCode);
} }
public bool TryShutdown(int exitCode = 0) public bool TryShutdown(int exitCode = 0)
{ {
return DoShutdown(new ShutdownRequestedEventArgs(), false, exitCode); return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode);
} }
public int Start(string[] args) public int Start(string[] args)
@ -134,7 +134,11 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime = null; _activeLifetime = null;
} }
private bool DoShutdown(ShutdownRequestedEventArgs e, bool force = false, int exitCode = 0) private bool DoShutdown(
ShutdownRequestedEventArgs e,
bool isProgrammatic,
bool force = false,
int exitCode = 0)
{ {
if (!force) if (!force)
{ {
@ -159,7 +163,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
{ {
if (w.Owner is null) if (w.Owner is null)
{ {
w.Close(); w.CloseCore(WindowCloseReason.ApplicationShutdown, isProgrammatic);
} }
} }
@ -183,7 +187,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
return true; return true;
} }
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e); private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e, false);
} }
public class ClassicDesktopStyleApplicationLifetimeOptions public class ClassicDesktopStyleApplicationLifetimeOptions

9
src/Avalonia.Controls/NativeMenuBar.cs

@ -23,15 +23,6 @@ namespace Avalonia.Controls
}); });
} }
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenu))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItem))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItemBase))]
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(NativeMenuItemSeparator))]
public NativeMenuBar()
{
}
public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable) public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable)
{ {
menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable); menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable);

7
src/Avalonia.Controls/NativeMenuItemSeparator.cs

@ -1,7 +1,10 @@
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public class NativeMenuItemSeparator : NativeMenuItemBase public class NativeMenuItemSeparator : NativeMenuItem
{ {
public NativeMenuItemSeparator()
{
Header = "-";
}
} }
} }

2
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -68,7 +68,7 @@ namespace Avalonia.Platform
/// Gets or sets a method called before the underlying implementation is destroyed. /// Gets or sets a method called before the underlying implementation is destroyed.
/// Return true to prevent the underlying implementation from closing. /// Return true to prevent the underlying implementation from closing.
/// </summary> /// </summary>
Func<bool> Closing { get; set; } Func<WindowCloseReason, bool> Closing { get; set; }
/// <summary> /// <summary>
/// Gets a value to indicate if the platform was able to extend client area to non-client area. /// Gets a value to indicate if the platform was able to extend client area to non-client area.

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -109,7 +109,7 @@ namespace Avalonia.Controls.Presenters
static TextPresenter() static TextPresenter()
{ {
AffectsRender<TextPresenter>(CaretBrushProperty, SelectionBrushProperty); AffectsRender<TextPresenter>(CaretBrushProperty, SelectionBrushProperty, TextElement.ForegroundProperty);
} }
public TextPresenter() public TextPresenter()

37
src/Avalonia.Controls/Window.cs

@ -430,14 +430,14 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Fired before a window is closed. /// Fired before a window is closed.
/// </summary> /// </summary>
public event EventHandler<CancelEventArgs>? Closing; public event EventHandler<WindowClosingEventArgs>? Closing;
/// <summary> /// <summary>
/// Closes the window. /// Closes the window.
/// </summary> /// </summary>
public void Close() public void Close()
{ {
Close(false); CloseCore(WindowCloseReason.WindowClosing, true);
} }
/// <summary> /// <summary>
@ -453,16 +453,16 @@ namespace Avalonia.Controls
public void Close(object dialogResult) public void Close(object dialogResult)
{ {
_dialogResult = dialogResult; _dialogResult = dialogResult;
Close(false); CloseCore(WindowCloseReason.WindowClosing, true);
} }
internal void Close(bool ignoreCancel) internal void CloseCore(WindowCloseReason reason, bool isProgrammatic)
{ {
bool close = true; bool close = true;
try try
{ {
if (!ignoreCancel && ShouldCancelClose()) if (ShouldCancelClose(new WindowClosingEventArgs(reason, isProgrammatic)))
{ {
close = false; close = false;
} }
@ -480,9 +480,10 @@ namespace Avalonia.Controls
/// Handles a closing notification from <see cref="IWindowImpl.Closing"/>. /// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
/// <returns>true if closing is cancelled. Otherwise false.</returns> /// <returns>true if closing is cancelled. Otherwise false.</returns>
/// </summary> /// </summary>
protected virtual bool HandleClosing() /// <param name="reason">The reason the window is closing.</param>
private protected virtual bool HandleClosing(WindowCloseReason reason)
{ {
if (!ShouldCancelClose()) if (!ShouldCancelClose(new WindowClosingEventArgs(reason, false)))
{ {
CloseInternal(); CloseInternal();
return false; return false;
@ -510,20 +511,22 @@ namespace Avalonia.Controls
_showingAsDialog = false; _showingAsDialog = false;
} }
private bool ShouldCancelClose(CancelEventArgs? args = null) private bool ShouldCancelClose(WindowClosingEventArgs args)
{ {
if (args is null)
{
args = new CancelEventArgs();
}
bool canClose = true; bool canClose = true;
foreach (var (child, _) in _children.ToArray()) if (_children.Count > 0)
{ {
if (child.ShouldCancelClose(args)) var childArgs = args.CloseReason == WindowCloseReason.WindowClosing ?
new WindowClosingEventArgs(WindowCloseReason.OwnerWindowClosing, args.IsProgrammatic) :
args;
foreach (var (child, _) in _children.ToArray())
{ {
canClose = false; if (child.ShouldCancelClose(childArgs))
{
canClose = false;
}
} }
} }
@ -1033,7 +1036,7 @@ namespace Avalonia.Controls
/// overridden method must call <see cref="OnClosing"/> on the base class if the /// overridden method must call <see cref="OnClosing"/> on the base class if the
/// <see cref="Closing"/> event needs to be raised. /// <see cref="Closing"/> event needs to be raised.
/// </remarks> /// </remarks>
protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e); protected virtual void OnClosing(WindowClosingEventArgs e) => Closing?.Invoke(this, e);
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {

57
src/Avalonia.Controls/WindowClosingEventArgs.cs

@ -0,0 +1,57 @@
using System.ComponentModel;
namespace Avalonia.Controls
{
/// <summary>
/// Specifies the reason that a window was closed.
/// </summary>
public enum WindowCloseReason
{
/// <summary>
/// The cause of the closure was not provided by the underlying platform.
/// </summary>
Undefined,
/// <summary>
/// The window itself was requested to close.
/// </summary>
WindowClosing,
/// <summary>
/// The window is closing due to a parent/owner window closing.
/// </summary>
OwnerWindowClosing,
/// <summary>
/// The window is closing due to the application shutting down.
/// </summary>
ApplicationShutdown,
/// <summary>
/// The window is closing due to the operating system shutting down.
/// </summary>
OSShutdown,
}
/// <summary>
/// Provides data for the <see cref="Window.Closing"/> event.
/// </summary>
public class WindowClosingEventArgs : CancelEventArgs
{
internal WindowClosingEventArgs(WindowCloseReason reason, bool isProgrammatic)
{
CloseReason = reason;
IsProgrammatic = isProgrammatic;
}
/// <summary>
/// Gets a value that indicates why the window is being closed.
/// </summary>
public WindowCloseReason CloseReason { get; }
/// <summary>
/// Gets a value indicating whether the window is being closed programmatically.
/// </summary>
public bool IsProgrammatic { get; }
}
}

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -42,7 +42,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<PixelPoint> PositionChanged { get; set; } public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public IPlatformHandle Handle { get; } public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; } public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState> WindowStateChanged { get; set; }

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -32,7 +32,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, PlatformResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public Action LostFocus { get; set; } public Action LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice(); public IMouseDevice MouseDevice { get; } = new MouseDevice();

2
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -175,7 +175,7 @@ namespace Avalonia.Headless
} }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
class FramebufferProxy : ILockedFramebuffer class FramebufferProxy : ILockedFramebuffer
{ {

4
src/Avalonia.Native/WindowImpl.cs

@ -48,7 +48,7 @@ namespace Avalonia.Native
{ {
if (_parent.Closing != null) if (_parent.Closing != null)
{ {
return _parent.Closing().AsComBool(); return _parent.Closing(WindowCloseReason.WindowClosing).AsComBool();
} }
return true.AsComBool(); return true.AsComBool();
@ -207,7 +207,7 @@ namespace Avalonia.Native
// NO OP on OSX // NO OP on OSX
} }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public ITopLevelNativeMenuExporter NativeMenuExporter { get; } public ITopLevelNativeMenuExporter NativeMenuExporter { get; }

17
src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml

@ -9,17 +9,16 @@
IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}" IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}"> Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
<Menu.Styles> <Menu.Styles>
<!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954 --> <Style Selector="MenuItem" x:DataType="NativeMenuItem">
<Style Selector="MenuItem"> <Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Header" Value="{ReflectionBinding Header}"/> <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="IsEnabled" Value="{ReflectionBinding IsEnabled}"/> <Setter Property="InputGesture" Value="{Binding Gesture}"/>
<Setter Property="InputGesture" Value="{ReflectionBinding Gesture}"/> <Setter Property="Items" Value="{Binding Menu.Items}"/>
<Setter Property="Items" Value="{ReflectionBinding Menu.Items}"/> <Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Command" Value="{ReflectionBinding Command}"/> <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
<Setter Property="CommandParameter" Value="{ReflectionBinding CommandParameter}"/>
<Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/> <Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/>
<!--NativeMenuItem is IBitmap and MenuItem is Image--> <!--NativeMenuItem is IBitmap and MenuItem is Image-->
<Setter Property="Icon" Value="{ReflectionBinding Icon , Converter={StaticResource AvaloniaThemesFluentNativeMenuBarIBitmapToImageConverter}}"/> <Setter Property="Icon" Value="{Binding Icon , Converter={StaticResource AvaloniaThemesFluentNativeMenuBarIBitmapToImageConverter}}"/>
</Style> </Style>
</Menu.Styles> </Menu.Styles>
</Menu> </Menu>

21
src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml

@ -9,17 +9,16 @@
<Menu IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}" <Menu IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}"> Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
<Menu.Styles> <Menu.Styles>
<!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954 --> <Style Selector="MenuItem" x:DataType="NativeMenuItem">
<Style Selector="MenuItem"> <Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Header" Value="{ReflectionBinding Header}" /> <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="IsEnabled" Value="{ReflectionBinding IsEnabled}" /> <Setter Property="InputGesture" Value="{Binding Gesture}"/>
<Setter Property="InputGesture" Value="{ReflectionBinding Gesture}" /> <Setter Property="Items" Value="{Binding Menu.Items}"/>
<Setter Property="Items" Value="{ReflectionBinding Menu.Items}" /> <Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Command" Value="{ReflectionBinding Command}" /> <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
<Setter Property="CommandParameter" Value="{ReflectionBinding CommandParameter}" /> <Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True"/>
<Setter Property="(NativeMenuBar.EnableMenuItemClickForwarding)" Value="True" /> <!--NativeMenuItem is IBitmap and MenuItem is Image-->
<!-- NativeMenuItem is IBitmap and MenuItem is Image --> <Setter Property="Icon" Value="{Binding Icon , Converter={StaticResource AvaloniaThemesSimpleNativeMenuBarIBitmapToImageConverter}}"/>
<Setter Property="Icon" Value="{ReflectionBinding Icon, Converter={StaticResource AvaloniaThemesSimpleNativeMenuBarIBitmapToImageConverter}}" />
</Style> </Style>
</Menu.Styles> </Menu.Styles>
</Menu> </Menu>

4
src/Avalonia.X11/X11Window.cs

@ -358,7 +358,7 @@ namespace Avalonia.X11
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState> WindowStateChanged { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged public Action<WindowTransparencyLevel> TransparencyLevelChanged
@ -546,7 +546,7 @@ namespace Avalonia.X11
{ {
if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW) if (ev.ClientMessageEvent.ptr1 == _x11.Atoms.WM_DELETE_WINDOW)
{ {
if (Closing?.Invoke() != true) if (Closing?.Invoke(WindowCloseReason.WindowClosing) != true)
Dispose(); Dispose();
} }
else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST) else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)

9
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -1137,11 +1137,14 @@ namespace Avalonia.Skia
if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0) if (pen.DashStyle?.Dashes != null && pen.DashStyle.Dashes.Count > 0)
{ {
var srcDashes = pen.DashStyle.Dashes; var srcDashes = pen.DashStyle.Dashes;
var dashesArray = new float[srcDashes.Count];
for (var i = 0; i < srcDashes.Count; ++i) var count = srcDashes.Count % 2 == 0 ? srcDashes.Count : srcDashes.Count * 2;
var dashesArray = new float[count];
for (var i = 0; i < count; ++i)
{ {
dashesArray[i] = (float) srcDashes[i] * paint.StrokeWidth; dashesArray[i] = (float) srcDashes[i % srcDashes.Count] * paint.StrokeWidth;
} }
var offset = (float)(pen.DashStyle.Offset * pen.Thickness); var offset = (float)(pen.DashStyle.Offset * pen.Thickness);

2
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1409,7 +1409,7 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool TranslateMessage(ref MSG lpMsg); public static extern bool TranslateMessage(ref MSG lpMsg);
[DllImport("user32.dll")] [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance); public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")]

3
src/Windows/Avalonia.Win32/OpenGl/Angle/AngleWin32PlatformGraphics.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.ExceptionServices;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Angle;
@ -88,8 +87,6 @@ internal class AngleWin32PlatformGraphics : IPlatformGraphics
return null; return null;
} }
return new AngleWin32PlatformGraphics(egl, AngleWin32EglDisplay.CreateSharedD3D11Display(egl));
foreach (var api in (options?.AllowedPlatformApis ?? new [] foreach (var api in (options?.AllowedPlatformApis ?? new []
{ {
AngleOptions.PlatformApi.DirectX11 AngleOptions.PlatformApi.DirectX11

6
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -10,6 +10,7 @@ using Avalonia.Controls.Remote;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Automation; using Avalonia.Win32.Automation;
using Avalonia.Win32.Input; using Avalonia.Win32.Input;
using Avalonia.Win32.Interop.Automation; using Avalonia.Win32.Interop.Automation;
@ -69,7 +70,7 @@ namespace Avalonia.Win32
case WindowsMessage.WM_CLOSE: case WindowsMessage.WM_CLOSE:
{ {
bool? preventClosing = Closing?.Invoke(); bool? preventClosing = Closing?.Invoke(WindowCloseReason.WindowClosing);
if (preventClosing == true) if (preventClosing == true)
{ {
return IntPtr.Zero; return IntPtr.Zero;
@ -106,6 +107,9 @@ namespace Avalonia.Win32
_touchDevice?.Dispose(); _touchDevice?.Dispose();
//Free other resources //Free other resources
Dispose(); Dispose();
// Schedule cleanup of anything that requires window to be destroyed
Dispatcher.UIThread.Post(AfterCloseCleanup);
return IntPtr.Zero; return IntPtr.Zero;
} }

17
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -191,7 +191,7 @@ namespace Avalonia.Win32
public Action Activated { get; set; } public Action Activated { get; set; }
public Func<bool> Closing { get; set; } public Func<WindowCloseReason, bool> Closing { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
@ -643,12 +643,6 @@ namespace Avalonia.Win32
_hwnd = IntPtr.Zero; _hwnd = IntPtr.Zero;
} }
if (_className != null)
{
UnregisterClass(_className, GetModuleHandle(null));
_className = null;
}
_framebuffer.Dispose(); _framebuffer.Dispose();
} }
@ -1144,6 +1138,15 @@ namespace Avalonia.Win32
} }
} }
private void AfterCloseCleanup()
{
if (_className != null)
{
UnregisterClass(_className, GetModuleHandle(null));
_className = null;
}
}
private void MaximizeWithoutCoveringTaskbar() private void MaximizeWithoutCoveringTaskbar()
{ {
IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST); IntPtr monitor = MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST);

3
tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj

@ -17,6 +17,9 @@
<EmbeddedResource Remove="..\Avalonia.RenderTests\Assets\NotoColorEmoji.ttf" /> <EmbeddedResource Remove="..\Avalonia.RenderTests\Assets\NotoColorEmoji.ttf" />
<EmbeddedResource Include="Media\TextFormatting\BreakPairTable.txt" /> <EmbeddedResource Include="Media\TextFormatting\BreakPairTable.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Nito.AsyncEx.Context" Version="5.1.2" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj" />

115
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
using Nito.AsyncEx;
using Xunit; using Xunit;
#nullable enable #nullable enable
@ -920,6 +921,120 @@ namespace Avalonia.Base.UnitTests
} }
} }
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void Typed_Bind_Executes_On_UIThread(BindingPriority priority)
{
AsyncContext.Run(async () =>
{
var target = new Class1();
var source = new Subject<string>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
var services = new TestServices(
threadingInterface: threadingInterfaceMock.Object);
target.PropertyChanged += (s, e) =>
{
Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
++raised;
};
using (UnitTestApplication.Start(services))
{
target.Bind(Class1.FooProperty, source, priority);
await Task.Run(() => source.OnNext("foobar"));
Dispatcher.UIThread.RunJobs();
Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
Assert.Equal(1, raised);
}
});
}
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void Untyped_Bind_Executes_On_UIThread(BindingPriority priority)
{
AsyncContext.Run(async () =>
{
var target = new Class1();
var source = new Subject<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
var services = new TestServices(
threadingInterface: threadingInterfaceMock.Object);
target.PropertyChanged += (s, e) =>
{
Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
++raised;
};
using (UnitTestApplication.Start(services))
{
target.Bind(Class1.FooProperty, source, priority);
await Task.Run(() => source.OnNext("foobar"));
Dispatcher.UIThread.RunJobs();
Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
Assert.Equal(1, raised);
}
});
}
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void BindingValue_Bind_Executes_On_UIThread(BindingPriority priority)
{
AsyncContext.Run(async () =>
{
var target = new Class1();
var source = new Subject<BindingValue<string>>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
var services = new TestServices(
threadingInterface: threadingInterfaceMock.Object);
target.PropertyChanged += (s, e) =>
{
Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
++raised;
};
using (UnitTestApplication.Start(services))
{
target.Bind(Class1.FooProperty, source, priority);
await Task.Run(() => source.OnNext("foobar"));
Dispatcher.UIThread.RunJobs();
Assert.Equal("foobar", target.GetValue(Class1.FooProperty));
Assert.Equal(1, raised);
}
});
}
[Fact] [Fact]
public async Task Bind_With_Scheduler_Executes_On_Scheduler() public async Task Bind_With_Scheduler_Executes_On_Scheduler()
{ {

44
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -7,8 +7,10 @@ using System.Threading.Tasks;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
using Nito.AsyncEx;
using Xunit; using Xunit;
namespace Avalonia.Base.UnitTests namespace Avalonia.Base.UnitTests
@ -519,25 +521,39 @@ namespace Avalonia.Base.UnitTests
} }
[Fact] [Fact]
public async Task Bind_Executes_On_UIThread() public void Bind_Executes_On_UIThread()
{ {
var target = new Class1(); AsyncContext.Run(async () =>
var source = new Subject<object>(); {
var currentThreadId = Thread.CurrentThread.ManagedThreadId; var target = new Class1();
var source = new Subject<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var raised = 0;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>(); var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread) threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId); .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
var services = new TestServices( var services = new TestServices(
threadingInterface: threadingInterfaceMock.Object); threadingInterface: threadingInterfaceMock.Object);
using (UnitTestApplication.Start(services)) target.PropertyChanged += (s, e) =>
{ {
target.Bind(Class1.FooProperty, source); Assert.Equal(currentThreadId, Thread.CurrentThread.ManagedThreadId);
++raised;
};
await Task.Run(() => source.OnNext("foobar")); using (UnitTestApplication.Start(services))
} {
target.Bind(Class1.FooProperty, source);
await Task.Run(() => source.OnNext("foobar"));
Dispatcher.UIThread.RunJobs();
Assert.Equal("foobar", target.Foo);
Assert.Equal(1, raised);
}
});
} }
[Fact] [Fact]

8
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -155,12 +155,16 @@ namespace Avalonia.Controls.UnitTests
window.Closing += (sender, e) => window.Closing += (sender, e) =>
{ {
Assert.Equal(WindowCloseReason.WindowClosing, e.CloseReason);
Assert.Equal(programaticClose, e.IsProgrammatic);
count++; count++;
windowClosing = count; windowClosing = count;
}; };
child.Closing += (sender, e) => child.Closing += (sender, e) =>
{ {
Assert.Equal(WindowCloseReason.OwnerWindowClosing, e.CloseReason);
Assert.Equal(programaticClose, e.IsProgrammatic);
count++; count++;
childClosing = count; childClosing = count;
}; };
@ -186,7 +190,7 @@ namespace Avalonia.Controls.UnitTests
} }
else else
{ {
var cancel = window.PlatformImpl.Closing(); var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(false, cancel); Assert.Equal(false, cancel);
} }
@ -248,7 +252,7 @@ namespace Avalonia.Controls.UnitTests
} }
else else
{ {
var cancel = window.PlatformImpl.Closing(); var cancel = window.PlatformImpl.Closing(WindowCloseReason.WindowClosing);
Assert.Equal(true, cancel); Assert.Equal(true, cancel);
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Method.cs

@ -166,6 +166,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
[Fact] [Fact]
public void Binding_Method_To_Command_Collected() public void Binding_Method_To_Command_Collected()
{ {
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
WeakReference<ViewModel> MakeRef() WeakReference<ViewModel> MakeRef()
{ {
var weakVm = new WeakReference<ViewModel>(null); var weakVm = new WeakReference<ViewModel>(null);

7
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Runtime.CompilerServices;
using System.Xml; using System.Xml;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Xunit; using Xunit;
@ -9,6 +11,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class MergeResourceIncludeTests public class MergeResourceIncludeTests
{ {
static MergeResourceIncludeTests()
{
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
}
[Fact] [Fact]
public void MergeResourceInclude_Works_With_Single_Resource() public void MergeResourceInclude_Works_With_Single_Resource()
{ {

8
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Xaml.Styling; using Avalonia.Markup.Xaml.Styling;
using Avalonia.Markup.Xaml.XamlIl.Runtime; using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Media; using Avalonia.Media;
@ -15,6 +17,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class StyleIncludeTests public class StyleIncludeTests
{ {
static StyleIncludeTests()
{
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
AssetLoader.RegisterResUriParsers();
}
[Fact] [Fact]
public void StyleInclude_Is_Built() public void StyleInclude_Is_Built()
{ {

Loading…
Cancel
Save