Browse Source

Merge branch 'master' into fixes/treeview-selecteditem-inside-selectionchanged-event

Max Katz 2 years ago
parent
commit
80f3fadac4
100 changed files with 1010 additions and 671 deletions
  1. 35 0
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  2. 1 1
      src/Avalonia.Base/Layout/Layoutable.cs
  3. 1 1
      src/Avalonia.Base/Media/Color.cs
  4. 1 1
      src/Avalonia.Base/Media/HsvColor.cs
  5. 45 1
      src/Avalonia.Base/Media/Imaging/PixelFormatReaders.cs
  6. 7 1
      src/Avalonia.Base/Platform/PixelFormat.cs
  7. 7 1
      src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs
  8. 1 1
      src/Avalonia.Base/Styling/ITemplate.cs
  9. 1 1
      src/Avalonia.Base/Visual.cs
  10. 1 3
      src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs
  11. 1 1
      src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs
  12. 22 8
      src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs
  13. 5 5
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs
  14. 6 5
      src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs
  15. 26 0
      src/Avalonia.Controls.ColorPicker/ColorView/AlphaComponentPosition.cs
  16. 18 0
      src/Avalonia.Controls.ColorPicker/ColorView/ColorView.Properties.cs
  17. 15 20
      src/Avalonia.Controls.ColorPicker/ColorView/ColorView.cs
  18. 152 17
      src/Avalonia.Controls.ColorPicker/Converters/ColorToHexConverter.cs
  19. 113 33
      src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs
  20. 0 2
      src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs
  21. 4 1
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
  22. 38 29
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml
  23. 2 1
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
  24. 3 1
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml
  25. 38 29
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml
  26. 2 1
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml
  27. 1 1
      src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayout.cs
  28. 47 57
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  29. 28 37
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  30. 3 4
      src/Avalonia.Controls/Carousel.cs
  31. 2 5
      src/Avalonia.Controls/ComboBox.cs
  32. 2 3
      src/Avalonia.Controls/ContextMenu.cs
  33. 5 7
      src/Avalonia.Controls/ItemsControl.cs
  34. 2 5
      src/Avalonia.Controls/ListBox.cs
  35. 2 4
      src/Avalonia.Controls/Menu.cs
  36. 2 3
      src/Avalonia.Controls/MenuItem.cs
  37. 9 3
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  38. 1 1
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  39. 2 4
      src/Avalonia.Controls/Primitives/TabStrip.cs
  40. 11 8
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  41. 1 1
      src/Avalonia.Controls/ScrollViewer.cs
  42. 5 5
      src/Avalonia.Controls/Slider.cs
  43. 2 5
      src/Avalonia.Controls/TabControl.cs
  44. 2 2
      src/Avalonia.Controls/Templates/FuncTemplate`1.cs
  45. 1 3
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  46. 1 1
      src/Avalonia.Controls/TopLevel.cs
  47. 4 0
      src/Avalonia.Controls/TreeView.cs
  48. 19 4
      src/Avalonia.Controls/TreeViewItem.cs
  49. 1 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  50. 2 3
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  51. 8 6
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  52. 4 2
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs
  53. 6 5
      src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs
  54. 4 4
      src/Markup/Avalonia.Markup.Xaml/Converters/ColorToBrushConverter.cs
  55. 5 5
      src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs
  56. 7 6
      src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs
  57. 4 5
      src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs
  58. 4 9
      src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
  59. 16 17
      src/Markup/Avalonia.Markup.Xaml/Extensions.cs
  60. 8 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  61. 4 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs
  62. 17 19
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
  63. 28 26
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  64. 6 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
  65. 1 5
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs
  66. 6 8
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ObservableStreamPlugin.cs
  67. 26 26
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
  68. 4 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorPlugin.cs
  69. 3 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs
  70. 9 9
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/TaskStreamPlugin.cs
  71. 1 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  72. 1 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/On.cs
  73. 2 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
  74. 1 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs
  75. 12 15
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs
  76. 1 1
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs
  77. 7 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResolveByNameExtension.cs
  78. 18 16
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  79. 4 4
      src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs
  80. 0 2
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
  81. 1 2
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
  82. 0 3
      src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
  83. 0 2
      src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
  84. 2 4
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  85. 3 3
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  86. 5 5
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  87. 4 4
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  88. 4 4
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  89. 8 9
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  90. 6 6
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  91. 2 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs
  92. 18 22
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  93. 1 2
      src/Markup/Avalonia.Markup/Data/Binding.cs
  94. 2 3
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  95. 17 10
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  96. 4 5
      src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
  97. 2 4
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  98. 4 4
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  99. 8 10
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  100. 2 4
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

+ 35 - 0
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@@ -28,6 +28,41 @@
     <Grid Grid.Column="2"
     <Grid Grid.Column="2"
           Grid.Row="0"
           Grid.Row="0"
           RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
           RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+      <Grid.Resources>
+
+        <x:Double x:Key="ColorSliderSize">24</x:Double>
+        <x:Double x:Key="ColorSliderTrackSize">18</x:Double>
+        <CornerRadius x:Key="ColorSliderCornerRadius">12</CornerRadius>
+        <CornerRadius x:Key="ColorSliderTrackCornerRadius">9</CornerRadius>
+
+        <!-- Due to 'SystemControlForegroundBaseHighBrush' usage this only works in Fluent theme. -->
+        <!-- Otherwise it would be necessary to make custom light/dark resources. -->
+        <ControlTheme x:Key="ColorSliderThumbTheme"
+                      TargetType="Thumb">
+          <Setter Property="Background" Value="Transparent" />
+          <Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
+          <Setter Property="BorderThickness" Value="5" />
+          <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+          <Setter Property="Template">
+            <Setter.Value>
+              <ControlTemplate>
+                <Grid>
+                  <Border Background="{TemplateBinding Background}"
+                          BorderBrush="{TemplateBinding BorderBrush}"
+                          BorderThickness="{TemplateBinding BorderThickness}"
+                          CornerRadius="{TemplateBinding CornerRadius}" />
+                  <Ellipse Height="{TemplateBinding Height}"
+                           Width="{TemplateBinding Width}"
+                           Fill="Transparent"
+                           Stroke="{TemplateBinding Foreground}"
+                           StrokeThickness="1" />
+                </Grid>
+              </ControlTemplate>
+            </Setter.Value>
+          </Setter>
+        </ControlTheme>
+
+      </Grid.Resources>
       <ColorSpectrum x:Name="ColorSpectrum1"
       <ColorSpectrum x:Name="ColorSpectrum1"
                      Grid.Row="0"
                      Grid.Row="0"
                      Color="Red"
                      Color="Red"

+ 1 - 1
src/Avalonia.Base/Layout/Layoutable.cs

@@ -125,7 +125,7 @@ namespace Avalonia.Layout
             AvaloniaProperty.Register<Layoutable, VerticalAlignment>(nameof(VerticalAlignment));
             AvaloniaProperty.Register<Layoutable, VerticalAlignment>(nameof(VerticalAlignment));
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="UseLayoutRoundingProperty"/> property.
+        /// Defines the <see cref="UseLayoutRounding"/> property.
         /// </summary>
         /// </summary>
         public static readonly StyledProperty<bool> UseLayoutRoundingProperty =
         public static readonly StyledProperty<bool> UseLayoutRoundingProperty =
             AvaloniaProperty.Register<Layoutable, bool>(nameof(UseLayoutRounding), defaultValue: true, inherits: true);
             AvaloniaProperty.Register<Layoutable, bool>(nameof(UseLayoutRounding), defaultValue: true, inherits: true);

+ 1 - 1
src/Avalonia.Base/Media/Color.cs

@@ -309,7 +309,7 @@ namespace Avalonia.Media
             if (input.Length == 3 || input.Length == 4)
             if (input.Length == 3 || input.Length == 4)
             {
             {
                 var extendedLength = 2 * input.Length;
                 var extendedLength = 2 * input.Length;
-                
+
 #if !BUILDTASK
 #if !BUILDTASK
                 Span<char> extended = stackalloc char[extendedLength];
                 Span<char> extended = stackalloc char[extendedLength];
 #else
 #else

+ 1 - 1
src/Avalonia.Base/Media/HsvColor.cs

@@ -131,7 +131,7 @@ namespace Avalonia.Media
         /// </summary>
         /// </summary>
         /// <remarks>
         /// <remarks>
         /// <list type="bullet">
         /// <list type="bullet">
-        ///   <item>0 is a shade of gray (no color).</item>
+        ///   <item>0 is fully white (or a shade of gray) and shows no color.</item>
         ///   <item>1 is the full color.</item>
         ///   <item>1 is the full color.</item>
         /// </list>
         /// </list>
         /// </remarks>
         /// </remarks>

+ 45 - 1
src/Avalonia.Base/Media/Imaging/PixelFormatReaders.cs

@@ -228,6 +228,44 @@ static unsafe class PixelFormatReader
 
 
         public void Reset(IntPtr address) => _address = (Rgba64*)address;
         public void Reset(IntPtr address) => _address = (Rgba64*)address;
     }
     }
+    
+    public unsafe struct Rgb24PixelFormatReader : IPixelFormatReader
+    {
+        private byte* _address;
+        public Rgba8888Pixel ReadNext()
+        {
+            var addr = _address;
+            _address += 3;
+            return new Rgba8888Pixel
+            {
+                R = addr[0],
+                G = addr[1],
+                B = addr[2],
+                A = 255,
+            };
+        }
+
+        public void Reset(IntPtr address) => _address = (byte*)address;
+    }
+    
+    public unsafe struct Bgr24PixelFormatReader : IPixelFormatReader
+    {
+        private byte* _address;
+        public Rgba8888Pixel ReadNext()
+        {
+            var addr = _address;
+            _address += 3;
+            return new Rgba8888Pixel
+            {
+                R = addr[2],
+                G = addr[1],
+                B = addr[0],
+                A = 255,
+            };
+        }
+
+        public void Reset(IntPtr address) => _address = (byte*)address;
+    }
 
 
     public static void Transcode(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst,
     public static void Transcode(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst,
         PixelFormat format)
         PixelFormat format)
@@ -242,6 +280,10 @@ static unsafe class PixelFormatReader
             Transcode<Gray8PixelReader>(dst, src, size, strideSrc, strideDst);
             Transcode<Gray8PixelReader>(dst, src, size, strideSrc, strideDst);
         else if (format == PixelFormats.Gray16)
         else if (format == PixelFormats.Gray16)
             Transcode<Gray16PixelReader>(dst, src, size, strideSrc, strideDst);
             Transcode<Gray16PixelReader>(dst, src, size, strideSrc, strideDst);
+        else if (format == PixelFormats.Rgb24)
+            Transcode<Rgb24PixelFormatReader>(dst, src, size, strideSrc, strideDst);
+        else if (format == PixelFormats.Bgr24)
+            Transcode<Bgr24PixelFormatReader>(dst, src, size, strideSrc, strideDst);
         else if (format == PixelFormats.Gray32Float)
         else if (format == PixelFormats.Gray32Float)
             Transcode<Gray32FloatPixelReader>(dst, src, size, strideSrc, strideDst);
             Transcode<Gray32FloatPixelReader>(dst, src, size, strideSrc, strideDst);
         else if (format == PixelFormats.Rgba64)
         else if (format == PixelFormats.Rgba64)
@@ -258,7 +300,9 @@ static unsafe class PixelFormatReader
                || format == PixelFormats.Gray8
                || format == PixelFormats.Gray8
                || format == PixelFormats.Gray16
                || format == PixelFormats.Gray16
                || format == PixelFormats.Gray32Float
                || format == PixelFormats.Gray32Float
-               || format == PixelFormats.Rgba64;
+               || format == PixelFormats.Rgba64
+               || format == PixelFormats.Bgr24
+               || format == PixelFormats.Rgb24;
     }
     }
     
     
     public static void Transcode<TReader>(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst) where TReader : struct, IPixelFormatReader
     public static void Transcode<TReader>(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst) where TReader : struct, IPixelFormatReader

+ 7 - 1
src/Avalonia.Base/Platform/PixelFormat.cs

@@ -13,7 +13,9 @@ namespace Avalonia.Platform
         Gray8,
         Gray8,
         Gray16,
         Gray16,
         Gray32Float,
         Gray32Float,
-        Rgba64
+        Rgba64,
+        Rgb24,
+        Bgr24
     }
     }
 
 
     public record struct PixelFormat
     public record struct PixelFormat
@@ -35,6 +37,8 @@ namespace Avalonia.Platform
                 else if (FormatEnum == PixelFormatEnum.Rgb565 
                 else if (FormatEnum == PixelFormatEnum.Rgb565 
                          || FormatEnum == PixelFormatEnum.Gray16)
                          || FormatEnum == PixelFormatEnum.Gray16)
                     return 16;
                     return 16;
+                else if (FormatEnum is PixelFormatEnum.Bgr24 or PixelFormatEnum.Rgb24)
+                    return 24;
                 else if (FormatEnum == PixelFormatEnum.Rgba64)
                 else if (FormatEnum == PixelFormatEnum.Rgba64)
                     return 64;
                     return 64;
 
 
@@ -70,5 +74,7 @@ namespace Avalonia.Platform
         public static PixelFormat Gray8 { get; } = new PixelFormat(PixelFormatEnum.Gray8);
         public static PixelFormat Gray8 { get; } = new PixelFormat(PixelFormatEnum.Gray8);
         public static PixelFormat Gray16 { get; } = new PixelFormat(PixelFormatEnum.Gray16);
         public static PixelFormat Gray16 { get; } = new PixelFormat(PixelFormatEnum.Gray16);
         public static PixelFormat Gray32Float { get; } = new PixelFormat(PixelFormatEnum.Gray32Float);
         public static PixelFormat Gray32Float { get; } = new PixelFormat(PixelFormatEnum.Gray32Float);
+        public static PixelFormat Rgb24 { get; } = new PixelFormat(PixelFormatEnum.Rgb24);
+        public static PixelFormat Bgr24 { get; } = new PixelFormat(PixelFormatEnum.Bgr24);
     }
     }
 }
 }

+ 7 - 1
src/Avalonia.Base/Rendering/Composition/Transport/BatchStreamArrayPool.cs

@@ -72,6 +72,11 @@ internal abstract class BatchStreamPoolBase<T> : IDisposable
 
 
     protected abstract T CreateItem();
     protected abstract T CreateItem();
 
 
+    protected virtual void ClearItem(T item)
+    {
+        
+    }
+
     protected virtual void DestroyItem(T item)
     protected virtual void DestroyItem(T item)
     {
     {
         
         
@@ -94,6 +99,7 @@ internal abstract class BatchStreamPoolBase<T> : IDisposable
 
 
     public void Return(T item)
     public void Return(T item)
     {
     {
+        ClearItem(item);
         lock (_pool)
         lock (_pool)
         {
         {
             _usage--;
             _usage--;
@@ -138,7 +144,7 @@ internal sealed class BatchStreamObjectPool<T> : BatchStreamPoolBase<T[]> where
         return new T[ArraySize];
         return new T[ArraySize];
     }
     }
 
 
-    protected override void DestroyItem(T[] item)
+    protected override void ClearItem(T[] item)
     {
     {
         Array.Clear(item, 0, item.Length);
         Array.Clear(item, 0, item.Length);
     }
     }

+ 1 - 1
src/Avalonia.Base/Styling/ITemplate.cs

@@ -2,6 +2,6 @@
 {
 {
     public interface ITemplate
     public interface ITemplate
     {
     {
-        object Build();
+        object? Build();
     }
     }
 }
 }

+ 1 - 1
src/Avalonia.Base/Visual.cs

@@ -50,7 +50,7 @@ namespace Avalonia
             AvaloniaProperty.Register<Visual, Geometry?>(nameof(Clip));
             AvaloniaProperty.Register<Visual, Geometry?>(nameof(Clip));
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="IsVisibleProperty"/> property.
+        /// Defines the <see cref="IsVisible"/> property.
         /// </summary>
         /// </summary>
         public static readonly StyledProperty<bool> IsVisibleProperty =
         public static readonly StyledProperty<bool> IsVisibleProperty =
             AvaloniaProperty.Register<Visual, bool>(nameof(IsVisible), true);
             AvaloniaProperty.Register<Visual, bool>(nameof(IsVisible), true);

+ 1 - 3
src/Avalonia.Controls.ColorPicker/ColorPicker/ColorPicker.cs

@@ -1,6 +1,4 @@
-using Avalonia.Controls.Primitives;
-
-namespace Avalonia.Controls
+namespace Avalonia.Controls
 {
 {
     /// <summary>
     /// <summary>
     /// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.
     /// Presents a color for user editing using a spectrum, palette and component sliders within a drop down.

+ 1 - 1
src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs

@@ -124,7 +124,7 @@ namespace Avalonia.Controls.Primitives
             if (accentStep != 0)
             if (accentStep != 0)
             {
             {
                 // ColorChanged will be invoked in OnPropertyChanged if the value is different
                 // ColorChanged will be invoked in OnPropertyChanged if the value is different
-                HsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep);
+                SetCurrentValue(HsvColorProperty, AccentColorConverter.GetAccent(hsvColor, accentStep));
             }
             }
         }
         }
     }
     }

+ 22 - 8
src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs

@@ -96,8 +96,22 @@ namespace Avalonia.Controls.Primitives
             // independent pixels of controls.
             // independent pixels of controls.
 
 
             var scale = LayoutHelper.GetLayoutScale(this);
             var scale = LayoutHelper.GetLayoutScale(this);
-            var pixelWidth = Convert.ToInt32(Bounds.Width * scale);
-            var pixelHeight = Convert.ToInt32(Bounds.Height * scale);
+            int pixelWidth;
+            int pixelHeight;
+
+            if (base._track != null)
+            {
+                pixelWidth = Convert.ToInt32(base._track.Bounds.Width * scale);
+                pixelHeight = Convert.ToInt32(base._track.Bounds.Height * scale);
+            }
+            else
+            {
+                // As a fallback, attempt to calculate using the overall control size
+                // This shouldn't happen as a track is a required template part of a slider
+                // However, if it does, the spectrum will still be shown
+                pixelWidth = Convert.ToInt32(Bounds.Width * scale);
+                pixelHeight = Convert.ToInt32(Bounds.Height * scale);
+            }
 
 
             if (pixelWidth != 0 && pixelHeight != 0)
             if (pixelWidth != 0 && pixelHeight != 0)
             {
             {
@@ -373,7 +387,7 @@ namespace Avalonia.Controls.Primitives
                 ignorePropertyChanged = true;
                 ignorePropertyChanged = true;
 
 
                 // Always keep the two color properties in sync
                 // Always keep the two color properties in sync
-                HsvColor = Color.ToHsv();
+                SetCurrentValue(HsvColorProperty, Color.ToHsv());
 
 
                 SetColorToSliderValues();
                 SetColorToSliderValues();
                 UpdateBackground();
                 UpdateBackground();
@@ -403,7 +417,7 @@ namespace Avalonia.Controls.Primitives
                 ignorePropertyChanged = true;
                 ignorePropertyChanged = true;
 
 
                 // Always keep the two color properties in sync
                 // Always keep the two color properties in sync
-                Color = HsvColor.ToRgb();
+                SetCurrentValue(ColorProperty, HsvColor.ToRgb());
 
 
                 SetColorToSliderValues();
                 SetColorToSliderValues();
                 UpdateBackground();
                 UpdateBackground();
@@ -440,13 +454,13 @@ namespace Avalonia.Controls.Primitives
 
 
                 if (ColorModel == ColorModel.Hsva)
                 if (ColorModel == ColorModel.Hsva)
                 {
                 {
-                    HsvColor = hsvColor;
-                    Color = hsvColor.ToRgb();
+                    SetCurrentValue(HsvColorProperty, hsvColor);
+                    SetCurrentValue(ColorProperty, hsvColor.ToRgb());
                 }
                 }
                 else
                 else
                 {
                 {
-                    Color = color;
-                    HsvColor = color.ToHsv();
+                    SetCurrentValue(ColorProperty, color);
+                    SetCurrentValue(HsvColorProperty, color.ToHsv());
                 }
                 }
 
 
                 UpdatePseudoClasses();
                 UpdatePseudoClasses();

+ 5 - 5
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs

@@ -96,10 +96,10 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// <summary>
         /// Defines the <see cref="ThirdComponent"/> property.
         /// Defines the <see cref="ThirdComponent"/> property.
         /// </summary>
         /// </summary>
-        public static readonly StyledProperty<ColorComponent> ThirdComponentProperty =
-            AvaloniaProperty.Register<ColorSpectrum, ColorComponent>(
+        public static readonly DirectProperty<ColorSpectrum, ColorComponent> ThirdComponentProperty =
+            AvaloniaProperty.RegisterDirect<ColorSpectrum, ColorComponent>(
                 nameof(ThirdComponent),
                 nameof(ThirdComponent),
-                ColorComponent.Component3); // Value
+                o => o.ThirdComponent);
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the currently selected color in the RGB color model.
         /// Gets or sets the currently selected color in the RGB color model.
@@ -239,8 +239,8 @@ namespace Avalonia.Controls.Primitives
         /// </remarks>
         /// </remarks>
         public ColorComponent ThirdComponent
         public ColorComponent ThirdComponent
         {
         {
-            get => GetValue(ThirdComponentProperty);
-            protected set => SetValue(ThirdComponentProperty, value);
+            get => _thirdComponent;
+            private set => SetAndRaise(ThirdComponentProperty, ref _thirdComponent, value);
         }
         }
     }
     }
 }
 }

+ 6 - 5
src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs

@@ -13,9 +13,9 @@ using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Layout;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
+using Avalonia.Reactive;
 using Avalonia.Threading;
 using Avalonia.Threading;
 using Avalonia.Utilities;
 using Avalonia.Utilities;
-using Avalonia.Reactive;
 
 
 namespace Avalonia.Controls.Primitives
 namespace Avalonia.Controls.Primitives
 {
 {
@@ -48,6 +48,7 @@ namespace Avalonia.Controls.Primitives
         private bool _isPointerPressed = false;
         private bool _isPointerPressed = false;
         private bool _shouldShowLargeSelection = false;
         private bool _shouldShowLargeSelection = false;
         private List<Hsv> _hsvValues = new List<Hsv>();
         private List<Hsv> _hsvValues = new List<Hsv>();
+        private ColorComponent _thirdComponent = ColorComponent.Component3; // HsvComponent.Value
 
 
         private IDisposable? _layoutRootDisposable;
         private IDisposable? _layoutRootDisposable;
         private IDisposable? _selectionEllipsePanelDisposable;
         private IDisposable? _selectionEllipsePanelDisposable;
@@ -403,7 +404,7 @@ namespace Avalonia.Controls.Primitives
 
 
                     _updatingHsvColor = true;
                     _updatingHsvColor = true;
                     Hsv newHsv = (new Rgb(color)).ToHsv();
                     Hsv newHsv = (new Rgb(color)).ToHsv();
-                    HsvColor = newHsv.ToHsvColor(color.A / 255.0);
+                    SetCurrentValue(HsvColorProperty, newHsv.ToHsvColor(color.A / 255.0));
                     _updatingHsvColor = false;
                     _updatingHsvColor = false;
 
 
                     UpdateEllipse();
                     UpdateEllipse();
@@ -534,7 +535,7 @@ namespace Avalonia.Controls.Primitives
             _updatingColor = true;
             _updatingColor = true;
             Rgb newRgb = (new Hsv(hsvColor)).ToRgb();
             Rgb newRgb = (new Hsv(hsvColor)).ToRgb();
 
 
-            Color = newRgb.ToColor(hsvColor.A);
+            SetCurrentValue(ColorProperty, newRgb.ToColor(hsvColor.A));
 
 
             _updatingColor = false;
             _updatingColor = false;
 
 
@@ -608,8 +609,8 @@ namespace Avalonia.Controls.Primitives
             Rgb newRgb = newHsv.ToRgb();
             Rgb newRgb = newHsv.ToRgb();
             double alpha = HsvColor.A;
             double alpha = HsvColor.A;
 
 
-            Color = newRgb.ToColor(alpha);
-            HsvColor = newHsv.ToHsvColor(alpha);
+            SetCurrentValue(ColorProperty, newRgb.ToColor(alpha));
+            SetCurrentValue(HsvColorProperty, newHsv.ToHsvColor(alpha));
 
 
             UpdateEllipse();
             UpdateEllipse();
             UpdatePseudoClasses();
             UpdatePseudoClasses();

+ 26 - 0
src/Avalonia.Controls.ColorPicker/ColorView/AlphaComponentPosition.cs

@@ -0,0 +1,26 @@
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Defines the position of a color's alpha component relative to all other components.
+    /// </summary>
+    public enum AlphaComponentPosition
+    {
+        /// <summary>
+        /// The alpha component occurs before all other components.
+        /// </summary>
+        /// <remarks>
+        /// For example, this may indicate the #AARRGGBB or ARGB format which
+        /// is the default format for XAML itself and the Color struct.
+        /// </remarks>
+        Leading,
+
+        /// <summary>
+        /// The alpha component occurs after all other components.
+        /// </summary>
+        /// <remarks>
+        /// For example, this may indicate the #RRGGBBAA or RGBA format which
+        /// is the default format for CSS.
+        /// </remarks>
+        Trailing,
+    }
+}

+ 18 - 0
src/Avalonia.Controls.ColorPicker/ColorView/ColorView.Properties.cs

@@ -42,6 +42,14 @@ namespace Avalonia.Controls
                 nameof(ColorSpectrumShape),
                 nameof(ColorSpectrumShape),
                 ColorSpectrumShape.Box);
                 ColorSpectrumShape.Box);
 
 
+        /// <summary>
+        /// Defines the <see cref="HexInputAlphaPosition"/> property.
+        /// </summary>
+        public static readonly StyledProperty<AlphaComponentPosition> HexInputAlphaPositionProperty =
+            AvaloniaProperty.Register<ColorView, AlphaComponentPosition>(
+                nameof(HexInputAlphaPosition),
+                AlphaComponentPosition.Trailing); // Match CSS (and default slider order) instead of XAML/WinUI
+
         /// <summary>
         /// <summary>
         /// Defines the <see cref="HsvColor"/> property.
         /// Defines the <see cref="HsvColor"/> property.
         /// </summary>
         /// </summary>
@@ -260,6 +268,16 @@ namespace Avalonia.Controls
             set => SetValue(ColorSpectrumShapeProperty, value);
             set => SetValue(ColorSpectrumShapeProperty, value);
         }
         }
 
 
+        /// <summary>
+        /// Gets or sets the position of the alpha component in the hexadecimal input box relative to
+        /// all other color components.
+        /// </summary>
+        public AlphaComponentPosition HexInputAlphaPosition
+        {
+            get => GetValue(HexInputAlphaPositionProperty);
+            set => SetValue(HexInputAlphaPositionProperty, value);
+        }
+
         /// <inheritdoc cref="ColorSpectrum.HsvColor"/>
         /// <inheritdoc cref="ColorSpectrum.HsvColor"/>
         public HsvColor HsvColor
         public HsvColor HsvColor
         {
         {

+ 15 - 20
src/Avalonia.Controls.ColorPicker/ColorView/ColorView.cs

@@ -1,14 +1,10 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Globalization;
 using Avalonia.Controls.Converters;
 using Avalonia.Controls.Converters;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Threading;
 using Avalonia.Threading;
-using Avalonia.VisualTree;
 
 
 namespace Avalonia.Controls
 namespace Avalonia.Controls
 {
 {
@@ -28,8 +24,7 @@ namespace Avalonia.Controls
         private TextBox?    _hexTextBox;
         private TextBox?    _hexTextBox;
         private TabControl? _tabControl;
         private TabControl? _tabControl;
 
 
-        private ColorToHexConverter colorToHexConverter = new ColorToHexConverter();
-        protected bool ignorePropertyChanged = false;
+        protected bool _ignorePropertyChanged = false;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ColorView"/> class.
         /// Initializes a new instance of the <see cref="ColorView"/> class.
@@ -46,11 +41,11 @@ namespace Avalonia.Controls
         {
         {
             if (_hexTextBox != null)
             if (_hexTextBox != null)
             {
             {
-                var convertedColor = colorToHexConverter.ConvertBack(_hexTextBox.Text, typeof(Color), null, CultureInfo.CurrentCulture);
+                var convertedColor = ColorToHexConverter.ParseHexString(_hexTextBox.Text ?? string.Empty, HexInputAlphaPosition);
 
 
                 if (convertedColor is Color color)
                 if (convertedColor is Color color)
                 {
                 {
-                    Color = color;
+                    SetCurrentValue(ColorProperty, color);
                 }
                 }
 
 
                 // Re-apply the hex value
                 // Re-apply the hex value
@@ -66,7 +61,7 @@ namespace Avalonia.Controls
         {
         {
             if (_hexTextBox != null)
             if (_hexTextBox != null)
             {
             {
-                _hexTextBox.Text = colorToHexConverter.Convert(Color, typeof(string), null, CultureInfo.CurrentCulture) as string;
+                _hexTextBox.Text = ColorToHexConverter.ToHexString(Color, HexInputAlphaPosition);
             }
             }
         }
         }
 
 
@@ -167,7 +162,7 @@ namespace Avalonia.Controls
                 // The work-around for this is done here where SelectedIndex is forcefully
                 // The work-around for this is done here where SelectedIndex is forcefully
                 // synchronized with whatever the TabControl property value is. This is
                 // synchronized with whatever the TabControl property value is. This is
                 // possible since selection validation is already done by this method.
                 // possible since selection validation is already done by this method.
-                SelectedIndex = _tabControl.SelectedIndex;
+                SetCurrentValue(SelectedIndexProperty, _tabControl.SelectedIndex);
             }
             }
 
 
             return;
             return;
@@ -200,7 +195,7 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         /// <inheritdoc/>
         protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
         protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
         {
         {
-            if (ignorePropertyChanged)
+            if (_ignorePropertyChanged)
             {
             {
                 base.OnPropertyChanged(change);
                 base.OnPropertyChanged(change);
                 return;
                 return;
@@ -209,29 +204,29 @@ namespace Avalonia.Controls
             // Always keep the two color properties in sync
             // Always keep the two color properties in sync
             if (change.Property == ColorProperty)
             if (change.Property == ColorProperty)
             {
             {
-                ignorePropertyChanged = true;
+                _ignorePropertyChanged = true;
 
 
-                HsvColor = Color.ToHsv();
+                SetCurrentValue(HsvColorProperty, Color.ToHsv());
                 SetColorToHexTextBox();
                 SetColorToHexTextBox();
 
 
                 OnColorChanged(new ColorChangedEventArgs(
                 OnColorChanged(new ColorChangedEventArgs(
                     change.GetOldValue<Color>(),
                     change.GetOldValue<Color>(),
                     change.GetNewValue<Color>()));
                     change.GetNewValue<Color>()));
 
 
-                ignorePropertyChanged = false;
+                _ignorePropertyChanged = false;
             }
             }
             else if (change.Property == HsvColorProperty)
             else if (change.Property == HsvColorProperty)
             {
             {
-                ignorePropertyChanged = true;
+                _ignorePropertyChanged = true;
 
 
-                Color = HsvColor.ToRgb();
+                SetCurrentValue(ColorProperty, HsvColor.ToRgb());
                 SetColorToHexTextBox();
                 SetColorToHexTextBox();
 
 
                 OnColorChanged(new ColorChangedEventArgs(
                 OnColorChanged(new ColorChangedEventArgs(
                     change.GetOldValue<HsvColor>().ToRgb(),
                     change.GetOldValue<HsvColor>().ToRgb(),
                     change.GetNewValue<HsvColor>().ToRgb()));
                     change.GetNewValue<HsvColor>().ToRgb()));
 
 
-                ignorePropertyChanged = false;
+                _ignorePropertyChanged = false;
             }
             }
             else if (change.Property == PaletteProperty)
             else if (change.Property == PaletteProperty)
             {
             {
@@ -241,7 +236,7 @@ namespace Avalonia.Controls
                 // bound properties controlling the palette grid
                 // bound properties controlling the palette grid
                 if (palette != null)
                 if (palette != null)
                 {
                 {
-                    PaletteColumnCount = palette.ColorCount;
+                    SetCurrentValue(PaletteColumnCountProperty, palette.ColorCount);
 
 
                     List<Color> newPaletteColors = new List<Color>();
                     List<Color> newPaletteColors = new List<Color>();
                     for (int shadeIndex = 0; shadeIndex < palette.ShadeCount; shadeIndex++)
                     for (int shadeIndex = 0; shadeIndex < palette.ShadeCount; shadeIndex++)
@@ -252,14 +247,14 @@ namespace Avalonia.Controls
                         }
                         }
                     }
                     }
 
 
-                    PaletteColors = newPaletteColors;
+                    SetCurrentValue(PaletteColorsProperty, newPaletteColors);
                 }
                 }
             }
             }
             else if (change.Property == IsAlphaEnabledProperty)
             else if (change.Property == IsAlphaEnabledProperty)
             {
             {
                 // Manually coerce the HsvColor value
                 // Manually coerce the HsvColor value
                 // (Color will be coerced automatically if HsvColor changes)
                 // (Color will be coerced automatically if HsvColor changes)
-                HsvColor = OnCoerceHsvColor(HsvColor);
+                SetCurrentValue(HsvColorProperty,  OnCoerceHsvColor(HsvColor));
             }
             }
             else if (change.Property == IsColorComponentsVisibleProperty ||
             else if (change.Property == IsColorComponentsVisibleProperty ||
                      change.Property == IsColorPaletteVisibleProperty ||
                      change.Property == IsColorPaletteVisibleProperty ||

+ 152 - 17
src/Avalonia.Controls.ColorPicker/Converters/ColorToHexConverter.cs

@@ -2,6 +2,7 @@
 using System.Globalization;
 using System.Globalization;
 using Avalonia.Data.Converters;
 using Avalonia.Data.Converters;
 using Avalonia.Media;
 using Avalonia.Media;
+using Avalonia.Utilities;
 
 
 namespace Avalonia.Controls.Converters
 namespace Avalonia.Controls.Converters
 {
 {
@@ -10,6 +11,11 @@ namespace Avalonia.Controls.Converters
     /// </summary>
     /// </summary>
     public class ColorToHexConverter : IValueConverter
     public class ColorToHexConverter : IValueConverter
     {
     {
+        /// <summary>
+        /// Gets or sets the position of a color's alpha component relative to all other components.
+        /// </summary>
+        public AlphaComponentPosition AlphaPosition { get; set; } = AlphaComponentPosition.Leading;
+
         /// <inheritdoc/>
         /// <inheritdoc/>
         public object? Convert(
         public object? Convert(
             object? value,
             object? value,
@@ -42,16 +48,7 @@ namespace Avalonia.Controls.Converters
                 return AvaloniaProperty.UnsetValue;
                 return AvaloniaProperty.UnsetValue;
             }
             }
 
 
-            string hexColor = color.ToUint32().ToString("x8", CultureInfo.InvariantCulture).ToUpperInvariant();
-
-            if (includeSymbol == false)
-            {
-                // TODO: When .net standard 2.0 is dropped, replace the below line
-                //hexColor = hexColor.Replace("#", string.Empty, StringComparison.Ordinal);
-                hexColor = hexColor.Replace("#", string.Empty);
-            }
-
-            return hexColor;
+            return ToHexString(color, AlphaPosition, includeSymbol);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -62,21 +59,159 @@ namespace Avalonia.Controls.Converters
             CultureInfo culture)
             CultureInfo culture)
         {
         {
             string hexValue = value?.ToString() ?? string.Empty;
             string hexValue = value?.ToString() ?? string.Empty;
+            return ParseHexString(hexValue, AlphaPosition) ?? AvaloniaProperty.UnsetValue;
+        }
+
+        /// <summary>
+        /// Converts the given color to its hex color value string representation.
+        /// </summary>
+        /// <param name="color">The color to represent as a hex value string.</param>
+        /// <param name="alphaPosition">The output position of the alpha component.</param>
+        /// <param name="includeSymbol">Whether the hex symbol '#' will be added.</param>
+        /// <returns>The input color converted to its hex value string.</returns>
+        public static string ToHexString(
+            Color color,
+            AlphaComponentPosition alphaPosition,
+            bool includeSymbol = false)
+        {
+            uint intColor;
+            if (alphaPosition == AlphaComponentPosition.Trailing)
+            {
+                intColor = ((uint)color.R << 24) | ((uint)color.G << 16) | ((uint)color.B << 8) | (uint)color.A;
+            }
+            else
+            {
+                // Default is Leading alpha
+                intColor = ((uint)color.A << 24) | ((uint)color.R << 16) | ((uint)color.G << 8) | (uint)color.B;
+            }
 
 
-            if (Color.TryParse(hexValue, out Color color))
+            string hexColor = intColor.ToString("x8", CultureInfo.InvariantCulture).ToUpperInvariant();
+
+            if (includeSymbol)
+            {
+                hexColor = '#' + hexColor;
+            }
+
+            return hexColor;
+        }
+
+        /// <summary>
+        /// Parses a hex color value string into a new <see cref="Color"/>.
+        /// </summary>
+        /// <param name="hexColor">The hex color string to parse.</param>
+        /// <param name="alphaPosition">The input position of the alpha component.</param>
+        /// <returns>The parsed <see cref="Color"/>; otherwise, null.</returns>
+        public static Color? ParseHexString(
+            string hexColor,
+            AlphaComponentPosition alphaPosition)
+        {
+            hexColor = hexColor.Trim();
+
+            if (!hexColor.StartsWith("#", StringComparison.Ordinal))
+            {
+                hexColor = "#" + hexColor;
+            }
+
+            if (TryParseHexFormat(hexColor.AsSpan(), alphaPosition, out Color color))
             {
             {
                 return color;
                 return color;
             }
             }
-            else if (hexValue.StartsWith("#", StringComparison.Ordinal) == false &&
-                     Color.TryParse("#" + hexValue, out Color color2))
+
+            return null;
+        }
+
+        /// <summary>
+        /// Parses the given span of characters representing a hex color value into a new <see cref="Color"/>.
+        /// </summary>
+        /// <remarks>
+        /// This is based on the Color.TryParseHexFormat() method.
+        /// It is copied because it needs to be extended to handle alpha position.
+        /// However, the alpha position enum is only available in the controls namespace with the ColorPicker control.
+        /// </remarks>
+        private static bool TryParseHexFormat(
+            ReadOnlySpan<char> s,
+            AlphaComponentPosition alphaPosition,
+            out Color color)
+        {
+            static bool TryParseCore(ReadOnlySpan<char> input, AlphaComponentPosition alphaPosition, ref Color color)
             {
             {
-                return color2;
+                var alphaComponent = 0u;
+
+                if (input.Length == 6)
+                {
+                    if (alphaPosition == AlphaComponentPosition.Trailing)
+                    {
+                        alphaComponent = 0x000000FF;
+                    }
+                    else
+                    {
+                        alphaComponent = 0xFF000000;
+                    }
+                }
+                else if (input.Length != 8)
+                {
+                    return false;
+                }
+
+                if (!input.TryParseUInt(NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsed))
+                {
+                    return false;
+                }
+
+                if (alphaComponent != 0)
+                {
+                    if (alphaPosition == AlphaComponentPosition.Trailing)
+                    {
+                        parsed = (parsed << 8) | alphaComponent;
+                    }
+                    else
+                    {
+                        parsed = parsed | alphaComponent;
+                    }
+                }
+
+                if (alphaPosition == AlphaComponentPosition.Trailing)
+                {
+                    // #RRGGBBAA
+                    color = new Color(
+                        a: (byte)(parsed & 0xFF),
+                        r: (byte)((parsed >> 24) & 0xFF),
+                        g: (byte)((parsed >> 16) & 0xFF),
+                        b: (byte)((parsed >> 8) & 0xFF));
+                }
+                else
+                {
+                    // #AARRGGBB
+                    color = new Color(
+                        a: (byte)((parsed >> 24) & 0xFF),
+                        r: (byte)((parsed >> 16) & 0xFF),
+                        g: (byte)((parsed >> 8) & 0xFF),
+                        b: (byte)(parsed & 0xFF));
+                }
+
+                return true;
             }
             }
-            else
+
+            color = default;
+
+            ReadOnlySpan<char> input = s.Slice(1);
+
+            // Handle shorthand cases like #FFF (RGB) or #FFFF (ARGB).
+            if (input.Length == 3 || input.Length == 4)
             {
             {
-                // Invalid hex color value provided
-                return AvaloniaProperty.UnsetValue;
+                var extendedLength = 2 * input.Length;
+                Span<char> extended = stackalloc char[extendedLength];
+
+                for (int i = 0; i < input.Length; i++)
+                {
+                    extended[2 * i + 0] = input[i];
+                    extended[2 * i + 1] = input[i];
+                }
+
+                return TryParseCore(extended, alphaPosition, ref color);
             }
             }
+
+            return TryParseCore(input, alphaPosition, ref color);
         }
         }
     }
     }
 }
 }

+ 113 - 33
src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs

@@ -1,6 +1,6 @@
 using System;
 using System;
-using System.Globalization;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Utilities;
 using Avalonia.Utilities;
 
 
@@ -11,8 +11,11 @@ namespace Avalonia.Controls.Primitives
     /// </summary>
     /// </summary>
     public static class ColorHelper
     public static class ColorHelper
     {
     {
-        private static readonly Dictionary<Color, string> cachedDisplayNames = new Dictionary<Color, string>();
-        private static readonly object cacheMutex = new object();
+        private static readonly Dictionary<HsvColor, string> _cachedDisplayNames = new Dictionary<HsvColor, string>();
+        private static readonly Dictionary<KnownColor, string> _cachedKnownColorNames = new Dictionary<KnownColor, string>();
+        private static readonly object _displayNameCacheMutex = new object();
+        private static readonly object _knownColorCacheMutex = new object();
+        private static readonly KnownColor[] _knownColors = (KnownColor[])Enum.GetValues(typeof(KnownColor));
 
 
         /// <summary>
         /// <summary>
         /// Gets the relative (perceptual) luminance/brightness of the given color.
         /// Gets the relative (perceptual) luminance/brightness of the given color.
@@ -59,7 +62,36 @@ namespace Avalonia.Controls.Primitives
         /// <returns>The approximate color display name.</returns>
         /// <returns>The approximate color display name.</returns>
         public static string ToDisplayName(Color color)
         public static string ToDisplayName(Color color)
         {
         {
-            // Without rounding, there are 16,777,216 possible RGB colors (without alpha).
+            var hsvColor = color.ToHsv();
+
+            // Handle extremes that are outside the below algorithm
+            if (color.A == 0x00)
+            {
+                return GetDisplayName(KnownColor.Transparent);
+            }
+
+            // HSV ----------------------------------------------------------------------
+            //
+            // There are far too many possible HSV colors to cache and search through
+            // for performance reasons. Therefore, the HSV color is rounded.
+            // Rounding is tolerable in this algorithm because it is perception based.
+            // Hue is the most important for user perception so is rounded the least.
+            // Then there is a lot of loss in rounding the saturation and value components
+            // which are not as closely related to perceived color.
+            //
+            //         Hue : Round to nearest int (0..360)
+            //  Saturation : Round to the nearest 1/10 (0..1)
+            //       Value : Round to the nearest 1/10 (0..1)
+            //       Alpha : Is ignored in this algorithm
+            //
+            // Rounding results in ~36_000 values to cache in the worse case.
+            //
+            // RGB ----------------------------------------------------------------------
+            //
+            // The original algorithm worked in RGB color space.
+            // If this code is every adjusted to work in RGB again note the following:
+            //
+            // Without rounding, there are 16_777_216 possible RGB colors (without alpha).
             // This is too many to cache and search through for performance reasons.
             // This is too many to cache and search through for performance reasons.
             // It is also needlessly large as there are only ~140 known/named colors.
             // It is also needlessly large as there are only ~140 known/named colors.
             // Therefore, rounding of the input color's component values is done to
             // Therefore, rounding of the input color's component values is done to
@@ -68,42 +100,67 @@ namespace Avalonia.Controls.Primitives
             // The rounding value of 5 is specially chosen.
             // The rounding value of 5 is specially chosen.
             // It is a factor of 255 and therefore evenly divisible which improves
             // It is a factor of 255 and therefore evenly divisible which improves
             // the quality of the calculations.
             // the quality of the calculations.
-            double rounding = 5;
-            var roundedColor = new Color(
-                0xFF,
-                Convert.ToByte(Math.Round(color.R / rounding) * rounding),
-                Convert.ToByte(Math.Round(color.G / rounding) * rounding),
-                Convert.ToByte(Math.Round(color.B / rounding) * rounding));
+            var roundedHsvColor = new HsvColor(
+                1.0,
+                Math.Round(hsvColor.H, 0),
+                Math.Round(hsvColor.S, 1),
+                Math.Round(hsvColor.V, 1));
 
 
             // Attempt to use a previously cached display name
             // Attempt to use a previously cached display name
-            lock (cacheMutex)
+            lock (_displayNameCacheMutex)
             {
             {
-                if (cachedDisplayNames.TryGetValue(roundedColor, out var displayName))
+                if (_cachedDisplayNames.TryGetValue(roundedHsvColor, out var displayName))
                 {
                 {
                     return displayName;
                     return displayName;
                 }
                 }
             }
             }
 
 
+            // Build the KnownColor name cache if it doesn't already exist
+            lock (_knownColorCacheMutex)
+            {
+                if (_cachedKnownColorNames.Count == 0)
+                {
+                    for (int i = 1; i < _knownColors.Length; i++) // Skip 'None' so start at 1
+                    {
+                        KnownColor knownColor = _knownColors[i];
+
+                        // Some known colors have the same numerical value. For example:
+                        //  - Aqua = 0xff00ffff
+                        //  - Cyan = 0xff00ffff
+                        //
+                        // This is not possible to represent in a dictionary which requires
+                        // unique values. Therefore, only the first value is used.
+
+                        if (!_cachedKnownColorNames.ContainsKey(knownColor))
+                        {
+                            _cachedKnownColorNames.Add(knownColor, GetDisplayName(knownColor));
+                        }
+                    }
+                }
+            }
+
             // Find the closest known color by measuring 3D Euclidean distance (ignore alpha)
             // Find the closest known color by measuring 3D Euclidean distance (ignore alpha)
+            // This is done in HSV color space to most closely match user-perception
             var closestKnownColor = KnownColor.None;
             var closestKnownColor = KnownColor.None;
             var closestKnownColorDistance = double.PositiveInfinity;
             var closestKnownColorDistance = double.PositiveInfinity;
-            var knownColors = (KnownColor[])Enum.GetValues(typeof(KnownColor));
 
 
-            for (int i = 1; i < knownColors.Length; i++) // Skip 'None'
+            for (int i = 1; i < _knownColors.Length; i++) // Skip 'None' so start at 1
             {
             {
+                KnownColor knownColor = _knownColors[i];
+
                 // Transparent is skipped since alpha is ignored making it equivalent to White
                 // Transparent is skipped since alpha is ignored making it equivalent to White
-                if (knownColors[i] != KnownColor.Transparent)
+                if (knownColor != KnownColor.Transparent)
                 {
                 {
-                    Color knownColor = KnownColors.ToColor(knownColors[i]);
+                    HsvColor knownHsvColor = KnownColors.ToColor(knownColor).ToHsv();
 
 
                     double distance = Math.Sqrt(
                     double distance = Math.Sqrt(
-                        Math.Pow((double)(roundedColor.R - knownColor.R), 2.0) +
-                        Math.Pow((double)(roundedColor.G - knownColor.G), 2.0) +
-                        Math.Pow((double)(roundedColor.B - knownColor.B), 2.0));
+                        Math.Pow((roundedHsvColor.H - knownHsvColor.H), 2.0) +
+                        Math.Pow((roundedHsvColor.S - knownHsvColor.S), 2.0) +
+                        Math.Pow((roundedHsvColor.V - knownHsvColor.V), 2.0));
 
 
                     if (distance < closestKnownColorDistance)
                     if (distance < closestKnownColorDistance)
                     {
                     {
-                        closestKnownColor = knownColors[i];
+                        closestKnownColor = knownColor;
                         closestKnownColorDistance = distance;
                         closestKnownColorDistance = distance;
                     }
                     }
                 }
                 }
@@ -113,26 +170,19 @@ namespace Avalonia.Controls.Primitives
             // Cache results for next time as well
             // Cache results for next time as well
             if (closestKnownColor != KnownColor.None)
             if (closestKnownColor != KnownColor.None)
             {
             {
-                var sb = StringBuilderCache.Acquire();
-                string name = closestKnownColor.ToString();
+                string? displayName;
 
 
-                // Add spaces converting PascalCase to human-readable names
-                for (int i = 0; i < name.Length; i++)
+                lock (_knownColorCacheMutex)
                 {
                 {
-                    if (i != 0 &&
-                        char.IsUpper(name[i]))
+                    if (!_cachedKnownColorNames.TryGetValue(closestKnownColor, out displayName))
                     {
                     {
-                        sb.Append(' ');
+                        displayName = GetDisplayName(closestKnownColor);
                     }
                     }
-
-                    sb.Append(name[i]);
                 }
                 }
 
 
-                string displayName = StringBuilderCache.GetStringAndRelease(sb);
-
-                lock (cacheMutex)
+                lock (_displayNameCacheMutex)
                 {
                 {
-                    cachedDisplayNames.Add(roundedColor, displayName);
+                    _cachedDisplayNames.Add(roundedHsvColor, displayName);
                 }
                 }
 
 
                 return displayName;
                 return displayName;
@@ -142,5 +192,35 @@ namespace Avalonia.Controls.Primitives
                 return string.Empty;
                 return string.Empty;
             }
             }
         }
         }
+
+        /// <summary>
+        /// Gets the human-readable display name for the given <see cref="KnownColor"/>.
+        /// </summary>
+        /// <remarks>
+        /// This currently uses the <see cref="KnownColor"/> enum value's C# name directly
+        /// which limits it to the EN language only. In the future this should be localized
+        /// to other cultures.
+        /// </remarks>
+        /// <param name="knownColor">The <see cref="KnownColor"/> to get the display name for.</param>
+        /// <returns>The human-readable display name for the given <see cref="KnownColor"/>.</returns>
+        private static string GetDisplayName(KnownColor knownColor)
+        {
+            var sb = StringBuilderCache.Acquire();
+            string name = knownColor.ToString();
+
+            // Add spaces converting PascalCase to human-readable names
+            for (int i = 0; i < name.Length; i++)
+            {
+                if (i != 0 &&
+                    char.IsUpper(name[i]))
+                {
+                    sb.Append(' ');
+                }
+
+                sb.Append(name[i]);
+            }
+
+            return StringBuilderCache.GetStringAndRelease(sb);
+        }
     }
     }
 }
 }

+ 0 - 2
src/Avalonia.Controls.ColorPicker/Helpers/ColorPickerHelpers.cs

@@ -4,8 +4,6 @@
 // Licensed to The Avalonia Project under the MIT License.
 // Licensed to The Avalonia Project under the MIT License.
 
 
 using System;
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Avalonia.Layout;
 using Avalonia.Layout;

+ 4 - 1
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml

@@ -42,7 +42,8 @@
             </Panel>
             </Panel>
           </DropDownButton.Content>
           </DropDownButton.Content>
           <DropDownButton.Flyout>
           <DropDownButton.Flyout>
-            <Flyout FlyoutPresenterClasses="nopadding">
+            <Flyout FlyoutPresenterClasses="nopadding"
+                    Placement="Top">
 
 
               <!-- The following is copy-pasted from the ColorView's control template.
               <!-- The following is copy-pasted from the ColorView's control template.
                    It MUST always be kept in sync with the ColorView (which is master).
                    It MUST always be kept in sync with the ColorView (which is master).
@@ -216,6 +217,7 @@
                                        Content="RGB"
                                        Content="RGB"
                                        CornerRadius="4,0,0,4"
                                        CornerRadius="4,0,0,4"
                                        BorderThickness="1,1,0,1"
                                        BorderThickness="1,1,0,1"
+                                       Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                           <RadioButton x:Name="HsvRadioButton"
                           <RadioButton x:Name="HsvRadioButton"
                                        Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
                                        Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
@@ -223,6 +225,7 @@
                                        Content="HSV"
                                        Content="HSV"
                                        CornerRadius="0,4,4,0"
                                        CornerRadius="0,4,4,0"
                                        BorderThickness="0,1,1,1"
                                        BorderThickness="0,1,1,1"
+                                       Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                         </Grid>
                         </Grid>
                         <Grid x:Name="HexInputGrid"
                         <Grid x:Name="HexInputGrid"

+ 38 - 29
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml

@@ -1,13 +1,20 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
 
+  <!-- Note that the Slider thumb should generally follow the overall Slider dimensions.
+       Therefore, there are not currently separate resources to control it. -->
+  <x:Double x:Key="ColorSliderSize">20</x:Double>
+  <x:Double x:Key="ColorSliderTrackSize">20</x:Double>
+  <CornerRadius x:Key="ColorSliderCornerRadius">10</CornerRadius>
+  <CornerRadius x:Key="ColorSliderTrackCornerRadius">10</CornerRadius>
+
   <ControlTheme x:Key="ColorSliderThumbTheme"
   <ControlTheme x:Key="ColorSliderThumbTheme"
                 TargetType="Thumb">
                 TargetType="Thumb">
     <Setter Property="Background" Value="Transparent" />
     <Setter Property="Background" Value="Transparent" />
     <Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
     <Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
     <!--TODO: <Setter Property="BorderBrush" Value="{DynamicResource ColorControlDefaultSelectorBrush}" />-->
     <!--TODO: <Setter Property="BorderBrush" Value="{DynamicResource ColorControlDefaultSelectorBrush}" />-->
     <Setter Property="BorderThickness" Value="3" />
     <Setter Property="BorderThickness" Value="3" />
-    <Setter Property="CornerRadius" Value="10" />
+    <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
     <Setter Property="Template">
     <Setter Property="Template">
       <Setter.Value>
       <Setter.Value>
         <ControlTemplate>
         <ControlTemplate>
@@ -25,27 +32,28 @@
 
 
     <Style Selector="^:horizontal">
     <Style Selector="^:horizontal">
       <Setter Property="BorderThickness" Value="0" />
       <Setter Property="BorderThickness" Value="0" />
-      <Setter Property="CornerRadius" Value="10" />
-      <Setter Property="Height" Value="20" />
+      <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+      <Setter Property="Height" Value="{DynamicResource ColorSliderSize}" />
       <Setter Property="Template">
       <Setter Property="Template">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
           <Border BorderThickness="{TemplateBinding BorderThickness}"
           <Border BorderThickness="{TemplateBinding BorderThickness}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   CornerRadius="{TemplateBinding CornerRadius}">
                   CornerRadius="{TemplateBinding CornerRadius}">
             <Grid Margin="{TemplateBinding Padding}">
             <Grid Margin="{TemplateBinding Padding}">
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{StaticResource ColorControlCheckeredBackgroundBrush}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding Background}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
+              <Border HorizontalAlignment="Stretch"
+                      VerticalAlignment="Center"
+                      Height="{Binding ElementName=PART_Track, Path=Bounds.Height}"
+                      Background="{StaticResource ColorControlCheckeredBackgroundBrush}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
+              <Border HorizontalAlignment="Stretch"
+                      VerticalAlignment="Center"
+                      Height="{Binding ElementName=PART_Track, Path=Bounds.Height}"
+                      Background="{TemplateBinding Background}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
               <Track Name="PART_Track"
               <Track Name="PART_Track"
+                     Height="{DynamicResource ColorSliderTrackSize}"
                      HorizontalAlignment="Stretch"
                      HorizontalAlignment="Stretch"
-                     VerticalAlignment="Stretch"
+                     VerticalAlignment="Center"
                      Minimum="{TemplateBinding Minimum}"
                      Minimum="{TemplateBinding Minimum}"
                      Maximum="{TemplateBinding Maximum}"
                      Maximum="{TemplateBinding Maximum}"
                      Value="{TemplateBinding Value, Mode=TwoWay}"
                      Value="{TemplateBinding Value, Mode=TwoWay}"
@@ -82,7 +90,7 @@
                   </RepeatButton>
                   </RepeatButton>
                 </Track.IncreaseButton>
                 </Track.IncreaseButton>
                 <Thumb Name="ColorSliderThumb"
                 <Thumb Name="ColorSliderThumb"
-                       Theme="{StaticResource ColorSliderThumbTheme}"
+                       Theme="{DynamicResource ColorSliderThumbTheme}"
                        Margin="0"
                        Margin="0"
                        Padding="0"
                        Padding="0"
                        DataContext="{TemplateBinding Value}"
                        DataContext="{TemplateBinding Value}"
@@ -97,26 +105,27 @@
 
 
     <Style Selector="^:vertical">
     <Style Selector="^:vertical">
       <Setter Property="BorderThickness" Value="0" />
       <Setter Property="BorderThickness" Value="0" />
-      <Setter Property="CornerRadius" Value="10" />
-      <Setter Property="Width" Value="20" />
+      <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+      <Setter Property="Width" Value="{DynamicResource ColorSliderSize}" />
       <Setter Property="Template">
       <Setter Property="Template">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
           <Border BorderThickness="{TemplateBinding BorderThickness}"
           <Border BorderThickness="{TemplateBinding BorderThickness}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   CornerRadius="{TemplateBinding CornerRadius}">
                   CornerRadius="{TemplateBinding CornerRadius}">
             <Grid Margin="{TemplateBinding Padding}">
             <Grid Margin="{TemplateBinding Padding}">
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{StaticResource ColorControlCheckeredBackgroundBrush}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding Background}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
+              <Border HorizontalAlignment="Center"
+                      VerticalAlignment="Stretch"
+                      Width="{Binding ElementName=PART_Track, Path=Bounds.Width}"
+                      Background="{StaticResource ColorControlCheckeredBackgroundBrush}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
+              <Border HorizontalAlignment="Center"
+                      VerticalAlignment="Stretch"
+                      Width="{Binding ElementName=PART_Track, Path=Bounds.Width}"
+                      Background="{TemplateBinding Background}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
               <Track Name="PART_Track"
               <Track Name="PART_Track"
-                     HorizontalAlignment="Stretch"
+                     Width="{DynamicResource ColorSliderTrackSize}"
+                     HorizontalAlignment="Center"
                      VerticalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      Minimum="{TemplateBinding Minimum}"
                      Minimum="{TemplateBinding Minimum}"
                      Maximum="{TemplateBinding Maximum}"
                      Maximum="{TemplateBinding Maximum}"
@@ -154,7 +163,7 @@
                   </RepeatButton>
                   </RepeatButton>
                 </Track.IncreaseButton>
                 </Track.IncreaseButton>
                 <Thumb Name="ColorSliderThumb"
                 <Thumb Name="ColorSliderThumb"
-                       Theme="{StaticResource ColorSliderThumbTheme}"
+                       Theme="{DynamicResource ColorSliderThumbTheme}"
                        Margin="0"
                        Margin="0"
                        Padding="0"
                        Padding="0"
                        DataContext="{TemplateBinding Value}"
                        DataContext="{TemplateBinding Value}"

+ 2 - 1
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml

@@ -8,7 +8,6 @@
 
 
   <pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
   <pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
   <converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
   <converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
-  <converters:ColorToHexConverter x:Key="ColorToHexConverter" />
   <converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
   <converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
   <globalization:NumberFormatInfo x:Key="ColorViewComponentNumberFormat" NumberDecimalDigits="0" />
   <globalization:NumberFormatInfo x:Key="ColorViewComponentNumberFormat" NumberDecimalDigits="0" />
   <x:Double x:Key="ColorViewTabStripHeight">48</x:Double>
   <x:Double x:Key="ColorViewTabStripHeight">48</x:Double>
@@ -465,6 +464,7 @@
                                  Content="RGB"
                                  Content="RGB"
                                  CornerRadius="4,0,0,4"
                                  CornerRadius="4,0,0,4"
                                  BorderThickness="1,1,0,1"
                                  BorderThickness="1,1,0,1"
+                                 Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                     <RadioButton x:Name="HsvRadioButton"
                     <RadioButton x:Name="HsvRadioButton"
                                  Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
                                  Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
@@ -472,6 +472,7 @@
                                  Content="HSV"
                                  Content="HSV"
                                  CornerRadius="0,4,4,0"
                                  CornerRadius="0,4,4,0"
                                  BorderThickness="0,1,1,1"
                                  BorderThickness="0,1,1,1"
+                                 Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                   </Grid>
                   </Grid>
                   <Grid x:Name="HexInputGrid"
                   <Grid x:Name="HexInputGrid"

+ 3 - 1
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml

@@ -42,7 +42,7 @@
             </Panel>
             </Panel>
           </DropDownButton.Content>
           </DropDownButton.Content>
           <DropDownButton.Flyout>
           <DropDownButton.Flyout>
-            <Flyout>
+            <Flyout Placement="Top">
 
 
               <!-- The following is copy-pasted from the ColorView's control template.
               <!-- The following is copy-pasted from the ColorView's control template.
                    It MUST always be kept in sync with the ColorView (which is master).
                    It MUST always be kept in sync with the ColorView (which is master).
@@ -216,6 +216,7 @@
                                        Content="RGB"
                                        Content="RGB"
                                        CornerRadius="0,0,0,0"
                                        CornerRadius="0,0,0,0"
                                        BorderThickness="1,1,0,1"
                                        BorderThickness="1,1,0,1"
+                                       Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                           <RadioButton x:Name="HsvRadioButton"
                           <RadioButton x:Name="HsvRadioButton"
                                        Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
                                        Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
@@ -223,6 +224,7 @@
                                        Content="HSV"
                                        Content="HSV"
                                        CornerRadius="0,0,0,0"
                                        CornerRadius="0,0,0,0"
                                        BorderThickness="0,1,1,1"
                                        BorderThickness="0,1,1,1"
+                                       Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                                        IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                         </Grid>
                         </Grid>
                         <Grid x:Name="HexInputGrid"
                         <Grid x:Name="HexInputGrid"

+ 38 - 29
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml

@@ -1,13 +1,20 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
 
+  <!-- Note that the Slider thumb should generally follow the overall Slider dimensions.
+       Therefore, there are not currently separate resources to control it. -->
+  <x:Double x:Key="ColorSliderSize">20</x:Double>
+  <x:Double x:Key="ColorSliderTrackSize">20</x:Double>
+  <CornerRadius x:Key="ColorSliderCornerRadius">10</CornerRadius>
+  <CornerRadius x:Key="ColorSliderTrackCornerRadius">10</CornerRadius>
+
   <ControlTheme x:Key="ColorSliderThumbTheme"
   <ControlTheme x:Key="ColorSliderThumbTheme"
                 TargetType="Thumb">
                 TargetType="Thumb">
     <Setter Property="Background" Value="Transparent" />
     <Setter Property="Background" Value="Transparent" />
     <Setter Property="BorderBrush" Value="{DynamicResource ThemeForegroundBrush}" />
     <Setter Property="BorderBrush" Value="{DynamicResource ThemeForegroundBrush}" />
     <!--TODO: <Setter Property="BorderBrush" Value="{DynamicResource ColorControlDefaultSelectorBrush}" />-->
     <!--TODO: <Setter Property="BorderBrush" Value="{DynamicResource ColorControlDefaultSelectorBrush}" />-->
     <Setter Property="BorderThickness" Value="3" />
     <Setter Property="BorderThickness" Value="3" />
-    <Setter Property="CornerRadius" Value="10" />
+    <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
     <Setter Property="Template">
     <Setter Property="Template">
       <Setter.Value>
       <Setter.Value>
         <ControlTemplate>
         <ControlTemplate>
@@ -25,27 +32,28 @@
 
 
     <Style Selector="^:horizontal">
     <Style Selector="^:horizontal">
       <Setter Property="BorderThickness" Value="0" />
       <Setter Property="BorderThickness" Value="0" />
-      <Setter Property="CornerRadius" Value="10" />
-      <Setter Property="Height" Value="20" />
+      <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+      <Setter Property="Height" Value="{DynamicResource ColorSliderSize}" />
       <Setter Property="Template">
       <Setter Property="Template">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
           <Border BorderThickness="{TemplateBinding BorderThickness}"
           <Border BorderThickness="{TemplateBinding BorderThickness}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   CornerRadius="{TemplateBinding CornerRadius}">
                   CornerRadius="{TemplateBinding CornerRadius}">
             <Grid Margin="{TemplateBinding Padding}">
             <Grid Margin="{TemplateBinding Padding}">
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{StaticResource ColorControlCheckeredBackgroundBrush}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding Background}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
+              <Border HorizontalAlignment="Stretch"
+                      VerticalAlignment="Center"
+                      Height="{Binding ElementName=PART_Track, Path=Bounds.Height}"
+                      Background="{StaticResource ColorControlCheckeredBackgroundBrush}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
+              <Border HorizontalAlignment="Stretch"
+                      VerticalAlignment="Center"
+                      Height="{Binding ElementName=PART_Track, Path=Bounds.Height}"
+                      Background="{TemplateBinding Background}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
               <Track Name="PART_Track"
               <Track Name="PART_Track"
+                     Height="{DynamicResource ColorSliderTrackSize}"
                      HorizontalAlignment="Stretch"
                      HorizontalAlignment="Stretch"
-                     VerticalAlignment="Stretch"
+                     VerticalAlignment="Center"
                      Minimum="{TemplateBinding Minimum}"
                      Minimum="{TemplateBinding Minimum}"
                      Maximum="{TemplateBinding Maximum}"
                      Maximum="{TemplateBinding Maximum}"
                      Value="{TemplateBinding Value, Mode=TwoWay}"
                      Value="{TemplateBinding Value, Mode=TwoWay}"
@@ -82,7 +90,7 @@
                   </RepeatButton>
                   </RepeatButton>
                 </Track.IncreaseButton>
                 </Track.IncreaseButton>
                 <Thumb Name="ColorSliderThumb"
                 <Thumb Name="ColorSliderThumb"
-                       Theme="{StaticResource ColorSliderThumbTheme}"
+                       Theme="{DynamicResource ColorSliderThumbTheme}"
                        Margin="0"
                        Margin="0"
                        Padding="0"
                        Padding="0"
                        DataContext="{TemplateBinding Value}"
                        DataContext="{TemplateBinding Value}"
@@ -97,26 +105,27 @@
 
 
     <Style Selector="^:vertical">
     <Style Selector="^:vertical">
       <Setter Property="BorderThickness" Value="0" />
       <Setter Property="BorderThickness" Value="0" />
-      <Setter Property="CornerRadius" Value="10" />
-      <Setter Property="Width" Value="20" />
+      <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+      <Setter Property="Width" Value="{DynamicResource ColorSliderSize}" />
       <Setter Property="Template">
       <Setter Property="Template">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
         <ControlTemplate TargetType="{x:Type ColorSlider}">
           <Border BorderThickness="{TemplateBinding BorderThickness}"
           <Border BorderThickness="{TemplateBinding BorderThickness}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   BorderBrush="{TemplateBinding BorderBrush}"
                   CornerRadius="{TemplateBinding CornerRadius}">
                   CornerRadius="{TemplateBinding CornerRadius}">
             <Grid Margin="{TemplateBinding Padding}">
             <Grid Margin="{TemplateBinding Padding}">
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{StaticResource ColorControlCheckeredBackgroundBrush}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding Background}"
-                         RadiusX="{TemplateBinding CornerRadius, Converter={StaticResource TopLeftCornerRadiusConverter}}"
-                         RadiusY="{TemplateBinding CornerRadius, Converter={StaticResource BottomRightCornerRadiusConverter}}" />
+              <Border HorizontalAlignment="Center"
+                      VerticalAlignment="Stretch"
+                      Width="{Binding ElementName=PART_Track, Path=Bounds.Width}"
+                      Background="{StaticResource ColorControlCheckeredBackgroundBrush}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
+              <Border HorizontalAlignment="Center"
+                      VerticalAlignment="Stretch"
+                      Width="{Binding ElementName=PART_Track, Path=Bounds.Width}"
+                      Background="{TemplateBinding Background}"
+                      CornerRadius="{DynamicResource ColorSliderTrackCornerRadius}" />
               <Track Name="PART_Track"
               <Track Name="PART_Track"
-                     HorizontalAlignment="Stretch"
+                     Width="{DynamicResource ColorSliderTrackSize}"
+                     HorizontalAlignment="Center"
                      VerticalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      Minimum="{TemplateBinding Minimum}"
                      Minimum="{TemplateBinding Minimum}"
                      Maximum="{TemplateBinding Maximum}"
                      Maximum="{TemplateBinding Maximum}"
@@ -154,7 +163,7 @@
                   </RepeatButton>
                   </RepeatButton>
                 </Track.IncreaseButton>
                 </Track.IncreaseButton>
                 <Thumb Name="ColorSliderThumb"
                 <Thumb Name="ColorSliderThumb"
-                       Theme="{StaticResource ColorSliderThumbTheme}"
+                       Theme="{DynamicResource ColorSliderThumbTheme}"
                        Margin="0"
                        Margin="0"
                        Padding="0"
                        Padding="0"
                        DataContext="{TemplateBinding Value}"
                        DataContext="{TemplateBinding Value}"

+ 2 - 1
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml

@@ -8,7 +8,6 @@
 
 
   <pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
   <pc:ContrastBrushConverter x:Key="ContrastBrushConverter" />
   <converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
   <converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
-  <converters:ColorToHexConverter x:Key="ColorToHexConverter" />
   <converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
   <converters:DoNothingForNullConverter x:Key="DoNothingForNullConverter" />
   <globalization:NumberFormatInfo x:Key="ColorViewComponentNumberFormat" NumberDecimalDigits="0" />
   <globalization:NumberFormatInfo x:Key="ColorViewComponentNumberFormat" NumberDecimalDigits="0" />
   <x:Double x:Key="ColorViewTabStripHeight">48</x:Double>
   <x:Double x:Key="ColorViewTabStripHeight">48</x:Double>
@@ -427,6 +426,7 @@
                                  Content="RGB"
                                  Content="RGB"
                                  CornerRadius="0,0,0,0"
                                  CornerRadius="0,0,0,0"
                                  BorderThickness="1,1,0,1"
                                  BorderThickness="1,1,0,1"
+                                 Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Rgba}, Mode=TwoWay}" />
                     <RadioButton x:Name="HsvRadioButton"
                     <RadioButton x:Name="HsvRadioButton"
                                  Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
                                  Theme="{StaticResource ColorViewColorModelRadioButtonTheme}"
@@ -434,6 +434,7 @@
                                  Content="HSV"
                                  Content="HSV"
                                  CornerRadius="0,0,0,0"
                                  CornerRadius="0,0,0,0"
                                  BorderThickness="0,1,1,1"
                                  BorderThickness="0,1,1,1"
+                                 Height="{Binding ElementName=PART_HexTextBox, Path=Bounds.Height}"
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                                  IsChecked="{TemplateBinding ColorModel, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static controls:ColorModel.Hsva}, Mode=TwoWay}" />
                   </Grid>
                   </Grid>
                   <Grid x:Name="HexInputGrid"
                   <Grid x:Name="HexInputGrid"

+ 1 - 1
src/Avalonia.Controls.ItemsRepeater/Layout/UniformGridLayout.cs

@@ -113,7 +113,7 @@ namespace Avalonia.Layout
             AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinRowSpacing));
             AvaloniaProperty.Register<UniformGridLayout, double>(nameof(MinRowSpacing));
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="MaximumRowsOrColumnsProperty"/> property.
+        /// Defines the <see cref="MaximumRowsOrColumns"/> property.
         /// </summary>
         /// </summary>
         public static readonly StyledProperty<int> MaximumRowsOrColumnsProperty =
         public static readonly StyledProperty<int> MaximumRowsOrColumnsProperty =
             AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MaximumRowsOrColumns));
             AvaloniaProperty.Register<UniformGridLayout, int>(nameof(MaximumRowsOrColumns));

+ 47 - 57
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@@ -52,34 +52,32 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="IsTextCompletionEnabled" /> property.</value>
         /// <value>The identifier for the <see cref="IsTextCompletionEnabled" /> property.</value>
         public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
         public static readonly StyledProperty<bool> IsTextCompletionEnabledProperty =
-            AvaloniaProperty.Register<AutoCompleteBox, bool>(nameof(IsTextCompletionEnabled));
+            AvaloniaProperty.Register<AutoCompleteBox, bool>(
+                nameof(IsTextCompletionEnabled));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="ItemTemplate" /> property.
         /// Identifies the <see cref="ItemTemplate" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="ItemTemplate" /> property.</value>
         /// <value>The identifier for the <see cref="ItemTemplate" /> property.</value>
         public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
         public static readonly StyledProperty<IDataTemplate> ItemTemplateProperty =
-            AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(nameof(ItemTemplate));
+            AvaloniaProperty.Register<AutoCompleteBox, IDataTemplate>(
+                nameof(ItemTemplate));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="IsDropDownOpen" /> property.
         /// Identifies the <see cref="IsDropDownOpen" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="IsDropDownOpen" /> property.</value>
         /// <value>The identifier for the <see cref="IsDropDownOpen" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, bool> IsDropDownOpenProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, bool>(
-                nameof(IsDropDownOpen),
-                o => o.IsDropDownOpen,
-                (o, v) => o.IsDropDownOpen = v);
+        public static readonly StyledProperty<bool> IsDropDownOpenProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, bool>(
+                nameof(IsDropDownOpen));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="SelectedItem" /> property.
         /// Identifies the <see cref="SelectedItem" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier the <see cref="SelectedItem" /> property.</value>
         /// <value>The identifier the <see cref="SelectedItem" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, object?> SelectedItemProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, object?>(
+        public static readonly StyledProperty<object?> SelectedItemProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, object?>(
                 nameof(SelectedItem),
                 nameof(SelectedItem),
-                o => o.SelectedItem,
-                (o, v) => o.SelectedItem = v,
                 defaultBindingMode: BindingMode.TwoWay,
                 defaultBindingMode: BindingMode.TwoWay,
                 enableDataValidation: true);
                 enableDataValidation: true);
 
 
@@ -115,58 +113,50 @@ namespace Avalonia.Controls
         /// Identifies the <see cref="ItemFilter" /> property.
         /// Identifies the <see cref="ItemFilter" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="ItemFilter" /> property.</value>
         /// <value>The identifier for the <see cref="ItemFilter" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
-                nameof(ItemFilter),
-                o => o.ItemFilter,
-                (o, v) => o.ItemFilter = v);
+        public static readonly StyledProperty<AutoCompleteFilterPredicate<object?>?> ItemFilterProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterPredicate<object?>?>(
+                nameof(ItemFilter));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="TextFilter" /> property.
         /// Identifies the <see cref="TextFilter" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="TextFilter" /> property.</value>
         /// <value>The identifier for the <see cref="TextFilter" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
+        public static readonly StyledProperty<AutoCompleteFilterPredicate<string?>?> TextFilterProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteFilterPredicate<string?>?>(
                 nameof(TextFilter),
                 nameof(TextFilter),
-                o => o.TextFilter,
-                (o, v) => o.TextFilter = v,
-                unsetValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
+                defaultValue: AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="ItemSelector" /> property.
         /// Identifies the <see cref="ItemSelector" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="ItemSelector" /> property.</value>
         /// <value>The identifier for the <see cref="ItemSelector" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<object>?> ItemSelectorProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<object>?>(
-                nameof(ItemSelector),
-                o => o.ItemSelector,
-                (o, v) => o.ItemSelector = v);
+        public static readonly StyledProperty<AutoCompleteSelector<object>?> ItemSelectorProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteSelector<object>?>(
+                nameof(ItemSelector));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="TextSelector" /> property.
         /// Identifies the <see cref="TextSelector" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="TextSelector" /> property.</value>
         /// <value>The identifier for the <see cref="TextSelector" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, AutoCompleteSelector<string?>?> TextSelectorProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, AutoCompleteSelector<string?>?>(
-                nameof(TextSelector),
-                o => o.TextSelector,
-                (o, v) => o.TextSelector = v);
+        public static readonly StyledProperty<AutoCompleteSelector<string?>?> TextSelectorProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, AutoCompleteSelector<string?>?>(
+                nameof(TextSelector));
 
 
         /// <summary>
         /// <summary>
         /// Identifies the <see cref="Items" /> property.
         /// Identifies the <see cref="Items" /> property.
         /// </summary>
         /// </summary>
         /// <value>The identifier for the <see cref="Items" /> property.</value>
         /// <value>The identifier for the <see cref="Items" /> property.</value>
-        public static readonly DirectProperty<AutoCompleteBox, IEnumerable?> ItemsProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, IEnumerable?>(
-                nameof(Items),
-                o => o.Items,
-                (o, v) => o.Items = v);
+        public static readonly StyledProperty<IEnumerable?> ItemsProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, IEnumerable?>(
+                nameof(Items));
 
 
-        public static readonly DirectProperty<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
-            AvaloniaProperty.RegisterDirect<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
-                nameof(AsyncPopulator),
-                o => o.AsyncPopulator,
-                (o, v) => o.AsyncPopulator = v);
+        /// <summary>
+        /// Identifies the <see cref="AsyncPopulator" /> property.
+        /// </summary>
+        /// <value>The identifier for the <see cref="AsyncPopulator" /> property.</value>
+        public static readonly StyledProperty<Func<string?, CancellationToken, Task<IEnumerable<object>>>?> AsyncPopulatorProperty =
+            AvaloniaProperty.Register<AutoCompleteBox, Func<string?, CancellationToken, Task<IEnumerable<object>>>?>(
+                nameof(AsyncPopulator));
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the minimum number of characters required to be entered
         /// Gets or sets the minimum number of characters required to be entered
@@ -265,8 +255,8 @@ namespace Avalonia.Controls
         /// </value>
         /// </value>
         public bool IsDropDownOpen
         public bool IsDropDownOpen
         {
         {
-            get => _isDropDownOpen;
-            set => SetAndRaise(IsDropDownOpenProperty, ref  _isDropDownOpen, value);
+            get => GetValue(IsDropDownOpenProperty);
+            set => SetValue(IsDropDownOpenProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -303,8 +293,8 @@ namespace Avalonia.Controls
         /// </remarks>
         /// </remarks>
         public object? SelectedItem
         public object? SelectedItem
         {
         {
-            get => _selectedItem;
-            set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
+            get => GetValue(SelectedItemProperty);
+            set => SetValue(SelectedItemProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -388,8 +378,8 @@ namespace Avalonia.Controls
         /// </remarks>
         /// </remarks>
         public AutoCompleteFilterPredicate<object?>? ItemFilter
         public AutoCompleteFilterPredicate<object?>? ItemFilter
         {
         {
-            get => _itemFilter;
-            set => SetAndRaise(ItemFilterProperty, ref _itemFilter, value);
+            get => GetValue(ItemFilterProperty);
+            set => SetValue(ItemFilterProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -406,8 +396,8 @@ namespace Avalonia.Controls
         /// </remarks>
         /// </remarks>
         public AutoCompleteFilterPredicate<string?>? TextFilter
         public AutoCompleteFilterPredicate<string?>? TextFilter
         {
         {
-            get => _textFilter;
-            set => SetAndRaise(TextFilterProperty, ref _textFilter, value);
+            get => GetValue(TextFilterProperty);
+            set => SetValue(TextFilterProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -420,8 +410,8 @@ namespace Avalonia.Controls
         /// </value>
         /// </value>
         public AutoCompleteSelector<object>? ItemSelector
         public AutoCompleteSelector<object>? ItemSelector
         {
         {
-            get => _itemSelector;
-            set => SetAndRaise(ItemSelectorProperty, ref _itemSelector, value);
+            get => GetValue(ItemSelectorProperty);
+            set => SetValue(ItemSelectorProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -436,14 +426,14 @@ namespace Avalonia.Controls
         /// </value>
         /// </value>
         public AutoCompleteSelector<string?>? TextSelector
         public AutoCompleteSelector<string?>? TextSelector
         {
         {
-            get => _textSelector;
-            set => SetAndRaise(TextSelectorProperty, ref _textSelector, value);
+            get => GetValue(TextSelectorProperty);
+            set => SetValue(TextSelectorProperty, value);
         }
         }
 
 
         public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
         public Func<string?, CancellationToken, Task<IEnumerable<object>>>? AsyncPopulator
         {
         {
-            get => _asyncPopulator;
-            set => SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value);
+            get => GetValue(AsyncPopulatorProperty);
+            set => SetValue(AsyncPopulatorProperty, value);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -454,8 +444,8 @@ namespace Avalonia.Controls
         /// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value>
         /// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value>
         public IEnumerable? Items
         public IEnumerable? Items
         {
         {
-            get => _itemsEnumerable;
-            set => SetAndRaise(ItemsProperty, ref _itemsEnumerable, value);
+            get => GetValue(ItemsProperty);
+            set => SetValue(ItemsProperty, value);
         }
         }
     }
     }
 }
 }

+ 28 - 37
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@@ -94,8 +94,6 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         private const string ElementTextBox = "PART_TextBox";
         private const string ElementTextBox = "PART_TextBox";
 
 
-        private IEnumerable? _itemsEnumerable;
-
         /// <summary>
         /// <summary>
         /// Gets or sets a local cached copy of the items data.
         /// Gets or sets a local cached copy of the items data.
         /// </summary>
         /// </summary>
@@ -188,24 +186,15 @@ namespace Avalonia.Controls
         /// </summary>
         /// </summary>
         private IDisposable? _collectionChangeSubscription;
         private IDisposable? _collectionChangeSubscription;
 
 
-        private Func<string?, CancellationToken, Task<IEnumerable<object>>>? _asyncPopulator;
         private CancellationTokenSource? _populationCancellationTokenSource;
         private CancellationTokenSource? _populationCancellationTokenSource;
 
 
         private bool _itemTemplateIsFromValueMemberBinding = true;
         private bool _itemTemplateIsFromValueMemberBinding = true;
         private bool _settingItemTemplateFromValueMemberBinding;
         private bool _settingItemTemplateFromValueMemberBinding;
 
 
-        private object? _selectedItem;
-        private bool _isDropDownOpen;
         private bool _isFocused = false;
         private bool _isFocused = false;
 
 
         private string? _searchText = string.Empty;
         private string? _searchText = string.Empty;
 
 
-        private AutoCompleteFilterPredicate<object?>? _itemFilter;
-        private AutoCompleteFilterPredicate<string?>? _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith);
-
-        private AutoCompleteSelector<object>? _itemSelector;
-        private AutoCompleteSelector<string?>? _textSelector;
-
         private readonly EventHandler _populateDropDownHandler;
         private readonly EventHandler _populateDropDownHandler;
 
 
         /// <summary>
         /// <summary>
@@ -264,7 +253,7 @@ namespace Avalonia.Controls
             bool isEnabled = (bool)e.NewValue!;
             bool isEnabled = (bool)e.NewValue!;
             if (!isEnabled)
             if (!isEnabled)
             {
             {
-                IsDropDownOpen = false;
+                SetCurrentValue(IsDropDownOpenProperty, false);
             }
             }
         }
         }
 
 
@@ -388,7 +377,7 @@ namespace Avalonia.Controls
             {
             {
                 // Reset the old value before it was incorrectly written
                 // Reset the old value before it was incorrectly written
                 _ignorePropertyChange = true;
                 _ignorePropertyChange = true;
-                SetValue(e.Property, e.OldValue);
+                SetCurrentValue(e.Property, e.OldValue);
 
 
                 throw new InvalidOperationException("Cannot set read-only property SearchText.");
                 throw new InvalidOperationException("Cannot set read-only property SearchText.");
             }
             }
@@ -403,7 +392,7 @@ namespace Avalonia.Controls
             AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue!;
             AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue!;
 
 
             // Sets the filter predicate for the new value
             // Sets the filter predicate for the new value
-            TextFilter = AutoCompleteSearch.GetFilter(mode);
+            SetCurrentValue(TextFilterProperty, AutoCompleteSearch.GetFilter(mode));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -417,12 +406,12 @@ namespace Avalonia.Controls
             // If null, revert to the "None" predicate
             // If null, revert to the "None" predicate
             if (value == null)
             if (value == null)
             {
             {
-                FilterMode = AutoCompleteFilterMode.None;
+                SetCurrentValue(FilterModeProperty, AutoCompleteFilterMode.None);
             }
             }
             else
             else
             {
             {
-                FilterMode = AutoCompleteFilterMode.Custom;
-                TextFilter = null;
+                SetCurrentValue(FilterModeProperty, AutoCompleteFilterMode.Custom);
+                SetCurrentValue(TextFilterProperty, null);
             }
             }
         }
         }
 
 
@@ -442,7 +431,7 @@ namespace Avalonia.Controls
         }
         }
         private void OnValueMemberBindingChanged(IBinding? value)
         private void OnValueMemberBindingChanged(IBinding? value)
         {
         {
-            if(_itemTemplateIsFromValueMemberBinding)
+            if (_itemTemplateIsFromValueMemberBinding)
             {
             {
                 var template =
                 var template =
                     new FuncDataTemplate(
                     new FuncDataTemplate(
@@ -456,7 +445,7 @@ namespace Avalonia.Controls
                         });
                         });
 
 
                 _settingItemTemplateFromValueMemberBinding = true;
                 _settingItemTemplateFromValueMemberBinding = true;
-                ItemTemplate = template;
+                SetCurrentValue(ItemTemplateProperty, template);
                 _settingItemTemplateFromValueMemberBinding = false;
                 _settingItemTemplateFromValueMemberBinding = false;
             }
             }
         }
         }
@@ -713,7 +702,7 @@ namespace Avalonia.Controls
                 // The drop down is not open, the Down key will toggle it open.
                 // The drop down is not open, the Down key will toggle it open.
                 if (e.Key == Key.Down)
                 if (e.Key == Key.Down)
                 {
                 {
-                    IsDropDownOpen = true;
+                    SetCurrentValue(IsDropDownOpenProperty, true);
                     e.Handled = true;
                     e.Handled = true;
                 }
                 }
             }
             }
@@ -722,7 +711,7 @@ namespace Avalonia.Controls
             switch (e.Key)
             switch (e.Key)
             {
             {
                 case Key.F4:
                 case Key.F4:
-                    IsDropDownOpen = !IsDropDownOpen;
+                    SetCurrentValue(IsDropDownOpenProperty, !IsDropDownOpen);
                     e.Handled = true;
                     e.Handled = true;
                     break;
                     break;
 
 
@@ -827,7 +816,7 @@ namespace Avalonia.Controls
             }
             }
             else
             else
             {
             {
-                IsDropDownOpen = false;
+                SetCurrentValue(IsDropDownOpenProperty, false);
                 _userCalledPopulate = false;
                 _userCalledPopulate = false;
                 ClearTextBoxSelection();
                 ClearTextBoxSelection();
             }
             }
@@ -1021,7 +1010,7 @@ namespace Avalonia.Controls
             if (args.Cancel)
             if (args.Cancel)
             {
             {
                 _ignorePropertyChange = true;
                 _ignorePropertyChange = true;
-                SetValue(IsDropDownOpenProperty, oldValue);
+                SetCurrentValue(IsDropDownOpenProperty, oldValue);
             }
             }
             else
             else
             {
             {
@@ -1046,7 +1035,7 @@ namespace Avalonia.Controls
             if (args.Cancel)
             if (args.Cancel)
             {
             {
                 _ignorePropertyChange = true;
                 _ignorePropertyChange = true;
-                SetValue(IsDropDownOpenProperty, oldValue);
+                SetCurrentValue(IsDropDownOpenProperty, oldValue);
             }
             }
             else
             else
             {
             {
@@ -1066,7 +1055,7 @@ namespace Avalonia.Controls
             // Force the drop down dependency property to be false.
             // Force the drop down dependency property to be false.
             if (IsDropDownOpen)
             if (IsDropDownOpen)
             {
             {
-                IsDropDownOpen = false;
+                SetCurrentValue(IsDropDownOpenProperty, false);
             }
             }
 
 
             // Fire the DropDownClosed event
             // Fire the DropDownClosed event
@@ -1088,7 +1077,7 @@ namespace Avalonia.Controls
             // Update the prefix/search text.
             // Update the prefix/search text.
             SearchText = Text;
             SearchText = Text;
 
 
-            if(TryPopulateAsync(SearchText))
+            if (TryPopulateAsync(SearchText))
             {
             {
                 return;
                 return;
             }
             }
@@ -1110,7 +1099,7 @@ namespace Avalonia.Controls
             _populationCancellationTokenSource?.Dispose();
             _populationCancellationTokenSource?.Dispose();
             _populationCancellationTokenSource = null;
             _populationCancellationTokenSource = null;
 
 
-            if(_asyncPopulator == null)
+            if (AsyncPopulator == null)
             {
             {
                 return false;
                 return false;
             }
             }
@@ -1127,7 +1116,7 @@ namespace Avalonia.Controls
 
 
             try
             try
             {
             {
-                IEnumerable<object> result = await _asyncPopulator!.Invoke(searchText, cancellationToken);
+                IEnumerable<object> result = await AsyncPopulator!.Invoke(searchText, cancellationToken);
                 var resultList = result.ToList();
                 var resultList = result.ToList();
 
 
                 if (cancellationToken.IsCancellationRequested)
                 if (cancellationToken.IsCancellationRequested)
@@ -1139,7 +1128,7 @@ namespace Avalonia.Controls
                 {
                 {
                     if (!cancellationToken.IsCancellationRequested)
                     if (!cancellationToken.IsCancellationRequested)
                     {
                     {
-                        Items = resultList;
+                        SetCurrentValue(ItemsProperty, resultList);
                         PopulateComplete();
                         PopulateComplete();
                     }
                     }
                 });
                 });
@@ -1199,7 +1188,7 @@ namespace Avalonia.Controls
         private string? FormatValue(object? value, bool clearDataContext)
         private string? FormatValue(object? value, bool clearDataContext)
         {
         {
             string? result = FormatValue(value);
             string? result = FormatValue(value);
-            if(clearDataContext && _valueBindingEvaluator != null)
+            if (clearDataContext && _valueBindingEvaluator != null)
             {
             {
                 _valueBindingEvaluator.ClearDataContext();
                 _valueBindingEvaluator.ClearDataContext();
             }
             }
@@ -1332,7 +1321,7 @@ namespace Avalonia.Controls
             // 1. Minimum prefix length
             // 1. Minimum prefix length
             // 2. If a delay timer is in use, use it
             // 2. If a delay timer is in use, use it
             bool populateReady = newText.Length >= MinimumPrefixLength && MinimumPrefixLength >= 0;
             bool populateReady = newText.Length >= MinimumPrefixLength && MinimumPrefixLength >= 0;
-            if(populateReady && MinimumPrefixLength == 0 && String.IsNullOrEmpty(newText) && String.IsNullOrEmpty(SearchText))
+            if (populateReady && MinimumPrefixLength == 0 && String.IsNullOrEmpty(newText) && String.IsNullOrEmpty(SearchText))
             {
             {
                 populateReady = false;
                 populateReady = false;
             }
             }
@@ -1361,10 +1350,12 @@ namespace Avalonia.Controls
                 {
                 {
                     _skipSelectedItemTextUpdate = true;
                     _skipSelectedItemTextUpdate = true;
                 }
                 }
-                SelectedItem = null;
+
+                SetCurrentValue(SelectedItemProperty, null);
+
                 if (IsDropDownOpen)
                 if (IsDropDownOpen)
                 {
                 {
-                    IsDropDownOpen = false;
+                    SetCurrentValue(IsDropDownOpenProperty, false);
                 }
                 }
             }
             }
         }
         }
@@ -1600,7 +1591,7 @@ namespace Avalonia.Controls
             if (isDropDownOpen != IsDropDownOpen)
             if (isDropDownOpen != IsDropDownOpen)
             {
             {
                 _ignorePropertyChange = true;
                 _ignorePropertyChange = true;
-                IsDropDownOpen = isDropDownOpen;
+                SetCurrentValue(IsDropDownOpenProperty, isDropDownOpen);
             }
             }
             if (IsDropDownOpen)
             if (IsDropDownOpen)
             {
             {
@@ -1688,7 +1679,7 @@ namespace Avalonia.Controls
             {
             {
                 _skipSelectedItemTextUpdate = true;
                 _skipSelectedItemTextUpdate = true;
             }
             }
-            SelectedItem = newSelectedItem;
+            SetCurrentValue(SelectedItemProperty, newSelectedItem);
 
 
             // Restore updates for TextSelection
             // Restore updates for TextSelection
             if (_ignoreTextSelectionChange)
             if (_ignoreTextSelectionChange)
@@ -1784,7 +1775,7 @@ namespace Avalonia.Controls
         /// <param name="e">The selection changed event data.</param>
         /// <param name="e">The selection changed event data.</param>
         private void OnAdapterSelectionChanged(object? sender, SelectionChangedEventArgs e)
         private void OnAdapterSelectionChanged(object? sender, SelectionChangedEventArgs e)
         {
         {
-            SelectedItem = _adapter!.SelectedItem;
+            SetCurrentValue(SelectedItemProperty, _adapter!.SelectedItem);
         }
         }
 
 
         //TODO Check UpdateTextCompletion
         //TODO Check UpdateTextCompletion
@@ -1795,7 +1786,7 @@ namespace Avalonia.Controls
         /// <param name="e">The event data.</param>
         /// <param name="e">The event data.</param>
         private void OnAdapterSelectionComplete(object? sender, RoutedEventArgs e)
         private void OnAdapterSelectionComplete(object? sender, RoutedEventArgs e)
         {
         {
-            IsDropDownOpen = false;
+            SetCurrentValue(IsDropDownOpenProperty, false);
 
 
             // Completion will update the selected value
             // Completion will update the selected value
             //UpdateTextCompletion(false);
             //UpdateTextCompletion(false);

+ 3 - 4
src/Avalonia.Controls/Carousel.cs

@@ -1,7 +1,6 @@
 using Avalonia.Animation;
 using Avalonia.Animation;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
 
 
 namespace Avalonia.Controls
 namespace Avalonia.Controls
 {
 {
@@ -20,8 +19,8 @@ namespace Avalonia.Controls
         /// The default value of <see cref="ItemsControl.ItemsPanelProperty"/> for 
         /// The default value of <see cref="ItemsControl.ItemsPanelProperty"/> for 
         /// <see cref="Carousel"/>.
         /// <see cref="Carousel"/>.
         /// </summary>
         /// </summary>
-        private static readonly ITemplate<Panel> PanelTemplate =
-            new FuncTemplate<Panel>(() => new VirtualizingCarouselPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new VirtualizingCarouselPanel());
 
 
         private IScrollable? _scroller;
         private IScrollable? _scroller;
 
 
@@ -31,7 +30,7 @@ namespace Avalonia.Controls
         static Carousel()
         static Carousel()
         {
         {
             SelectionModeProperty.OverrideDefaultValue<Carousel>(SelectionMode.AlwaysSelected);
             SelectionModeProperty.OverrideDefaultValue<Carousel>(SelectionMode.AlwaysSelected);
-            ItemsPanelProperty.OverrideDefaultValue<Carousel>(PanelTemplate);
+            ItemsPanelProperty.OverrideDefaultValue<Carousel>(DefaultPanel);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 2 - 5
src/Avalonia.Controls/ComboBox.cs

@@ -1,13 +1,10 @@
 using System;
 using System;
-using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Selection;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Shapes;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Layout;
@@ -29,8 +26,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        private static readonly FuncTemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new VirtualizingStackPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new VirtualizingStackPanel());
 
 
         /// <summary>
         /// <summary>
         /// Defines the <see cref="IsDropDownOpen"/> property.
         /// Defines the <see cref="IsDropDownOpen"/> property.

+ 2 - 3
src/Avalonia.Controls/ContextMenu.cs

@@ -4,7 +4,6 @@ using System.ComponentModel;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
 using System.Linq;
 using System.Linq;
 using Avalonia.Controls.Diagnostics;
 using Avalonia.Controls.Diagnostics;
-using Avalonia.Controls.Generators;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives.PopupPositioning;
 using Avalonia.Controls.Primitives.PopupPositioning;
@@ -84,8 +83,8 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<Control?> PlacementTargetProperty =
         public static readonly StyledProperty<Control?> PlacementTargetProperty =
             Popup.PlacementTargetProperty.AddOwner<ContextMenu>();
             Popup.PlacementTargetProperty.AddOwner<ContextMenu>();
 
 
-        private static readonly ITemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new StackPanel { Orientation = Orientation.Vertical });
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new StackPanel { Orientation = Orientation.Vertical });
         private Popup? _popup;
         private Popup? _popup;
         private List<Control>? _attachedControls;
         private List<Control>? _attachedControls;
         private IInputElement? _previousFocus;
         private IInputElement? _previousFocus;

+ 5 - 7
src/Avalonia.Controls/ItemsControl.cs

@@ -2,9 +2,7 @@ using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Collections.Specialized;
-using System.Diagnostics.CodeAnalysis;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
-using Avalonia.Collections;
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Generators;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Presenters;
@@ -29,8 +27,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// The default value for the <see cref="ItemsPanel"/> property.
         /// The default value for the <see cref="ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        private static readonly FuncTemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new StackPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new StackPanel());
 
 
         /// <summary>
         /// <summary>
         /// Defines the <see cref="Items"/> property.
         /// Defines the <see cref="Items"/> property.
@@ -58,8 +56,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// Defines the <see cref="ItemsPanel"/> property.
         /// Defines the <see cref="ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        public static readonly StyledProperty<ITemplate<Panel>> ItemsPanelProperty =
-            AvaloniaProperty.Register<ItemsControl, ITemplate<Panel>>(nameof(ItemsPanel), DefaultPanel);
+        public static readonly StyledProperty<ITemplate<Panel?>> ItemsPanelProperty =
+            AvaloniaProperty.Register<ItemsControl, ITemplate<Panel?>>(nameof(ItemsPanel), DefaultPanel);
 
 
         /// <summary>
         /// <summary>
         /// Defines the <see cref="ItemsSource"/> property.
         /// Defines the <see cref="ItemsSource"/> property.
@@ -202,7 +200,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// Gets or sets the panel used to display the items.
         /// Gets or sets the panel used to display the items.
         /// </summary>
         /// </summary>
-        public ITemplate<Panel> ItemsPanel
+        public ITemplate<Panel?> ItemsPanel
         {
         {
             get => GetValue(ItemsPanelProperty);
             get => GetValue(ItemsPanelProperty);
             set => SetValue(ItemsPanelProperty, value);
             set => SetValue(ItemsPanelProperty, value);

+ 2 - 5
src/Avalonia.Controls/ListBox.cs

@@ -1,13 +1,10 @@
 using System.Collections;
 using System.Collections;
-using Avalonia.Controls.Generators;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Selection;
 using Avalonia.Controls.Selection;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Platform;
-using Avalonia.VisualTree;
 
 
 namespace Avalonia.Controls
 namespace Avalonia.Controls
 {
 {
@@ -20,8 +17,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        private static readonly FuncTemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new VirtualizingStackPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new VirtualizingStackPanel());
 
 
         /// <summary>
         /// <summary>
         /// Defines the <see cref="Scroll"/> property.
         /// Defines the <see cref="Scroll"/> property.

+ 2 - 4
src/Avalonia.Controls/Menu.cs

@@ -1,7 +1,6 @@
 using Avalonia.Automation;
 using Avalonia.Automation;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
 using Avalonia.Controls.Platform;
 using Avalonia.Controls.Platform;
-using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
@@ -14,9 +13,8 @@ namespace Avalonia.Controls
     /// </summary>
     /// </summary>
     public class Menu : MenuBase, IMainMenu
     public class Menu : MenuBase, IMainMenu
     {
     {
-        private static readonly ITemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new StackPanel { Orientation = Orientation.Horizontal });
-
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new (() => new StackPanel { Orientation = Orientation.Horizontal });
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="Menu"/> class.
         /// Initializes a new instance of the <see cref="Menu"/> class.

+ 2 - 3
src/Avalonia.Controls/MenuItem.cs

@@ -4,7 +4,6 @@ using System.Linq;
 using Avalonia.Reactive;
 using Avalonia.Reactive;
 using System.Windows.Input;
 using System.Windows.Input;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
-using Avalonia.Controls.Generators;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Mixins;
 using Avalonia.Controls.Mixins;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
@@ -107,8 +106,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        private static readonly ITemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new StackPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new StackPanel());
 
 
         private bool _commandCanExecute = true;
         private bool _commandCanExecute = true;
         private bool _commandBindingError;
         private bool _commandBindingError;

+ 9 - 3
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Controls.Presenters
         /// <summary>
         /// <summary>
         /// Defines the <see cref="ItemsPanel"/> property.
         /// Defines the <see cref="ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        public static readonly StyledProperty<ITemplate<Panel>> ItemsPanelProperty =
+        public static readonly StyledProperty<ITemplate<Panel?>> ItemsPanelProperty =
             ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>();
             ItemsControl.ItemsPanelProperty.AddOwner<ItemsPresenter>();
 
 
         private PanelContainerGenerator? _generator;
         private PanelContainerGenerator? _generator;
@@ -68,7 +68,7 @@ namespace Avalonia.Controls.Presenters
         /// <summary>
         /// <summary>
         /// Gets or sets a template which creates the <see cref="Panel"/> used to display the items.
         /// Gets or sets a template which creates the <see cref="Panel"/> used to display the items.
         /// </summary>
         /// </summary>
-        public ITemplate<Panel> ItemsPanel
+        public ITemplate<Panel?> ItemsPanel
         {
         {
             get => GetValue(ItemsPanelProperty);
             get => GetValue(ItemsPanelProperty);
             set => SetValue(ItemsPanelProperty, value);
             set => SetValue(ItemsPanelProperty, value);
@@ -166,6 +166,12 @@ namespace Avalonia.Controls.Presenters
                 }
                 }
 
 
                 Panel = ItemsPanel.Build();
                 Panel = ItemsPanel.Build();
+
+                if (Panel is null)
+                {
+                    return;
+                }
+
                 Panel.TemplatedParent = TemplatedParent;
                 Panel.TemplatedParent = TemplatedParent;
                 Panel.IsItemsHost = true;
                 Panel.IsItemsHost = true;
                 _scrollSnapPointsInfo = Panel as IScrollSnapPointsInfo;
                 _scrollSnapPointsInfo = Panel as IScrollSnapPointsInfo;
@@ -183,7 +189,7 @@ namespace Avalonia.Controls.Presenters
                 else
                 else
                     CreateSimplePanelGenerator();
                     CreateSimplePanelGenerator();
 
 
-                if(Panel is IScrollSnapPointsInfo scrollSnapPointsInfo)
+                if (Panel is IScrollSnapPointsInfo scrollSnapPointsInfo)
                 {
                 {
                     scrollSnapPointsInfo.VerticalSnapPointsChanged += (s, e) =>
                     scrollSnapPointsInfo.VerticalSnapPointsChanged += (s, e) =>
                     {
                     {

+ 1 - 1
src/Avalonia.Controls/Primitives/ScrollBar.cs

@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives
             AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation), Orientation.Vertical);
             AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation), Orientation.Vertical);
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="IsExpandedProperty"/> property.
+        /// Defines the <see cref="IsExpanded"/> property.
         /// </summary>
         /// </summary>
         public static readonly DirectProperty<ScrollBar, bool> IsExpandedProperty =
         public static readonly DirectProperty<ScrollBar, bool> IsExpandedProperty =
             AvaloniaProperty.RegisterDirect<ScrollBar, bool>(
             AvaloniaProperty.RegisterDirect<ScrollBar, bool>(

+ 2 - 4
src/Avalonia.Controls/Primitives/TabStrip.cs

@@ -1,15 +1,13 @@
-using Avalonia.Controls.Generators;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Layout;
 using Avalonia.Layout;
-using Avalonia.VisualTree;
 
 
 namespace Avalonia.Controls.Primitives
 namespace Avalonia.Controls.Primitives
 {
 {
     public class TabStrip : SelectingItemsControl
     public class TabStrip : SelectingItemsControl
     {
     {
-        private static readonly FuncTemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new WrapPanel { Orientation = Orientation.Horizontal });
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new WrapPanel { Orientation = Orientation.Horizontal });
 
 
         static TabStrip()
         static TabStrip()
         {
         {

+ 11 - 8
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@@ -286,14 +286,17 @@ namespace Avalonia.Controls.Primitives
                 {
                 {
                     Logger.TryGet(LogEventLevel.Verbose, LogArea.Control)?.Log(this, "Creating control template");
                     Logger.TryGet(LogEventLevel.Verbose, LogArea.Control)?.Log(this, "Creating control template");
 
 
-                    var (child, nameScope) = template.Build(this);
-                    ApplyTemplatedParent(child, this);
-                    ((ISetLogicalParent)child).SetParent(this);
-                    VisualChildren.Add(child);
-
-                    var e = new TemplateAppliedEventArgs(nameScope);
-                    OnApplyTemplate(e);
-                    RaiseEvent(e);
+                    if (template.Build(this) is { } templateResult)
+                    {
+                        var (child, nameScope) = templateResult;
+                        ApplyTemplatedParent(child, this);
+                        ((ISetLogicalParent)child).SetParent(this);
+                        VisualChildren.Add(child);
+
+                        var e = new TemplateAppliedEventArgs(nameScope);
+                        OnApplyTemplate(e);
+                        RaiseEvent(e);
+                    }
                 }
                 }
 
 
                 _appliedTemplate = template;
                 _appliedTemplate = template;

+ 1 - 1
src/Avalonia.Controls/ScrollViewer.cs

@@ -200,7 +200,7 @@ namespace Avalonia.Controls
                 ScrollBarVisibility.Auto);
                 ScrollBarVisibility.Auto);
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="IsExpandedProperty"/> property.
+        /// Defines the <see cref="IsExpanded"/> property.
         /// </summary>
         /// </summary>
         public static readonly DirectProperty<ScrollViewer, bool> IsExpandedProperty =
         public static readonly DirectProperty<ScrollViewer, bool> IsExpandedProperty =
             ScrollBar.IsExpandedProperty.AddOwner<ScrollViewer>(o => o.IsExpanded);
             ScrollBar.IsExpandedProperty.AddOwner<ScrollViewer>(o => o.IsExpanded);

+ 5 - 5
src/Avalonia.Controls/Slider.cs

@@ -80,16 +80,16 @@ namespace Avalonia.Controls
             AvaloniaProperty.Register<Slider, TickPlacement>(nameof(TickPlacement), 0d);
             AvaloniaProperty.Register<Slider, TickPlacement>(nameof(TickPlacement), 0d);
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="TicksProperty"/> property.
+        /// Defines the <see cref="Ticks"/> property.
         /// </summary>
         /// </summary>
         public static readonly StyledProperty<AvaloniaList<double>?> TicksProperty =
         public static readonly StyledProperty<AvaloniaList<double>?> TicksProperty =
             TickBar.TicksProperty.AddOwner<Slider>();
             TickBar.TicksProperty.AddOwner<Slider>();
 
 
         // Slider required parts
         // Slider required parts
-        private bool _isDragging;
-        private Track? _track;
-        private Button? _decreaseButton;
-        private Button? _increaseButton;
+        protected bool _isDragging;
+        protected Track? _track;
+        protected Button? _decreaseButton;
+        protected Button? _increaseButton;
         private IDisposable? _decreaseButtonPressDispose;
         private IDisposable? _decreaseButtonPressDispose;
         private IDisposable? _decreaseButtonReleaseDispose;
         private IDisposable? _decreaseButtonReleaseDispose;
         private IDisposable? _increaseButtonSubscription;
         private IDisposable? _increaseButtonSubscription;

+ 2 - 5
src/Avalonia.Controls/TabControl.cs

@@ -1,8 +1,6 @@
-using System.ComponentModel;
 using System.Linq;
 using System.Linq;
 using Avalonia.Collections;
 using Avalonia.Collections;
 using Avalonia.Automation.Peers;
 using Avalonia.Automation.Peers;
-using Avalonia.Controls.Generators;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
@@ -12,7 +10,6 @@ using Avalonia.LogicalTree;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
 using Avalonia.Automation;
 using Avalonia.Automation;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
-using Avalonia.Data;
 
 
 namespace Avalonia.Controls
 namespace Avalonia.Controls
 {
 {
@@ -61,8 +58,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
         /// </summary>
         /// </summary>
-        private static readonly FuncTemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new WrapPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new WrapPanel());
 
 
         /// <summary>
         /// <summary>
         /// Initializes static members of the <see cref="TabControl"/> class.
         /// Initializes static members of the <see cref="TabControl"/> class.

+ 2 - 2
src/Avalonia.Controls/Templates/FuncTemplate`1.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Controls.Templates
     /// Creates a control from a <see cref="Func{TControl}"/>.
     /// Creates a control from a <see cref="Func{TControl}"/>.
     /// </summary>
     /// </summary>
     /// <typeparam name="TControl">The type of control.</typeparam>
     /// <typeparam name="TControl">The type of control.</typeparam>
-    public class FuncTemplate<TControl> : ITemplate<TControl> where TControl : Control
+    public class FuncTemplate<TControl> : ITemplate<TControl> where TControl : Control?
     {
     {
         private readonly Func<TControl> _func;
         private readonly Func<TControl> _func;
 
 
@@ -31,6 +31,6 @@ namespace Avalonia.Controls.Templates
             return _func();
             return _func();
         }
         }
 
 
-        object ITemplate.Build() => Build();
+        object? ITemplate.Build() => Build();
     }
     }
 }
 }

+ 1 - 3
src/Avalonia.Controls/Templates/IControlTemplate.cs

@@ -1,13 +1,11 @@
-using System;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
-using Avalonia.Styling;
 
 
 namespace Avalonia.Controls.Templates
 namespace Avalonia.Controls.Templates
 {
 {
     /// <summary>
     /// <summary>
     /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
     /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
     /// </summary>
     /// </summary>
-    public interface IControlTemplate : ITemplate<TemplatedControl, ControlTemplateResult>
+    public interface IControlTemplate : ITemplate<TemplatedControl, ControlTemplateResult?>
     {
     {
     }
     }
 
 

+ 1 - 1
src/Avalonia.Controls/TopLevel.cs

@@ -75,7 +75,7 @@ namespace Avalonia.Controls
                 unsetValue: WindowTransparencyLevel.None);        
                 unsetValue: WindowTransparencyLevel.None);        
 
 
         /// <summary>
         /// <summary>
-        /// Defines the <see cref="TransparencyBackgroundFallbackProperty"/> property.
+        /// Defines the <see cref="TransparencyBackgroundFallback"/> property.
         /// </summary>
         /// </summary>
         public static readonly StyledProperty<IBrush> TransparencyBackgroundFallbackProperty =
         public static readonly StyledProperty<IBrush> TransparencyBackgroundFallbackProperty =
             AvaloniaProperty.Register<TopLevel, IBrush>(nameof(TransparencyBackgroundFallback), Brushes.White);
             AvaloniaProperty.Register<TopLevel, IBrush>(nameof(TransparencyBackgroundFallback), Brushes.White);

+ 4 - 0
src/Avalonia.Controls/TreeView.cs

@@ -10,6 +10,7 @@ using Avalonia.Controls.Generators;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Input.Platform;
+using Avalonia.Layout;
 using Avalonia.Threading;
 using Avalonia.Threading;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
 
 
@@ -163,6 +164,9 @@ namespace Avalonia.Controls
         {
         {
             item.IsExpanded = true;
             item.IsExpanded = true;
 
 
+            if (item.Presenter?.Panel is null)
+                (this.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
+
             if (item.Presenter?.Panel is { } panel)
             if (item.Presenter?.Panel is { } panel)
             {
             {
                 foreach (var child in panel.Children)
                 foreach (var child in panel.Children)

+ 19 - 4
src/Avalonia.Controls/TreeViewItem.cs

@@ -40,8 +40,8 @@ namespace Avalonia.Controls
             AvaloniaProperty.RegisterDirect<TreeViewItem, int>(
             AvaloniaProperty.RegisterDirect<TreeViewItem, int>(
                 nameof(Level), o => o.Level);
                 nameof(Level), o => o.Level);
 
 
-        private static readonly ITemplate<Panel> DefaultPanel =
-            new FuncTemplate<Panel>(() => new StackPanel());
+        private static readonly FuncTemplate<Panel?> DefaultPanel =
+            new(() => new StackPanel());
 
 
         private TreeView? _treeView;
         private TreeView? _treeView;
         private Control? _header;
         private Control? _header;
@@ -90,8 +90,20 @@ namespace Avalonia.Controls
 
 
         internal TreeView? TreeViewOwner => _treeView;
         internal TreeView? TreeViewOwner => _treeView;
 
 
-        protected internal override Control CreateContainerForItemOverride() => new TreeViewItem();
-        protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is TreeViewItem;
+        protected internal override Control CreateContainerForItemOverride()
+        {
+            return EnsureTreeView().CreateContainerForItemOverride();
+        }
+
+        protected internal override bool IsItemItsOwnContainerOverride(Control item)
+        {
+            return EnsureTreeView().IsItemItsOwnContainerOverride(item);
+        }
+
+        protected internal override void PrepareContainerForItemOverride(Control container, object? item, int index)
+        {
+            EnsureTreeView().PrepareContainerForItemOverride(container, item, index);
+        }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
         protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
         protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
@@ -283,6 +295,9 @@ namespace Avalonia.Controls
             return logical != null ? result : @default;
             return logical != null ? result : @default;
         }
         }
 
 
+        private TreeView EnsureTreeView() => _treeView ??
+            throw new InvalidOperationException("The TreeViewItem is not part of a TreeView.");
+
         private void HeaderDoubleTapped(object? sender, TappedEventArgs e)
         private void HeaderDoubleTapped(object? sender, TappedEventArgs e)
         {
         {
             OnHeaderDoubleTapped(e);
             OnHeaderDoubleTapped(e);

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -58,7 +58,6 @@
         <Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
         <Compile Include="XamlIl\Runtime\IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs" />
         <Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />
         <Compile Include="XamlIl\Runtime\XamlIlRuntimeHelpers.cs" />
         <Compile Include="XamlLoadException.cs" />
         <Compile Include="XamlLoadException.cs" />
-        <Compile Include="..\Avalonia.Markup\Markup\Parsers\BindingExpressionGrammar.cs" />
         <Compile Include="XamlTypes.cs" />
         <Compile Include="XamlTypes.cs" />
     </ItemGroup>
     </ItemGroup>
   <ItemGroup>
   <ItemGroup>
@@ -69,6 +68,7 @@
   <Import Project="..\..\..\build\ApiDiff.props" />
   <Import Project="..\..\..\build\ApiDiff.props" />
   <Import Project="..\..\..\build\DevAnalyzers.props" />
   <Import Project="..\..\..\build\DevAnalyzers.props" />
   <Import Project="..\..\..\build\TrimmingEnable.props" />
   <Import Project="..\..\..\build\TrimmingEnable.props" />
+  <Import Project="..\..\..\build\NullableEnable.props" />
 
 
   <ItemGroup Label="InternalsVisibleTo">
   <ItemGroup Label="InternalsVisibleTo">
     <InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Markup.Xaml.UnitTests, PublicKey=$(AvaloniaPublicKey)" />

+ 2 - 3
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@@ -1,8 +1,7 @@
 using System;
 using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.IO;
 using Avalonia.Platform;
 using Avalonia.Platform;
-#nullable enable
+
 namespace Avalonia.Markup.Xaml
 namespace Avalonia.Markup.Xaml
 {
 {
     /// <summary>
     /// <summary>
@@ -79,7 +78,7 @@ namespace Avalonia.Markup.Xaml
 
 
             var compiledLoader = assetLocator.GetAssembly(uri, baseUri)
             var compiledLoader = assetLocator.GetAssembly(uri, baseUri)
                 ?.GetType("CompiledAvaloniaXaml.!XamlLoader")
                 ?.GetType("CompiledAvaloniaXaml.!XamlLoader")
-                ?.GetMethod("TryLoad", new[] { typeof(System.IServiceProvider), typeof(string) });
+                ?.GetMethod("TryLoad", new[] { typeof(IServiceProvider), typeof(string) });
             if (compiledLoader != null)
             if (compiledLoader != null)
             {
             {
                 var compiledResult = compiledLoader.Invoke(null, new object?[] { sp, absoluteUri.ToString()});
                 var compiledResult = compiledLoader.Invoke(null, new object?[] { sp, absoluteUri.ToString()});

+ 8 - 6
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@@ -14,18 +14,20 @@ namespace Avalonia.Markup.Xaml.Converters
     [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
     [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
     public class AvaloniaPropertyTypeConverter : TypeConverter
     public class AvaloniaPropertyTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        /// <inheritdoc />
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var registry = AvaloniaPropertyRegistry.Instance;
             var registry = AvaloniaPropertyRegistry.Instance;
             var (ns, owner, propertyName) = PropertyParser.Parse(new CharacterReader(((string)value).AsSpan()));
             var (ns, owner, propertyName) = PropertyParser.Parse(new CharacterReader(((string)value).AsSpan()));
             var ownerType = TryResolveOwnerByName(context, ns, owner);
             var ownerType = TryResolveOwnerByName(context, ns, owner);
-            var targetType = context.GetFirstParent<ControlTemplate>()?.TargetType ??
-                context.GetFirstParent<Style>()?.Selector?.TargetType ??
+            var targetType = context?.GetFirstParent<ControlTemplate>()?.TargetType ??
+                context?.GetFirstParent<Style>()?.Selector?.TargetType ??
                 typeof(Control);
                 typeof(Control);
             var effectiveOwner = ownerType ?? targetType;
             var effectiveOwner = ownerType ?? targetType;
             var property = registry.FindRegistered(effectiveOwner, propertyName);
             var property = registry.FindRegistered(effectiveOwner, propertyName);
@@ -51,11 +53,11 @@ namespace Avalonia.Markup.Xaml.Converters
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
-        private static Type TryResolveOwnerByName(ITypeDescriptorContext context, string ns, string owner)
+        private static Type? TryResolveOwnerByName(ITypeDescriptorContext? context, string? ns, string? owner)
         {
         {
             if (owner != null)
             if (owner != null)
             {
             {
-                var result = context.ResolveType(ns, owner);
+                var result = context?.ResolveType(ns, owner);
 
 
                 if (result == null)
                 if (result == null)
                 {
                 {

+ 4 - 2
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs

@@ -6,12 +6,14 @@ namespace Avalonia.Markup.Xaml.Converters
 {
 {
     public class AvaloniaUriTypeConverter : TypeConverter
     public class AvaloniaUriTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        /// <inheritdoc />
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var s = value as string;
             var s = value as string;
             if (s == null)
             if (s == null)

+ 6 - 5
src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs

@@ -1,20 +1,21 @@
 using System;
 using System;
+using System.ComponentModel;
 using System.Globalization;
 using System.Globalization;
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Platform;
 
 
 namespace Avalonia.Markup.Xaml.Converters
 namespace Avalonia.Markup.Xaml.Converters
 {
 {
-    using System.ComponentModel;
-
     public class BitmapTypeConverter : TypeConverter
     public class BitmapTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        /// <inheritdoc />
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var s = (string)value;
             var s = (string)value;
             var uri = s.StartsWith("/")
             var uri = s.StartsWith("/")
@@ -25,7 +26,7 @@ namespace Avalonia.Markup.Xaml.Converters
                 return new Bitmap(uri.LocalPath);
                 return new Bitmap(uri.LocalPath);
 
 
             var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
             var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
-            return new Bitmap(assets.Open(uri, context.GetContextBaseUri()));
+            return new Bitmap(assets.Open(uri, context?.GetContextBaseUri()));
         }
         }
     }
     }
 }
 }

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml/Converters/ColorToBrushConverter.cs

@@ -23,7 +23,7 @@ namespace Avalonia.Markup.Xaml.Converters
         /// If <paramref name="value"/> is a <see cref="Color"/> and <paramref name="targetType"/>
         /// If <paramref name="value"/> is a <see cref="Color"/> and <paramref name="targetType"/>
         /// is <see cref="IBrush"/> then converts the color to a solid color brush.
         /// is <see cref="IBrush"/> then converts the color to a solid color brush.
         /// </returns>
         /// </returns>
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
         {
             return Convert(value, targetType);
             return Convert(value, targetType);
         }
         }
@@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml.Converters
         /// If <paramref name="value"/> is an <see cref="ISolidColorBrush"/> and <paramref name="targetType"/>
         /// If <paramref name="value"/> is an <see cref="ISolidColorBrush"/> and <paramref name="targetType"/>
         /// is <see cref="Color"/> then converts the solid color brush to a color.
         /// is <see cref="Color"/> then converts the solid color brush to a color.
         /// </returns>
         /// </returns>
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
         {
             return ConvertBack(value, targetType);
             return ConvertBack(value, targetType);
         }
         }
@@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.Converters
         /// If <paramref name="value"/> is a <see cref="Color"/> and <paramref name="targetType"/>
         /// If <paramref name="value"/> is a <see cref="Color"/> and <paramref name="targetType"/>
         /// is <see cref="IBrush"/> then converts the color to a solid color brush.
         /// is <see cref="IBrush"/> then converts the color to a solid color brush.
         /// </returns>
         /// </returns>
-        public static object Convert(object value, Type targetType)
+        public static object? Convert(object? value, Type? targetType)
         {
         {
             if (targetType == typeof(IBrush) && value is Color c)
             if (targetType == typeof(IBrush) && value is Color c)
             {
             {
@@ -75,7 +75,7 @@ namespace Avalonia.Markup.Xaml.Converters
         /// If <paramref name="value"/> is an <see cref="ISolidColorBrush"/> and <paramref name="targetType"/>
         /// If <paramref name="value"/> is an <see cref="ISolidColorBrush"/> and <paramref name="targetType"/>
         /// is <see cref="Color"/> then converts the solid color brush to a color.
         /// is <see cref="Color"/> then converts the solid color brush to a color.
         /// </returns>
         /// </returns>
-        public static object ConvertBack(object value, Type targetType)
+        public static object? ConvertBack(object? value, Type? targetType)
         {
         {
             if (targetType == typeof(Color) && value is ISolidColorBrush brush)
             if (targetType == typeof(Color) && value is ISolidColorBrush brush)
             {
             {

+ 5 - 5
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

@@ -1,24 +1,24 @@
 using System;
 using System;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Globalization;
 using System.Globalization;
-
 using Avalonia.Media;
 using Avalonia.Media;
 
 
-
 namespace Avalonia.Markup.Xaml.Converters
 namespace Avalonia.Markup.Xaml.Converters
 {
 {
     public class FontFamilyTypeConverter : TypeConverter
     public class FontFamilyTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        /// <inheritdoc />
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var s = (string)value;
             var s = (string)value;
 
 
-            return FontFamily.Parse(s, context.GetContextBaseUri());
+            return FontFamily.Parse(s, context?.GetContextBaseUri());
         }
         }
     }
     }
 }
 }

+ 7 - 6
src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs

@@ -2,20 +2,21 @@
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
 using Avalonia.Platform;
 using Avalonia.Platform;
 using System;
 using System;
+using System.ComponentModel;
 using System.Globalization;
 using System.Globalization;
 
 
 namespace Avalonia.Markup.Xaml.Converters
 namespace Avalonia.Markup.Xaml.Converters
 {
 {
-	using System.ComponentModel;
-
     public class IconTypeConverter : TypeConverter
     public class IconTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        /// <inheritdoc />
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var path = value as string;
             var path = value as string;
             if (path != null)
             if (path != null)
@@ -32,7 +33,7 @@ namespace Avalonia.Markup.Xaml.Converters
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        private static WindowIcon CreateIconFromPath(ITypeDescriptorContext context, string s)
+        private static WindowIcon CreateIconFromPath(ITypeDescriptorContext? context, string s)
         {
         {
             var uri = s.StartsWith("/")
             var uri = s.StartsWith("/")
                 ? new Uri(s, UriKind.Relative)
                 ? new Uri(s, UriKind.Relative)
@@ -41,7 +42,7 @@ namespace Avalonia.Markup.Xaml.Converters
             if(uri.IsAbsoluteUri && uri.IsFile)
             if(uri.IsAbsoluteUri && uri.IsFile)
                 return new WindowIcon(uri.LocalPath);
                 return new WindowIcon(uri.LocalPath);
             var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
             var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
-            return new WindowIcon(assets.Open(uri, context.GetContextBaseUri()));
+            return new WindowIcon(assets.Open(uri, context?.GetContextBaseUri()));
         }
         }
     }
     }
 }
 }

+ 4 - 5
src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs

@@ -1,20 +1,19 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Globalization;
 using System.Globalization;
+using Avalonia.Utilities;
 
 
 namespace Avalonia.Markup.Xaml.Converters
 namespace Avalonia.Markup.Xaml.Converters
 {
 {
-    using System.ComponentModel;
-    using Avalonia.Utilities;
-
     public class PointsListTypeConverter : TypeConverter
     public class PointsListTypeConverter : TypeConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+        public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
         {
         {
             return sourceType == typeof(string);
             return sourceType == typeof(string);
         }
         }
 
 
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        public override object ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var points = new List<Point>();
             var points = new List<Point>();
 
 

+ 4 - 9
src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs

@@ -1,18 +1,13 @@
 using System;
 using System;
+using System.ComponentModel;
 using System.Globalization;
 using System.Globalization;
 
 
 namespace Avalonia.Markup.Xaml.Converters
 namespace Avalonia.Markup.Xaml.Converters
 {
 {
-	using System.ComponentModel;
-
-    public class TimeSpanTypeConverter : System.ComponentModel.TimeSpanConverter
+    public class TimeSpanTypeConverter : TimeSpanConverter
     {
     {
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            return base.CanConvertFrom(context, sourceType);
-        }
-
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+        /// <inheritdoc />
+        public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
         {
         {
             var valueStr = (string)value;
             var valueStr = (string)value;
             if (!valueStr.Contains(':'))
             if (!valueStr.Contains(':'))

+ 16 - 17
src/Markup/Avalonia.Markup.Xaml/Extensions.cs

@@ -1,6 +1,5 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Controls;
@@ -11,38 +10,38 @@ namespace Avalonia.Markup.Xaml
 {
 {
     internal static class Extensions
     internal static class Extensions
     {
     {
-        public static T GetService<T>(this IServiceProvider sp) => (T)sp?.GetService(typeof(T));
-        
-        
-        public static Uri GetContextBaseUri(this IServiceProvider ctx) => ctx.GetService<IUriContext>().BaseUri;
+        public static T? GetService<T>(this IServiceProvider sp) => (T?)sp.GetService(typeof(T));
+
+        public static T GetRequiredService<T>(this IServiceProvider sp)
+            => sp.GetService<T>() ?? throw new InvalidOperationException($"Service {typeof(T)} hasn't been registered");
 
 
-        public static T GetFirstParent<T>(this IServiceProvider ctx) where T : class 
-            => ctx.GetService<IAvaloniaXamlIlParentStackProvider>().Parents.OfType<T>().FirstOrDefault();
+        public static Uri? GetContextBaseUri(this IServiceProvider ctx) => ctx.GetService<IUriContext>()?.BaseUri;
 
 
-        public static T GetLastParent<T>(this IServiceProvider ctx) where T : class 
-            => ctx.GetService<IAvaloniaXamlIlParentStackProvider>().Parents.OfType<T>().LastOrDefault();
+        public static T? GetFirstParent<T>(this IServiceProvider ctx) where T : class
+            => ctx.GetService<IAvaloniaXamlIlParentStackProvider>()?.Parents.OfType<T>().FirstOrDefault();
+
+        public static T? GetLastParent<T>(this IServiceProvider ctx) where T : class
+            => ctx.GetService<IAvaloniaXamlIlParentStackProvider>()?.Parents.OfType<T>().LastOrDefault();
 
 
         public static IEnumerable<T> GetParents<T>(this IServiceProvider sp)
         public static IEnumerable<T> GetParents<T>(this IServiceProvider sp)
-        {
-            return sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents.OfType<T>();
-        }
+            => sp.GetService<IAvaloniaXamlIlParentStackProvider>()?.Parents.OfType<T>() ?? Enumerable.Empty<T>();
 
 
         public static bool IsInControlTemplate(this IServiceProvider sp) => sp.GetService<IAvaloniaXamlIlControlTemplateProvider>() != null;
         public static bool IsInControlTemplate(this IServiceProvider sp) => sp.GetService<IAvaloniaXamlIlControlTemplateProvider>() != null;
 
 
         [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.XamlTypeResolvedRequiresUnreferenceCodeMessage)]
-        public static Type ResolveType(this IServiceProvider ctx, string namespacePrefix, string type)
+        public static Type ResolveType(this IServiceProvider ctx, string? namespacePrefix, string type)
         {
         {
-            var tr = ctx.GetService<IXamlTypeResolver>();
+            var tr = ctx.GetRequiredService<IXamlTypeResolver>();
             string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}";
             string name = string.IsNullOrEmpty(namespacePrefix) ? type : $"{namespacePrefix}:{type}";
-            return tr?.Resolve(name);
+            return tr.Resolve(name);
         }
         }
         
         
-        public static object GetDefaultAnchor(this IServiceProvider provider)
+        public static object? GetDefaultAnchor(this IServiceProvider provider)
         {
         {
             // If the target is not a control, so we need to find an anchor that will let us look
             // If the target is not a control, so we need to find an anchor that will let us look
             // up named controls and style resources. First look for the closest Control in
             // up named controls and style resources. First look for the closest Control in
             // the context.
             // the context.
-            object anchor = provider.GetFirstParent<Control>();
+            object? anchor = provider.GetFirstParent<Control>();
 
 
             if (anchor is null)
             if (anchor is null)
             {
             {

+ 8 - 4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

@@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             };
             };
         }
         }
 
 
-        private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation)
+        private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation)
         {
         {
             if (Source != null)
             if (Source != null)
             {
             {
@@ -61,8 +61,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             }
             }
             else
             else
             {
             {
+                var styledElement = target as StyledElement
+                    ?? anchor as StyledElement
+                    ?? throw new ArgumentException($"Cannot find a valid {nameof(StyledElement)} to use as the binding source.");
+
                 return CreateSourceObserver(
                 return CreateSourceObserver(
-                    (target as StyledElement) ?? (anchor as StyledElement),
+                    styledElement,
                     Path.BuildExpression(enableDataValidation));
                     Path.BuildExpression(enableDataValidation));
             }
             }
         }
         }
@@ -70,8 +74,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
         [ConstructorArgument("path")]
         [ConstructorArgument("path")]
         public CompiledBindingPath Path { get; set; }
         public CompiledBindingPath Path { get; set; }
 
 
-        public object Source { get; set; }
+        public object? Source { get; set; }
 
 
-        public Type DataType { get; set; }
+        public Type? DataType { get; set; }
     }
     }
 }
 }

+ 4 - 6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs

@@ -1,13 +1,11 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Text;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Data.Core.Plugins;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
-    class ArrayElementPlugin : IPropertyAccessorPlugin
+    internal class ArrayElementPlugin : IPropertyAccessorPlugin
     {
     {
         private readonly int[] _indices;
         private readonly int[] _indices;
         private readonly Type _elementType;
         private readonly Type _elementType;
@@ -25,7 +23,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
-        public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
+        public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
         {
         {
             if (reference.TryGetTarget(out var target) && target is Array arr)
             if (reference.TryGetTarget(out var target) && target is Array arr)
             {
             {
@@ -48,9 +46,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
             public override Type PropertyType { get; }
             public override Type PropertyType { get; }
 
 
-            public override object Value => _reference.TryGetTarget(out var arr) ? arr.GetValue(_indices) : null;
+            public override object? Value => _reference.TryGetTarget(out var arr) ? arr.GetValue(_indices) : null;
 
 
-            public override bool SetValue(object value, BindingPriority priority)
+            public override bool SetValue(object? value, BindingPriority priority)
             {
             {
                 if (_reference.TryGetTarget(out var arr))
                 if (_reference.TryGetTarget(out var arr))
                 {
                 {

+ 17 - 19
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs

@@ -2,8 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Text;
 using System.Windows.Input;
 using System.Windows.Input;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Data.Core.Plugins;
@@ -13,11 +11,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
     internal class CommandAccessorPlugin : IPropertyAccessorPlugin
     internal class CommandAccessorPlugin : IPropertyAccessorPlugin
     {
     {
-        private readonly Action<object, object> _execute;
-        private readonly Func<object, object, bool> _canExecute;
+        private readonly Action<object, object?> _execute;
+        private readonly Func<object, object?, bool>? _canExecute;
         private readonly ISet<string> _dependsOnProperties;
         private readonly ISet<string> _dependsOnProperties;
 
 
-        public CommandAccessorPlugin(Action<object, object> execute, Func<object, object, bool> canExecute, ISet<string> dependsOnProperties)
+        public CommandAccessorPlugin(Action<object, object?> execute, Func<object, object?, bool>? canExecute, ISet<string> dependsOnProperties)
         {
         {
             _execute = execute;
             _execute = execute;
             _canExecute = canExecute;
             _canExecute = canExecute;
@@ -31,18 +29,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
-        public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
+        public IPropertyAccessor Start(WeakReference<object?> reference, string propertyName)
         {
         {
             return new CommandAccessor(reference, _execute, _canExecute, _dependsOnProperties);
             return new CommandAccessor(reference, _execute, _canExecute, _dependsOnProperties);
         }
         }
 
 
         private sealed class CommandAccessor : PropertyAccessorBase
         private sealed class CommandAccessor : PropertyAccessorBase
         {
         {
-            private readonly WeakReference<object> _reference;
-            private Command _command;
+            private readonly WeakReference<object?> _reference;
+            private readonly Command _command;
             private readonly ISet<string> _dependsOnProperties;
             private readonly ISet<string> _dependsOnProperties;
 
 
-            public CommandAccessor(WeakReference<object> reference, Action<object, object> execute, Func<object, object, bool> canExecute, ISet<string> dependsOnProperties)
+            public CommandAccessor(WeakReference<object?> reference, Action<object, object?> execute, Func<object, object?, bool>? canExecute, ISet<string> dependsOnProperties)
             {
             {
                 _reference = reference ?? throw new ArgumentNullException(nameof(reference));
                 _reference = reference ?? throw new ArgumentNullException(nameof(reference));
                 _dependsOnProperties = dependsOnProperties;
                 _dependsOnProperties = dependsOnProperties;
@@ -50,7 +48,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
             }
             }
 
 
-            public override object Value => _reference.TryGetTarget(out var _) ? _command : null;
+            public override object? Value => _reference.TryGetTarget(out _) ? _command : null;
 
 
             private void RaiseCanExecuteChanged()
             private void RaiseCanExecuteChanged()
             {
             {
@@ -59,13 +57,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
             private sealed class Command : ICommand
             private sealed class Command : ICommand
             {
             {
-                private readonly WeakReference<object> _target;
-                private readonly Action<object, object> _execute;
-                private readonly Func<object, object, bool> _canExecute;
+                private readonly WeakReference<object?> _target;
+                private readonly Action<object, object?> _execute;
+                private readonly Func<object, object?, bool>? _canExecute;
 
 
-                public event EventHandler CanExecuteChanged;
+                public event EventHandler? CanExecuteChanged;
 
 
-                public Command(WeakReference<object> target, Action<object, object> execute, Func<object, object, bool> canExecute)
+                public Command(WeakReference<object?> target, Action<object, object?> execute, Func<object, object?, bool>? canExecute)
                 {
                 {
                     _target = target;
                     _target = target;
                     _execute = execute;
                     _execute = execute;
@@ -78,7 +76,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                        , Threading.DispatcherPriority.Input);
                        , Threading.DispatcherPriority.Input);
                 }
                 }
 
 
-                public bool CanExecute(object parameter)
+                public bool CanExecute(object? parameter)
                 {
                 {
                     if (_target.TryGetTarget(out var target))
                     if (_target.TryGetTarget(out var target))
                     {
                     {
@@ -91,7 +89,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                     return false;
                     return false;
                 }
                 }
 
 
-                public void Execute(object parameter)
+                public void Execute(object? parameter)
                 {
                 {
                     if (_target.TryGetTarget(out var target))
                     if (_target.TryGetTarget(out var target))
                     {
                     {
@@ -102,12 +100,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
             public override Type PropertyType => typeof(ICommand);
             public override Type PropertyType => typeof(ICommand);
 
 
-            public override bool SetValue(object value, BindingPriority priority)
+            public override bool SetValue(object? value, BindingPriority priority)
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            void OnNotifyPropertyChanged(object sender, PropertyChangedEventArgs e)
+            private void OnNotifyPropertyChanged(object? sender, PropertyChangedEventArgs e)
             {
             {
                 if (string.IsNullOrEmpty(e.PropertyName) || _dependsOnProperties.Contains(e.PropertyName))
                 if (string.IsNullOrEmpty(e.PropertyName) || _dependsOnProperties.Contains(e.PropertyName))
                 {
                 {

+ 28 - 26
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@@ -17,7 +17,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         public CompiledBindingPath()
         public CompiledBindingPath()
             => _elements = Array.Empty<ICompiledBindingPathElement>();
             => _elements = Array.Empty<ICompiledBindingPathElement>();
 
 
-        internal CompiledBindingPath(ICompiledBindingPathElement[] elements, object rawSource)
+        internal CompiledBindingPath(ICompiledBindingPathElement[] elements, object? rawSource)
         {
         {
             _elements = elements;
             _elements = elements;
             RawSource = rawSource;
             RawSource = rawSource;
@@ -26,14 +26,14 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.CompiledBindingSafeSupressWarningMessage)]
         [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.CompiledBindingSafeSupressWarningMessage)]
         internal ExpressionNode BuildExpression(bool enableValidation)
         internal ExpressionNode BuildExpression(bool enableValidation)
         {
         {
-            ExpressionNode pathRoot = null;
-            ExpressionNode path = null;
+            ExpressionNode? pathRoot = null;
+            ExpressionNode? path = null;
             foreach (var element in _elements)
             foreach (var element in _elements)
             {
             {
-                ExpressionNode node = null;
+                ExpressionNode? node;
                 switch (element)
                 switch (element)
                 {
                 {
-                    case NotExpressionPathElement _:
+                    case NotExpressionPathElement:
                         node = new LogicalNotNode();
                         node = new LogicalNotNode();
                         break;
                         break;
                     case PropertyElement prop:
                     case PropertyElement prop:
@@ -54,7 +54,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                     case AncestorPathElement ancestor:
                     case AncestorPathElement ancestor:
                         node = new FindAncestorNode(ancestor.AncestorType, ancestor.Level);
                         node = new FindAncestorNode(ancestor.AncestorType, ancestor.Level);
                         break;
                         break;
-                    case SelfPathElement _:
+                    case SelfPathElement:
                         node = new SelfNode();
                         node = new SelfNode();
                         break;
                         break;
                     case ElementNameElement name:
                     case ElementNameElement name:
@@ -70,7 +70,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                         throw new InvalidOperationException($"Unknown binding path element type {element.GetType().FullName}");
                         throw new InvalidOperationException($"Unknown binding path element type {element.GetType().FullName}");
                 }
                 }
 
 
-                path = pathRoot is null ? (pathRoot = node) : path.Next = node;
+                path = pathRoot is null ? (pathRoot = node) : path!.Next = node;
             }
             }
 
 
             return pathRoot ?? new EmptyExpressionNode();
             return pathRoot ?? new EmptyExpressionNode();
@@ -81,16 +81,17 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         internal SourceMode SourceMode => Array.Exists(_elements, e => e is IControlSourceBindingPathElement)
         internal SourceMode SourceMode => Array.Exists(_elements, e => e is IControlSourceBindingPathElement)
             ? SourceMode.Control : SourceMode.Data;
             ? SourceMode.Control : SourceMode.Data;
 
 
-        internal object RawSource { get; }
+        internal object? RawSource { get; }
 
 
+        /// <inheritdoc />
         public override string ToString()
         public override string ToString()
             => string.Concat((IEnumerable<ICompiledBindingPathElement>) _elements);
             => string.Concat((IEnumerable<ICompiledBindingPathElement>) _elements);
     }
     }
 
 
     public class CompiledBindingPathBuilder
     public class CompiledBindingPathBuilder
     {
     {
-        private object _rawSource;
-        private List<ICompiledBindingPathElement> _elements = new List<ICompiledBindingPathElement>();
+        private object? _rawSource;
+        private readonly List<ICompiledBindingPathElement> _elements = new();
 
 
         public CompiledBindingPathBuilder Not()
         public CompiledBindingPathBuilder Not()
         {
         {
@@ -98,7 +99,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             return this;
             return this;
         }
         }
 
 
-        public CompiledBindingPathBuilder Property(IPropertyInfo info, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory)
+        public CompiledBindingPathBuilder Property(IPropertyInfo info, Func<WeakReference<object?>, IPropertyInfo, IPropertyAccessor> accessorFactory)
         {
         {
             _elements.Add(new PropertyElement(info, accessorFactory, _elements.Count == 0));
             _elements.Add(new PropertyElement(info, accessorFactory, _elements.Count == 0));
             return this;
             return this;
@@ -110,7 +111,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             return this;
             return this;
         }
         }
 
 
-        public CompiledBindingPathBuilder Command(string methodName, Action<object, object> executeHelper, Func<object, object, bool> canExecuteHelper, string[] dependsOnProperties)
+        public CompiledBindingPathBuilder Command(string methodName, Action<object, object?> executeHelper, Func<object, object?, bool>? canExecuteHelper, string[]? dependsOnProperties)
         {
         {
             _elements.Add(new MethodAsCommandElement(methodName, executeHelper, canExecuteHelper, dependsOnProperties ?? Array.Empty<string>()));
             _elements.Add(new MethodAsCommandElement(methodName, executeHelper, canExecuteHelper, dependsOnProperties ?? Array.Empty<string>()));
             return this;
             return this;
@@ -163,7 +164,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             return this;
             return this;
         }
         }
 
 
-        public CompiledBindingPathBuilder SetRawSource(object rawSource)
+        public CompiledBindingPathBuilder SetRawSource(object? rawSource)
         {
         {
             _rawSource = rawSource;
             _rawSource = rawSource;
             return this;
             return this;
@@ -187,7 +188,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
     {
     {
         private readonly bool _isFirstElement;
         private readonly bool _isFirstElement;
 
 
-        public PropertyElement(IPropertyInfo property, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory, bool isFirstElement)
+        public PropertyElement(IPropertyInfo property, Func<WeakReference<object?>, IPropertyInfo, IPropertyAccessor> accessorFactory, bool isFirstElement)
         {
         {
             Property = property;
             Property = property;
             AccessorFactory = accessorFactory;
             AccessorFactory = accessorFactory;
@@ -196,7 +197,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
         public IPropertyInfo Property { get; }
         public IPropertyInfo Property { get; }
 
 
-        public Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; }
+        public Func<WeakReference<object?>, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; }
 
 
         public override string ToString()
         public override string ToString()
             => _isFirstElement ? Property.Name : $".{Property.Name}";
             => _isFirstElement ? Property.Name : $".{Property.Name}";
@@ -206,7 +207,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
     {
     {
         public MethodAsDelegateElement(RuntimeMethodHandle method, RuntimeTypeHandle delegateType)
         public MethodAsDelegateElement(RuntimeMethodHandle method, RuntimeTypeHandle delegateType)
         {
         {
-            Method = (MethodInfo)MethodBase.GetMethodFromHandle(method);
+            Method = MethodBase.GetMethodFromHandle(method) as MethodInfo
+                ?? throw new ArgumentException("Invalid method handle", nameof(method));
             DelegateType = Type.GetTypeFromHandle(delegateType);
             DelegateType = Type.GetTypeFromHandle(delegateType);
         }
         }
 
 
@@ -217,7 +219,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class MethodAsCommandElement : ICompiledBindingPathElement
     internal class MethodAsCommandElement : ICompiledBindingPathElement
     {
     {
-        public MethodAsCommandElement(string methodName, Action<object, object> executeHelper, Func<object, object, bool> canExecuteHelper, string[] dependsOnElements)
+        public MethodAsCommandElement(string methodName, Action<object, object?> executeHelper, Func<object, object?, bool>? canExecuteHelper, string[] dependsOnElements)
         {
         {
             MethodName = methodName;
             MethodName = methodName;
             ExecuteMethod = executeHelper;
             ExecuteMethod = executeHelper;
@@ -226,8 +228,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
 
 
         public string MethodName { get; }
         public string MethodName { get; }
-        public Action<object, object> ExecuteMethod { get; }
-        public Func<object, object, bool> CanExecuteMethod { get; }
+        public Action<object, object?> ExecuteMethod { get; }
+        public Func<object, object?, bool>? CanExecuteMethod { get; }
         public HashSet<string> DependsOnProperties { get; }
         public HashSet<string> DependsOnProperties { get; }
     }
     }
 
 
@@ -240,7 +242,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
     {
     {
         Type Type { get; }
         Type Type { get; }
 
 
-        Func<object, object> Cast { get; }
+        Func<object?, object?> Cast { get; }
     }
     }
 
 
     internal class TaskStreamPathElement<T> : IStronglyTypedStreamElement
     internal class TaskStreamPathElement<T> : IStronglyTypedStreamElement
@@ -267,13 +269,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class AncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
     internal class AncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
     {
     {
-        public AncestorPathElement(Type ancestorType, int level)
+        public AncestorPathElement(Type? ancestorType, int level)
         {
         {
             AncestorType = ancestorType;
             AncestorType = ancestorType;
             Level = level;
             Level = level;
         }
         }
 
 
-        public Type AncestorType { get; }
+        public Type? AncestorType { get; }
         public int Level { get; }
         public int Level { get; }
 
 
         public override string ToString()
         public override string ToString()
@@ -282,13 +284,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
     internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement
     {
     {
-        public VisualAncestorPathElement(Type ancestorType, int level)
+        public VisualAncestorPathElement(Type? ancestorType, int level)
         {
         {
             AncestorType = ancestorType;
             AncestorType = ancestorType;
             Level = level;
             Level = level;
         }
         }
 
 
-        public Type AncestorType { get; }
+        public Type? AncestorType { get; }
         public int Level { get; }
         public int Level { get; }
     }
     }
 
 
@@ -323,7 +325,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class TypeCastPathElement<T> : ITypeCastElement
     internal class TypeCastPathElement<T> : ITypeCastElement
     {
     {
-        private static object TryCast(object obj)
+        private static object? TryCast(object? obj)
         {
         {
             if (obj is T result)
             if (obj is T result)
                 return result;
                 return result;
@@ -332,7 +334,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
         public Type Type => typeof(T);
         public Type Type => typeof(T);
 
 
-        public Func<object, object> Cast => TryCast;
+        public Func<object?, object?> Cast => TryCast;
 
 
         public override string ToString()
         public override string ToString()
             => $"({Type.FullName})";
             => $"({Type.FullName})";

+ 6 - 6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs

@@ -5,13 +5,13 @@ using Avalonia.Reactive;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
-    class FindVisualAncestorNode : ExpressionNode
+    internal class FindVisualAncestorNode : ExpressionNode
     {
     {
         private readonly int _level;
         private readonly int _level;
-        private readonly Type _ancestorType;
-        private IDisposable _subscription;
+        private readonly Type? _ancestorType;
+        private IDisposable? _subscription;
 
 
-        public FindVisualAncestorNode(Type ancestorType, int level)
+        public FindVisualAncestorNode(Type? ancestorType, int level)
         {
         {
             _level = level;
             _level = level;
             _ancestorType = ancestorType;
             _ancestorType = ancestorType;
@@ -32,9 +32,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             }
             }
         }
         }
 
 
-        protected override void StartListeningCore(WeakReference<object> reference)
+        protected override void StartListeningCore(WeakReference<object?> reference)
         {
         {
-            if (reference.TryGetTarget(out object target) && target is Visual visual)
+            if (reference.TryGetTarget(out object? target) && target is Visual visual)
             {
             {
                 _subscription = VisualLocator.Track(visual, _level, _ancestorType).Subscribe(ValueChanged);
                 _subscription = VisualLocator.Track(visual, _level, _ancestorType).Subscribe(ValueChanged);
             }
             }

+ 1 - 5
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs

@@ -1,19 +1,15 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Reflection;
-using System.Text;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Data.Core.Plugins;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
     internal class MethodAccessorPlugin : IPropertyAccessorPlugin
     internal class MethodAccessorPlugin : IPropertyAccessorPlugin
     {
     {
-        private MethodInfo _method;
+        private readonly MethodInfo _method;
         private readonly Type _delegateType;
         private readonly Type _delegateType;
 
 
         public MethodAccessorPlugin(MethodInfo method, Type delegateType)
         public MethodAccessorPlugin(MethodInfo method, Type delegateType)

+ 6 - 8
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ObservableStreamPlugin.cs

@@ -1,33 +1,31 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Text;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Reactive;
 using Avalonia.Reactive;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
-    class ObservableStreamPlugin<T> : IStreamPlugin
+    internal class ObservableStreamPlugin<T> : IStreamPlugin
     {
     {
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
-        public bool Match(WeakReference<object> reference)
+        public bool Match(WeakReference<object?> reference)
         {
         {
             return reference.TryGetTarget(out var target) && target is IObservable<T>;
             return reference.TryGetTarget(out var target) && target is IObservable<T>;
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
-        public IObservable<object> Start(WeakReference<object> reference)
+        public IObservable<object?> Start(WeakReference<object?> reference)
         {
         {
             if (!(reference.TryGetTarget(out var target) && target is IObservable<T> obs))
             if (!(reference.TryGetTarget(out var target) && target is IObservable<T> obs))
             {
             {
-                return Observable.Empty<object>();
+                return Observable.Empty<object?>();
             }
             }
-            else if (target is IObservable<object> obj)
+            else if (target is IObservable<object?> obj)
             {
             {
                 return obj;
                 return obj;
             }
             }
 
 
-            return obs.Select(x => (object)x);
+            return obs.Select(x => (object?)x);
         }
         }
     }
     }
 }
 }

+ 26 - 26
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs

@@ -11,29 +11,29 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
     public static class PropertyInfoAccessorFactory
     public static class PropertyInfoAccessorFactory
     {
     {
-        public static IPropertyAccessor CreateInpcPropertyAccessor(WeakReference<object> target, IPropertyInfo property)
+        public static IPropertyAccessor CreateInpcPropertyAccessor(WeakReference<object?> target, IPropertyInfo property)
             => new InpcPropertyAccessor(target, property);
             => new InpcPropertyAccessor(target, property);
 
 
-        public static IPropertyAccessor CreateAvaloniaPropertyAccessor(WeakReference<object> target, IPropertyInfo property)
-            => new AvaloniaPropertyAccessor(new WeakReference<AvaloniaObject>((AvaloniaObject)(target.TryGetTarget(out var o) ? o : null)), (AvaloniaProperty)property);
+        public static IPropertyAccessor CreateAvaloniaPropertyAccessor(WeakReference<object?> target, IPropertyInfo property)
+            => new AvaloniaPropertyAccessor(new WeakReference<AvaloniaObject?>((AvaloniaObject?)(target.TryGetTarget(out var o) ? o : null)), (AvaloniaProperty)property);
 
 
-        public static IPropertyAccessor CreateIndexerPropertyAccessor(WeakReference<object> target, IPropertyInfo property, int argument)
+        public static IPropertyAccessor CreateIndexerPropertyAccessor(WeakReference<object?> target, IPropertyInfo property, int argument)
             => new IndexerAccessor(target, property, argument);
             => new IndexerAccessor(target, property, argument);
     }
     }
 
 
     internal class AvaloniaPropertyAccessor : PropertyAccessorBase
     internal class AvaloniaPropertyAccessor : PropertyAccessorBase
     {
     {
-        private readonly WeakReference<AvaloniaObject> _reference;
+        private readonly WeakReference<AvaloniaObject?> _reference;
         private readonly AvaloniaProperty _property;
         private readonly AvaloniaProperty _property;
-        private IDisposable _subscription;
+        private IDisposable? _subscription;
 
 
-        public AvaloniaPropertyAccessor(WeakReference<AvaloniaObject> reference, AvaloniaProperty property)
+        public AvaloniaPropertyAccessor(WeakReference<AvaloniaObject?> reference, AvaloniaProperty property)
         {
         {
-            _reference = reference ?? throw new ArgumentNullException(nameof(reference));;
-            _property = property ?? throw new ArgumentNullException(nameof(property));;
+            _reference = reference ?? throw new ArgumentNullException(nameof(reference));
+            _property = property ?? throw new ArgumentNullException(nameof(property));
         }
         }
 
 
-        public AvaloniaObject Instance
+        public AvaloniaObject? Instance
         {
         {
             get
             get
             {
             {
@@ -43,13 +43,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
 
 
         public override Type PropertyType => _property.PropertyType;
         public override Type PropertyType => _property.PropertyType;
-        public override object Value => Instance?.GetValue(_property);
+        public override object? Value => Instance?.GetValue(_property);
 
 
-        public override bool SetValue(object value, BindingPriority priority)
+        public override bool SetValue(object? value, BindingPriority priority)
         {
         {
-            if (!_property.IsReadOnly)
+            if (!_property.IsReadOnly && Instance is { } instance)
             {
             {
-                Instance.SetValue(_property, value, priority);
+                instance.SetValue(_property, value, priority);
                 return true;
                 return true;
             }
             }
 
 
@@ -70,10 +70,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class InpcPropertyAccessor : PropertyAccessorBase, IWeakEventSubscriber<PropertyChangedEventArgs>
     internal class InpcPropertyAccessor : PropertyAccessorBase, IWeakEventSubscriber<PropertyChangedEventArgs>
     {
     {
-        protected readonly WeakReference<object> _reference;
+        protected readonly WeakReference<object?> _reference;
         private readonly IPropertyInfo _property;
         private readonly IPropertyInfo _property;
 
 
-        public InpcPropertyAccessor(WeakReference<object> reference, IPropertyInfo property)
+        public InpcPropertyAccessor(WeakReference<object?> reference, IPropertyInfo property)
         {
         {
             _reference = reference ?? throw new ArgumentNullException(nameof(reference));
             _reference = reference ?? throw new ArgumentNullException(nameof(reference));
             _property = property ?? throw new ArgumentNullException(nameof(property));
             _property = property ?? throw new ArgumentNullException(nameof(property));
@@ -81,7 +81,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
         public override Type PropertyType => _property.PropertyType;
         public override Type PropertyType => _property.PropertyType;
 
 
-        public override object Value
+        public override object? Value
         {
         {
             get
             get
             {
             {
@@ -89,7 +89,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             }
             }
         }
         }
 
 
-        public override bool SetValue(object value, BindingPriority priority)
+        public override bool SetValue(object? value, BindingPriority priority)
         {
         {
             if (_property.CanSet && _reference.TryGetTarget(out var o))
             if (_property.CanSet && _reference.TryGetTarget(out var o))
             {
             {
@@ -103,7 +103,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             return false;
             return false;
         }
         }
 
 
-        public void OnEvent(object sender, WeakEvent ev, PropertyChangedEventArgs e)
+        public void OnEvent(object? sender, WeakEvent ev, PropertyChangedEventArgs e)
         {
         {
             if (e.PropertyName == _property.Name || string.IsNullOrEmpty(e.PropertyName))
             if (e.PropertyName == _property.Name || string.IsNullOrEmpty(e.PropertyName))
             {
             {
@@ -144,9 +144,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 
 
     internal class IndexerAccessor : InpcPropertyAccessor, IWeakEventSubscriber<NotifyCollectionChangedEventArgs>
     internal class IndexerAccessor : InpcPropertyAccessor, IWeakEventSubscriber<NotifyCollectionChangedEventArgs>
     {
     {
-        private int _index;
+        private readonly int _index;
 
 
-        public IndexerAccessor(WeakReference<object> target, IPropertyInfo basePropertyInfo, int argument)
+        public IndexerAccessor(WeakReference<object?> target, IPropertyInfo basePropertyInfo, int argument)
             :base(target, basePropertyInfo)
             :base(target, basePropertyInfo)
         {
         {
             _index = argument;
             _index = argument;
@@ -167,7 +167,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                 WeakEvents.CollectionChanged.Unsubscribe(incc, this);
                 WeakEvents.CollectionChanged.Unsubscribe(incc, this);
         }
         }
         
         
-        public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs args)
+        public void OnEvent(object? sender, WeakEvent ev, NotifyCollectionChangedEventArgs args)
         {
         {
             if (ShouldNotifyListeners(args))
             if (ShouldNotifyListeners(args))
             {
             {
@@ -175,7 +175,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
             }
             }
         }
         }
 
 
-        bool ShouldNotifyListeners(NotifyCollectionChangedEventArgs e)
+        private bool ShouldNotifyListeners(NotifyCollectionChangedEventArgs e)
         {
         {
             switch (e.Action)
             switch (e.Action)
             {
             {
@@ -185,12 +185,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                     return _index >= e.OldStartingIndex;
                     return _index >= e.OldStartingIndex;
                 case NotifyCollectionChangedAction.Replace:
                 case NotifyCollectionChangedAction.Replace:
                     return _index >= e.NewStartingIndex &&
                     return _index >= e.NewStartingIndex &&
-                           _index < e.NewStartingIndex + e.NewItems.Count;
+                           _index < e.NewStartingIndex + e.NewItems!.Count;
                 case NotifyCollectionChangedAction.Move:
                 case NotifyCollectionChangedAction.Move:
                     return (_index >= e.NewStartingIndex &&
                     return (_index >= e.NewStartingIndex &&
-                            _index < e.NewStartingIndex + e.NewItems.Count) ||
+                            _index < e.NewStartingIndex + e.NewItems!.Count) ||
                            (_index >= e.OldStartingIndex &&
                            (_index >= e.OldStartingIndex &&
-                            _index < e.OldStartingIndex + e.OldItems.Count);
+                            _index < e.OldStartingIndex + e.OldItems!.Count);
                 case NotifyCollectionChangedAction.Reset:
                 case NotifyCollectionChangedAction.Reset:
                     return true;
                     return true;
             }
             }

+ 4 - 7
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorPlugin.cs

@@ -1,20 +1,17 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Text;
-using Avalonia.Data;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core.Plugins;
 using Avalonia.Data.Core.Plugins;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
-    class PropertyInfoAccessorPlugin : IPropertyAccessorPlugin
+    internal class PropertyInfoAccessorPlugin : IPropertyAccessorPlugin
     {
     {
         private readonly IPropertyInfo _propertyInfo;
         private readonly IPropertyInfo _propertyInfo;
-        private readonly Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> _accessorFactory;
+        private readonly Func<WeakReference<object?>, IPropertyInfo, IPropertyAccessor> _accessorFactory;
 
 
-        public PropertyInfoAccessorPlugin(IPropertyInfo propertyInfo, Func<WeakReference<object>, IPropertyInfo, IPropertyAccessor> accessorFactory)
+        public PropertyInfoAccessorPlugin(IPropertyInfo propertyInfo, Func<WeakReference<object?>, IPropertyInfo, IPropertyAccessor> accessorFactory)
         {
         {
             _propertyInfo = propertyInfo;
             _propertyInfo = propertyInfo;
             _accessorFactory = accessorFactory;
             _accessorFactory = accessorFactory;
@@ -27,7 +24,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
-        public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
+        public IPropertyAccessor Start(WeakReference<object?> reference, string propertyName)
         {
         {
             Debug.Assert(_propertyInfo.Name == propertyName);
             Debug.Assert(_propertyInfo.Name == propertyName);
             return _accessorFactory(reference, _propertyInfo);
             return _accessorFactory(reference, _propertyInfo);

+ 3 - 3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs

@@ -5,14 +5,14 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
     internal class StrongTypeCastNode : TypeCastNode
     internal class StrongTypeCastNode : TypeCastNode
     {
     {
-        private Func<object, object> _cast;
+        private readonly Func<object?, object?> _cast;
 
 
-        public StrongTypeCastNode(Type type, Func<object, object> cast) : base(type)
+        public StrongTypeCastNode(Type type, Func<object?, object?> cast) : base(type)
         {
         {
             _cast = cast;
             _cast = cast;
         }
         }
 
 
-        protected override object Cast(object value)
+        protected override object? Cast(object? value)
             => _cast(value);
             => _cast(value);
     }
     }
 }
 }

+ 9 - 9
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/TaskStreamPlugin.cs

@@ -7,20 +7,20 @@ using Avalonia.Reactive;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
 {
 {
-    class TaskStreamPlugin<T> : IStreamPlugin
+    internal class TaskStreamPlugin<T> : IStreamPlugin
     {
     {
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
-        public bool Match(WeakReference<object> reference)
+        public bool Match(WeakReference<object?> reference)
         {
         {
             return reference.TryGetTarget(out var target) && target is Task<T>;
             return reference.TryGetTarget(out var target) && target is Task<T>;
         }
         }
 
 
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
-        public IObservable<object> Start(WeakReference<object> reference)
+        public IObservable<object?> Start(WeakReference<object?> reference)
         {
         {
             if(!(reference.TryGetTarget(out var target) && target is Task<T> task))
             if(!(reference.TryGetTarget(out var target) && target is Task<T> task))
             {
             {
-                return Observable.Empty<object>();
+                return Observable.Empty<object?>();
             }
             }
 
 
             switch (task.Status)
             switch (task.Status)
@@ -29,9 +29,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
                 case TaskStatus.Faulted:
                 case TaskStatus.Faulted:
                     return HandleCompleted(task);
                     return HandleCompleted(task);
                 default:
                 default:
-                    var subject = new LightweightSubject<object>();
+                    var subject = new LightweightSubject<object?>();
                     task.ContinueWith(
                     task.ContinueWith(
-                            x => HandleCompleted(task).Subscribe(subject),
+                            _ => HandleCompleted(task).Subscribe(subject),
                             TaskScheduler.FromCurrentSynchronizationContext())
                             TaskScheduler.FromCurrentSynchronizationContext())
                         .ConfigureAwait(false);
                         .ConfigureAwait(false);
                     return subject;
                     return subject;
@@ -39,14 +39,14 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
         }
         }
         
         
         
         
-        private static IObservable<object> HandleCompleted(Task<T> task)
+        private static IObservable<object?> HandleCompleted(Task<T> task)
         {
         {
             switch (task.Status)
             switch (task.Status)
             {
             {
                 case TaskStatus.RanToCompletion:
                 case TaskStatus.RanToCompletion:
-                    return Observable.Return((object)task.Result);
+                    return Observable.Return((object?)task.Result);
                 case TaskStatus.Faulted:
                 case TaskStatus.Faulted:
-                    return Observable.Return(new BindingNotification(task.Exception, BindingErrorType.Error));
+                    return Observable.Return(new BindingNotification(task.Exception!, BindingErrorType.Error));
                 default:
                 default:
                     throw new AvaloniaInternalException("HandleCompleted called for non-completed Task.");
                     throw new AvaloniaInternalException("HandleCompleted called for non-completed Task.");
             }
             }

+ 1 - 3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@@ -4,8 +4,6 @@ using Avalonia.Data;
 using Avalonia.Markup.Xaml.Converters;
 using Avalonia.Markup.Xaml.Converters;
 using Avalonia.Media;
 using Avalonia.Media;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
 {
     public class DynamicResourceExtension : IBinding
     public class DynamicResourceExtension : IBinding
@@ -31,7 +29,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
 
             var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
             var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
 
 
-            if (!(provideTarget.TargetObject is StyledElement))
+            if (provideTarget?.TargetObject is not StyledElement)
             {
             {
                 _anchor = serviceProvider.GetFirstParent<StyledElement>() ??
                 _anchor = serviceProvider.GetFirstParent<StyledElement>() ??
                     serviceProvider.GetFirstParent<IResourceProvider>() ??
                     serviceProvider.GetFirstParent<IResourceProvider>() ??

+ 1 - 2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/On.cs

@@ -1,5 +1,4 @@
-#nullable enable
-using System.Collections.Generic;
+using System.Collections.Generic;
 using Avalonia.Metadata;
 using Avalonia.Metadata;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions;
 namespace Avalonia.Markup.Xaml.MarkupExtensions;

+ 2 - 4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs

@@ -1,6 +1,4 @@
-#nullable enable
 using System;
 using System;
-
 using Avalonia.Metadata;
 using Avalonia.Metadata;
 using Avalonia.Platform;
 using Avalonia.Platform;
 
 
@@ -20,7 +18,7 @@ public sealed class OnFormFactorExtension : OnFormFactorExtensionBase<object, On
 
 
     public static bool ShouldProvideOption(IServiceProvider serviceProvider, FormFactorType option)
     public static bool ShouldProvideOption(IServiceProvider serviceProvider, FormFactorType option)
     {
     {
-        return serviceProvider.GetService<IRuntimePlatform>().GetRuntimeInfo().FormFactor == option;
+        return serviceProvider.GetService<IRuntimePlatform>()?.GetRuntimeInfo().FormFactor == option;
     }
     }
 }
 }
 
 
@@ -38,7 +36,7 @@ public sealed class OnFormFactorExtension<TReturn> : OnFormFactorExtensionBase<T
 
 
     public static bool ShouldProvideOption(IServiceProvider serviceProvider, FormFactorType option)
     public static bool ShouldProvideOption(IServiceProvider serviceProvider, FormFactorType option)
     {
     {
-        return serviceProvider.GetService<IRuntimePlatform>().GetRuntimeInfo().FormFactor == option;
+        return serviceProvider.GetService<IRuntimePlatform>()?.GetRuntimeInfo().FormFactor == option;
     }
     }
 }
 }
 
 

+ 1 - 3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs

@@ -1,6 +1,4 @@
-#nullable enable
-using System;
-using Avalonia.Compatibility;
+using Avalonia.Compatibility;
 using Avalonia.Metadata;
 using Avalonia.Metadata;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions;
 namespace Avalonia.Markup.Xaml.MarkupExtensions;

+ 12 - 15
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs

@@ -2,7 +2,6 @@ using Avalonia.Data;
 using System;
 using System;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Data.Converters;
 using Avalonia.Data.Converters;
-using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 
 
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 namespace Avalonia.Markup.Xaml.MarkupExtensions
@@ -21,11 +20,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
 
         public Binding ProvideValue(IServiceProvider serviceProvider)
         public Binding ProvideValue(IServiceProvider serviceProvider)
         {
         {
-            var descriptorContext = (ITypeDescriptorContext)serviceProvider;
-
             return new Binding
             return new Binding
             {
             {
-                TypeResolver = descriptorContext.ResolveType,
+                TypeResolver = serviceProvider.ResolveType,
                 Converter = Converter,
                 Converter = Converter,
                 ConverterParameter = ConverterParameter,
                 ConverterParameter = ConverterParameter,
                 ElementName = ElementName,
                 ElementName = ElementName,
@@ -36,33 +33,33 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 Source = Source,
                 Source = Source,
                 StringFormat = StringFormat,
                 StringFormat = StringFormat,
                 RelativeSource = RelativeSource,
                 RelativeSource = RelativeSource,
-                DefaultAnchor = new WeakReference(descriptorContext.GetDefaultAnchor()),
+                DefaultAnchor = new WeakReference(serviceProvider.GetDefaultAnchor()),
                 TargetNullValue = TargetNullValue,
                 TargetNullValue = TargetNullValue,
-                NameScope = new WeakReference<INameScope>(serviceProvider.GetService<INameScope>())
+                NameScope = new WeakReference<INameScope?>(serviceProvider.GetService<INameScope>())
             };
             };
         }
         }
 
 
-        public IValueConverter Converter { get; set; }
+        public IValueConverter? Converter { get; set; }
 
 
-        public object ConverterParameter { get; set; }
+        public object? ConverterParameter { get; set; }
 
 
-        public string ElementName { get; set; }
+        public string? ElementName { get; set; }
 
 
-        public object FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
+        public object? FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
 
 
         public BindingMode Mode { get; set; }
         public BindingMode Mode { get; set; }
 
 
         [ConstructorArgument("path")]
         [ConstructorArgument("path")]
-        public string Path { get; set; }
+        public string Path { get; set; } = "";
 
 
         public BindingPriority Priority { get; set; } = BindingPriority.LocalValue;
         public BindingPriority Priority { get; set; } = BindingPriority.LocalValue;
 
 
-        public object Source { get; set; }
+        public object? Source { get; set; }
 
 
-        public string StringFormat { get; set; }
+        public string? StringFormat { get; set; }
 
 
-        public RelativeSource RelativeSource { get; set; }
+        public RelativeSource? RelativeSource { get; set; }
 
 
-        public object TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
+        public object? TargetNullValue { get; set; } = AvaloniaProperty.UnsetValue;
     }
     }
 }
 }

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs

@@ -28,7 +28,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
         [ConstructorArgument("mode")]
         [ConstructorArgument("mode")]
         public RelativeSourceMode Mode { get; set; } = RelativeSourceMode.FindAncestor;
         public RelativeSourceMode Mode { get; set; } = RelativeSourceMode.FindAncestor;
 
 
-        public Type AncestorType { get; set; }
+        public Type? AncestorType { get; set; }
 
 
         public TreeType Tree { get; set; }
         public TreeType Tree { get; set; }
 
 

+ 7 - 4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResolveByNameExtension.cs

@@ -2,8 +2,6 @@
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core;
 
 
-#nullable  enable
-
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
 {
     public class ResolveByNameExtension
     public class ResolveByNameExtension
@@ -18,6 +16,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
         public object? ProvideValue(IServiceProvider serviceProvider)
         public object? ProvideValue(IServiceProvider serviceProvider)
         {
         {
             var nameScope = serviceProvider.GetService<INameScope>();
             var nameScope = serviceProvider.GetService<INameScope>();
+
+            if (nameScope is null)
+                return null;
             
             
             var value = nameScope.FindAsync(Name);
             var value = nameScope.FindAsync(Name);
 
 
@@ -25,10 +26,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
                 return value.GetResult();
                 return value.GetResult();
 
 
             var provideValueTarget = serviceProvider.GetService<IProvideValueTarget>();
             var provideValueTarget = serviceProvider.GetService<IProvideValueTarget>();
-            var target = provideValueTarget.TargetObject;
 
 
-            if (provideValueTarget.TargetProperty is IPropertyInfo property) 
+            if (provideValueTarget?.TargetProperty is IPropertyInfo property)
+            {
+                var target = provideValueTarget.TargetObject;
                 value.OnCompleted(() => property.Set(target, value.GetResult()));
                 value.OnCompleted(() => property.Set(target, value.GetResult()));
+            }
 
 
             return AvaloniaProperty.UnsetValue;
             return AvaloniaProperty.UnsetValue;
         }
         }

+ 18 - 16
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@@ -1,6 +1,5 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Markup.Data;
 using Avalonia.Markup.Data;
@@ -8,8 +7,6 @@ using Avalonia.Markup.Xaml.Converters;
 using Avalonia.Markup.Xaml.XamlIl.Runtime;
 using Avalonia.Markup.Xaml.XamlIl.Runtime;
 using Avalonia.Styling;
 using Avalonia.Styling;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 namespace Avalonia.Markup.Xaml.MarkupExtensions
 {
 {
     public class StaticResourceExtension
     public class StaticResourceExtension
@@ -25,7 +22,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
 
         public object? ResourceKey { get; set; }
         public object? ResourceKey { get; set; }
 
 
-        public object ProvideValue(IServiceProvider serviceProvider)
+        public object? ProvideValue(IServiceProvider serviceProvider)
         {
         {
             if (ResourceKey is not { } resourceKey)
             if (ResourceKey is not { } resourceKey)
             {
             {
@@ -34,32 +31,37 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
 
             var stack = serviceProvider.GetService<IAvaloniaXamlIlParentStackProvider>();
             var stack = serviceProvider.GetService<IAvaloniaXamlIlParentStackProvider>();
             var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
             var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
-            var themeVariant = (provideTarget.TargetObject as IThemeVariantHost)?.ActualThemeVariant;
+            var targetObject = provideTarget?.TargetObject;
+            var targetProperty = provideTarget?.TargetProperty;
+            var themeVariant = (targetObject as IThemeVariantHost)?.ActualThemeVariant;
 
 
-            var targetType = provideTarget.TargetProperty switch
+            var targetType = targetProperty switch
             {
             {
                 AvaloniaProperty ap => ap.PropertyType,
                 AvaloniaProperty ap => ap.PropertyType,
                 PropertyInfo pi => pi.PropertyType,
                 PropertyInfo pi => pi.PropertyType,
-                _ => null,
+                _ => null
             };
             };
 
 
-            if (provideTarget.TargetObject is Setter { Property: not null } setter)
+            if (targetObject is Setter { Property: { } setterProperty })
             {
             {
-                targetType = setter.Property?.PropertyType;
+                targetType = setterProperty.PropertyType;
             }
             }
-            
+
             // Look upwards though the ambient context for IResourceNodes
             // Look upwards though the ambient context for IResourceNodes
             // which might be able to give us the resource.
             // which might be able to give us the resource.
-            foreach (var parent in stack.Parents)
+            if (stack is not null)
             {
             {
-                if (parent is IResourceNode node && node.TryGetResource(resourceKey, themeVariant, out var value))
+                foreach (var parent in stack.Parents)
                 {
                 {
-                    return ColorToBrushConverter.Convert(value, targetType);
+                    if (parent is IResourceNode node && node.TryGetResource(resourceKey, themeVariant, out var value))
+                    {
+                        return ColorToBrushConverter.Convert(value, targetType);
+                    }
                 }
                 }
             }
             }
 
 
-            if (provideTarget.TargetObject is Control target &&
-                provideTarget.TargetProperty is PropertyInfo property)
+            if (targetObject is Control target &&
+                targetProperty is PropertyInfo property)
             {
             {
                 // This is stored locally to avoid allocating closure in the outer scope.
                 // This is stored locally to avoid allocating closure in the outer scope.
                 var localTargetType = targetType;
                 var localTargetType = targetType;
@@ -72,7 +74,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             throw new KeyNotFoundException($"Static resource '{resourceKey}' not found.");
             throw new KeyNotFoundException($"Static resource '{resourceKey}' not found.");
         }
         }
 
 
-        private object GetValue(StyledElement control, Type? targetType)
+        private object? GetValue(StyledElement control, Type? targetType)
         {
         {
             return ColorToBrushConverter.Convert(control.FindResource(ResourceKey!), targetType);
             return ColorToBrushConverter.Convert(control.FindResource(ResourceKey!), targetType);
         }
         }

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs

@@ -5,7 +5,7 @@ namespace Avalonia.Markup.Xaml.Parsers
 {
 {
     internal class PropertyParser
     internal class PropertyParser
     {
     {
-        public static (string ns, string owner, string name) Parse(CharacterReader r)
+        public static (string? ns, string? owner, string name) Parse(CharacterReader r)
         {
         {
             if (r.End)
             if (r.End)
             {
             {
@@ -14,9 +14,9 @@ namespace Avalonia.Markup.Xaml.Parsers
 
 
             var openParens = r.TakeIf('(');
             var openParens = r.TakeIf('(');
             bool closeParens = false;
             bool closeParens = false;
-            string ns = null;
-            string owner = null;
-            string name = null;
+            string? ns = null;
+            string? owner = null;
+            string? name = null;
 
 
             do
             do
             {
             {

+ 0 - 2
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs

@@ -2,8 +2,6 @@ using System.Reflection;
 
 
 namespace Avalonia.Markup.Xaml;
 namespace Avalonia.Markup.Xaml;
 
 
-#nullable enable
-
 public class RuntimeXamlLoaderConfiguration
 public class RuntimeXamlLoaderConfiguration
 {
 {
     /// <summary>
     /// <summary>

+ 1 - 2
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs

@@ -1,5 +1,4 @@
-#nullable enable
-using System;
+using System;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
 
 

+ 0 - 3
src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs

@@ -1,8 +1,5 @@
 using System;
 using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using Avalonia.Controls;
-
-#nullable enable
 
 
 namespace Avalonia.Markup.Xaml.Styling;
 namespace Avalonia.Markup.Xaml.Styling;
 
 

+ 0 - 2
src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs

@@ -3,8 +3,6 @@ using System.Diagnostics.CodeAnalysis;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Styling;
 using Avalonia.Styling;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Xaml.Styling
 namespace Avalonia.Markup.Xaml.Styling
 {
 {
     /// <summary>
     /// <summary>

+ 2 - 4
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@@ -4,8 +4,6 @@ using Avalonia.Controls;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Xaml.Styling
 namespace Avalonia.Markup.Xaml.Styling
 {
 {
     /// <summary>
     /// <summary>
@@ -65,11 +63,11 @@ namespace Avalonia.Markup.Xaml.Styling
                     _isLoading = false;
                     _isLoading = false;
                 }
                 }
 
 
-                return _loaded?[0]!;
+                return _loaded[0];
             }
             }
         }
         }
 
 
-        bool IResourceNode.HasResources => Loaded?.HasResources ?? false;
+        bool IResourceNode.HasResources => Loaded.HasResources;
 
 
         IReadOnlyList<IStyle> IStyle.Children => _loaded ?? Array.Empty<IStyle>();
         IReadOnlyList<IStyle> IStyle.Children => _loaded ?? Array.Empty<IStyle>();
 
 

+ 3 - 3
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@@ -9,10 +9,10 @@ namespace Avalonia.Markup.Xaml.Templates
     {
     {
         [Content]
         [Content]
         [TemplateContent]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
 
-        public Type TargetType { get; set; }
+        public Type? TargetType { get; set; }
 
 
-        public ControlTemplateResult Build(TemplatedControl control) => TemplateContent.Load(Content);
+        public ControlTemplateResult? Build(TemplatedControl control) => TemplateContent.Load(Content);
     }
     }
 }
 }

+ 5 - 5
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@@ -8,13 +8,13 @@ namespace Avalonia.Markup.Xaml.Templates
     public class DataTemplate : IRecyclingDataTemplate, ITypedDataTemplate
     public class DataTemplate : IRecyclingDataTemplate, ITypedDataTemplate
     {
     {
         [DataType]
         [DataType]
-        public Type DataType { get; set; }
+        public Type? DataType { get; set; }
 
 
         [Content]
         [Content]
         [TemplateContent]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
 
-        public bool Match(object data)
+        public bool Match(object? data)
         {
         {
             if (DataType == null)
             if (DataType == null)
             {
             {
@@ -26,9 +26,9 @@ namespace Avalonia.Markup.Xaml.Templates
             }
             }
         }
         }
 
 
-        public Control Build(object data) => Build(data, null);
+        public Control? Build(object? data) => Build(data, null);
 
 
-        public Control Build(object data, Control existing)
+        public Control? Build(object? data, Control? existing)
         {
         {
             return existing ?? TemplateContent.Load(Content)?.Control;
             return existing ?? TemplateContent.Load(Content)?.Control;
         }
         }

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@@ -4,14 +4,14 @@ using Avalonia.Styling;
 
 
 namespace Avalonia.Markup.Xaml.Templates
 namespace Avalonia.Markup.Xaml.Templates
 {
 {
-    public class ItemsPanelTemplate : ITemplate<Panel>
+    public class ItemsPanelTemplate : ITemplate<Panel?>
     {
     {
         [Content]
         [Content]
         [TemplateContent]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
 
-        public Panel Build() => (Panel)TemplateContent.Load(Content)?.Control;
+        public Panel? Build() => (Panel?)TemplateContent.Load(Content)?.Control;
 
 
-        object ITemplate.Build() => Build();
+        object? ITemplate.Build() => Build();
     }
     }
 }
 }

+ 4 - 4
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

@@ -4,14 +4,14 @@ using Avalonia.Styling;
 
 
 namespace Avalonia.Markup.Xaml.Templates
 namespace Avalonia.Markup.Xaml.Templates
 {
 {
-    public class Template : ITemplate<Control>
+    public class Template : ITemplate<Control?>
     {
     {
         [Content]
         [Content]
         [TemplateContent]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
 
-        public Control Build() => TemplateContent.Load(Content)?.Control;
+        public Control? Build() => TemplateContent.Load(Content)?.Control;
 
 
-        object ITemplate.Build() => Build();
+        object? ITemplate.Build() => Build();
     }
     }
 }
 }

+ 8 - 9
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@@ -3,14 +3,13 @@ using Avalonia.Controls.Templates;
 
 
 namespace Avalonia.Markup.Xaml.Templates
 namespace Avalonia.Markup.Xaml.Templates
 {
 {
-    
     public static class TemplateContent
     public static class TemplateContent
     {
     {
-        public static ControlTemplateResult Load(object templateContent)
+        public static ControlTemplateResult? Load(object? templateContent)
         {
         {
-            if (templateContent is Func<IServiceProvider, object> direct)
+            if (templateContent is Func<IServiceProvider?, object?> direct)
             {
             {
-                return (ControlTemplateResult)direct(null);
+                return (ControlTemplateResult?)direct(null);
             }
             }
 
 
             if (templateContent is null)
             if (templateContent is null)
@@ -18,18 +17,18 @@ namespace Avalonia.Markup.Xaml.Templates
                 return null;
                 return null;
             }
             }
 
 
-            throw new ArgumentException(nameof(templateContent));
+            throw new ArgumentException($"Unexpected content {templateContent.GetType()}", nameof(templateContent));
         }
         }
 
 
-        public static TemplateResult<T> Load<T>(object templateContent)
+        public static TemplateResult<T>? Load<T>(object? templateContent)
         {
         {
-            if (templateContent is Func<IServiceProvider, object> direct)
-                return (TemplateResult<T>)direct(null);
+            if (templateContent is Func<IServiceProvider?, object?> direct)
+                return (TemplateResult<T>?)direct(null);
 
 
             if (templateContent is null)
             if (templateContent is null)
                 return null;
                 return null;
 
 
-            throw new ArgumentException(nameof(templateContent));
+            throw new ArgumentException($"Unexpected content {templateContent.GetType()}", nameof(templateContent));
         }
         }
     }
     }
 }
 }

+ 6 - 6
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@@ -13,16 +13,16 @@ namespace Avalonia.Markup.Xaml.Templates
     public class TreeDataTemplate : ITreeDataTemplate, ITypedDataTemplate
     public class TreeDataTemplate : ITreeDataTemplate, ITypedDataTemplate
     {
     {
         [DataType]
         [DataType]
-        public Type DataType { get; set; }
+        public Type? DataType { get; set; }
 
 
         [Content]
         [Content]
         [TemplateContent]
         [TemplateContent]
-        public object Content { get; set; }
+        public object? Content { get; set; }
 
 
         [AssignBinding]
         [AssignBinding]
-        public BindingBase ItemsSource { get; set; }
+        public BindingBase? ItemsSource { get; set; }
 
 
-        public bool Match(object data)
+        public bool Match(object? data)
         {
         {
             if (DataType == null)
             if (DataType == null)
             {
             {
@@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.Templates
         }
         }
 
 
         [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "If ItemsSource is a CompiledBinding, then path members will be preserver")]
         [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "If ItemsSource is a CompiledBinding, then path members will be preserver")]
-        public InstancedBinding ItemsSelector(object item)
+        public InstancedBinding? ItemsSelector(object item)
         {
         {
             if (ItemsSource != null)
             if (ItemsSource != null)
             {
             {
@@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.Templates
             return null;
             return null;
         }
         }
 
 
-        public Control Build(object data)
+        public Control? Build(object? data)
         {
         {
             var visualTreeForItem = TemplateContent.Load(Content)?.Control;
             var visualTreeForItem = TemplateContent.Load(Content)?.Control;
             if (visualTreeForItem != null)
             if (visualTreeForItem != null)

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
     
     
     public class AvaloniaXamlIlXmlNamespaceInfo
     public class AvaloniaXamlIlXmlNamespaceInfo
     {
     {
-        public string ClrNamespace { get; set; }
-        public string ClrAssemblyName { get; set; }
+        public string ClrNamespace { get; set; } = string.Empty;
+        public string ClrAssemblyName { get; set; } = string.Empty;
     }
     }
 }
 }

+ 18 - 22
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -6,10 +6,7 @@ using System.Reflection;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.Templates;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
 using Avalonia.Data;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Avalonia.Markup.Xaml.Styling;
 using Avalonia.Platform;
 using Avalonia.Platform;
-using Avalonia.Styling;
 
 
 // ReSharper disable UnusedMember.Global
 // ReSharper disable UnusedMember.Global
 // ReSharper disable UnusedParameter.Global
 // ReSharper disable UnusedParameter.Global
@@ -27,9 +24,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         public static Func<IServiceProvider, object> DeferredTransformationFactoryV2<T>(Func<IServiceProvider, object> builder,
         public static Func<IServiceProvider, object> DeferredTransformationFactoryV2<T>(Func<IServiceProvider, object> builder,
             IServiceProvider provider)
             IServiceProvider provider)
         {
         {
-            var resourceNodes = provider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents
+            var resourceNodes = provider.GetRequiredService<IAvaloniaXamlIlParentStackProvider>().Parents
                 .OfType<IResourceNode>().ToList();
                 .OfType<IResourceNode>().ToList();
-            var rootObject = provider.GetService<IRootObjectProvider>().RootObject;
+            var rootObject = provider.GetRequiredService<IRootObjectProvider>().RootObject;
             var parentScope = provider.GetService<INameScope>();
             var parentScope = provider.GetService<INameScope>();
             return sp =>
             return sp =>
             {
             {
@@ -44,17 +41,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
             };
             };
         }
         }
 
 
-        class DeferredParentServiceProvider :
+        private class DeferredParentServiceProvider :
             IAvaloniaXamlIlParentStackProvider,
             IAvaloniaXamlIlParentStackProvider,
             IServiceProvider,
             IServiceProvider,
             IRootObjectProvider,
             IRootObjectProvider,
             IAvaloniaXamlIlControlTemplateProvider
             IAvaloniaXamlIlControlTemplateProvider
         {
         {
-            private readonly IServiceProvider _parentProvider;
-            private readonly List<IResourceNode> _parentResourceNodes;
+            private readonly IServiceProvider? _parentProvider;
+            private readonly List<IResourceNode>? _parentResourceNodes;
             private readonly INameScope _nameScope;
             private readonly INameScope _nameScope;
 
 
-            public DeferredParentServiceProvider(IServiceProvider parentProvider, List<IResourceNode> parentResourceNodes,
+            public DeferredParentServiceProvider(IServiceProvider? parentProvider, List<IResourceNode>? parentResourceNodes,
                 object rootObject, INameScope nameScope)
                 object rootObject, INameScope nameScope)
             {
             {
                 _parentProvider = parentProvider;
                 _parentProvider = parentProvider;
@@ -73,7 +70,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
                     yield return p;
                     yield return p;
             }
             }
 
 
-            public object GetService(Type serviceType)
+            public object? GetService(Type serviceType)
             {
             {
                 if (serviceType == typeof(INameScope))
                 if (serviceType == typeof(INameScope))
                     return _nameScope;
                     return _nameScope;
@@ -115,25 +112,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         public static IServiceProvider CreateInnerServiceProviderV1(IServiceProvider compiled)
         public static IServiceProvider CreateInnerServiceProviderV1(IServiceProvider compiled)
             => new InnerServiceProvider(compiled);
             => new InnerServiceProvider(compiled);
 
 
-        class InnerServiceProvider : IServiceProvider
+        private class InnerServiceProvider : IServiceProvider
         {
         {
             private readonly IServiceProvider _compiledProvider;
             private readonly IServiceProvider _compiledProvider;
-            private XamlTypeResolver _resolver;
+            private XamlTypeResolver? _resolver;
 
 
             public InnerServiceProvider(IServiceProvider compiledProvider)
             public InnerServiceProvider(IServiceProvider compiledProvider)
             {
             {
                 _compiledProvider = compiledProvider;
                 _compiledProvider = compiledProvider;
             }
             }
-            public object GetService(Type serviceType)
+            public object? GetService(Type serviceType)
             {
             {
                 if (serviceType == typeof(IXamlTypeResolver))
                 if (serviceType == typeof(IXamlTypeResolver))
-                    return _resolver ?? (_resolver = new XamlTypeResolver(
-                               _compiledProvider.GetService<IAvaloniaXamlIlXmlNamespaceInfoProvider>()));
+                    return _resolver ??= new XamlTypeResolver(
+                        _compiledProvider.GetRequiredService<IAvaloniaXamlIlXmlNamespaceInfoProvider>());
                 return null;
                 return null;
             }
             }
         }
         }
 
 
-        class XamlTypeResolver : IXamlTypeResolver
+        private class XamlTypeResolver : IXamlTypeResolver
         {
         {
             private readonly IAvaloniaXamlIlXmlNamespaceInfoProvider _nsInfo;
             private readonly IAvaloniaXamlIlXmlNamespaceInfoProvider _nsInfo;
 
 
@@ -148,7 +145,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
                 var sp = qualifiedTypeName.Split(new[] {':'}, 2);
                 var sp = qualifiedTypeName.Split(new[] {':'}, 2);
                 var (ns, name) = sp.Length == 1 ? ("", qualifiedTypeName) : (sp[0], sp[1]);
                 var (ns, name) = sp.Length == 1 ? ("", qualifiedTypeName) : (sp[0], sp[1]);
                 var namespaces = _nsInfo.XmlNamespaces;
                 var namespaces = _nsInfo.XmlNamespaces;
-                var dic = (Dictionary<string, IReadOnlyList<AvaloniaXamlIlXmlNamespaceInfo>>)namespaces;
                 if (!namespaces.TryGetValue(ns, out var lst))
                 if (!namespaces.TryGetValue(ns, out var lst))
                     throw new ArgumentException("Unable to resolve namespace for type " + qualifiedTypeName);
                     throw new ArgumentException("Unable to resolve namespace for type " + qualifiedTypeName);
                 foreach (var entry in lst)
                 foreach (var entry in lst)
@@ -177,20 +173,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
         }
         }
         #line default
         #line default
 
 
-        class RootServiceProvider : IServiceProvider
+        private class RootServiceProvider : IServiceProvider
         {
         {
             private readonly INameScope _nameScope;
             private readonly INameScope _nameScope;
-            private readonly IServiceProvider _parentServiceProvider;
-            private readonly IRuntimePlatform _runtimePlatform;
+            private readonly IServiceProvider? _parentServiceProvider;
+            private readonly IRuntimePlatform? _runtimePlatform;
 
 
-            public RootServiceProvider(INameScope nameScope, IServiceProvider parentServiceProvider)
+            public RootServiceProvider(INameScope nameScope, IServiceProvider? parentServiceProvider)
             {
             {
                 _nameScope = nameScope;
                 _nameScope = nameScope;
                 _parentServiceProvider = parentServiceProvider;
                 _parentServiceProvider = parentServiceProvider;
                 _runtimePlatform = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
                 _runtimePlatform = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
             }
             }
 
 
-            public object GetService(Type serviceType)
+            public object? GetService(Type serviceType)
             {
             {
                 if (serviceType == typeof(INameScope))
                 if (serviceType == typeof(INameScope))
                     return _nameScope;
                     return _nameScope;

+ 1 - 2
src/Markup/Avalonia.Markup/Data/Binding.cs

@@ -16,7 +16,6 @@ namespace Avalonia.Data
         /// Initializes a new instance of the <see cref="Binding"/> class.
         /// Initializes a new instance of the <see cref="Binding"/> class.
         /// </summary>
         /// </summary>
         public Binding()
         public Binding()
-            :base()
         {
         {
         }
         }
 
 
@@ -54,7 +53,7 @@ namespace Avalonia.Data
         /// <summary>
         /// <summary>
         /// Gets or sets a function used to resolve types from names in the binding path.
         /// Gets or sets a function used to resolve types from names in the binding path.
         /// </summary>
         /// </summary>
-        public Func<string, string, Type>? TypeResolver { get; set; }
+        public Func<string?, string, Type>? TypeResolver { get; set; }
 
 
         private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation)
         private protected override ExpressionObserver CreateExpressionObserver(AvaloniaObject target, AvaloniaProperty? targetProperty, object? anchor, bool enableDataValidation)
         {
         {

+ 2 - 3
src/Markup/Avalonia.Markup/Data/BindingBase.cs

@@ -7,7 +7,6 @@ using Avalonia.LogicalTree;
 using Avalonia.Reactive;
 using Avalonia.Reactive;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
 
 
-
 namespace Avalonia.Data
 namespace Avalonia.Data
 {
 {
     public abstract class BindingBase : IBinding
     public abstract class BindingBase : IBinding
@@ -68,7 +67,7 @@ namespace Avalonia.Data
 
 
         public WeakReference? DefaultAnchor { get; set; }
         public WeakReference? DefaultAnchor { get; set; }
 
 
-        public WeakReference<INameScope>? NameScope { get; set; }
+        public WeakReference<INameScope?>? NameScope { get; set; }
 
 
         private protected abstract ExpressionObserver CreateExpressionObserver(
         private protected abstract ExpressionObserver CreateExpressionObserver(
             AvaloniaObject target,
             AvaloniaObject target,
@@ -169,7 +168,7 @@ namespace Avalonia.Data
         {
         {
             _ = target ?? throw new ArgumentNullException(nameof(target));
             _ = target ?? throw new ArgumentNullException(nameof(target));
 
 
-            if (NameScope is null || !NameScope.TryGetTarget(out var scope) || scope is null)
+            if (NameScope is null || !NameScope.TryGetTarget(out var scope))
                 throw new InvalidOperationException("Name scope is null or was already collected");
                 throw new InvalidOperationException("Name scope is null or was already collected");
             var result = new ExpressionObserver(
             var result = new ExpressionObserver(
                 NameScopeLocator.Track(scope, elementName),
                 NameScopeLocator.Track(scope, elementName),

+ 17 - 10
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Data
         ISetterValue
         ISetterValue
     {
     {
         private bool _isSetterValue;
         private bool _isSetterValue;
-        private StyledElement _target = default!;
+        private StyledElement? _target;
         private Type? _targetType;
         private Type? _targetType;
         private bool _hasProducedValue;
         private bool _hasProducedValue;
 
 
@@ -42,7 +42,7 @@ namespace Avalonia.Data
             //
             //
             // If the binding appears in a `Setter`, then make a clone and instantiate that because
             // If the binding appears in a `Setter`, then make a clone and instantiate that because
             // because the setter can outlive the control and cause a leak.
             // because the setter can outlive the control and cause a leak.
-            if (_target == null && !_isSetterValue)
+            if (_target is null && !_isSetterValue)
             {
             {
                 _target = (StyledElement)target;
                 _target = (StyledElement)target;
                 _targetType = targetProperty?.PropertyType;
                 _targetType = targetProperty?.PropertyType;
@@ -93,9 +93,9 @@ namespace Avalonia.Data
 
 
         void IObserver<object?>.OnNext(object? value)
         void IObserver<object?>.OnNext(object? value)
         {
         {
-            if (_target.TemplatedParent is AvaloniaObject templatedParent && Property != null)
+            if (_target?.TemplatedParent is { } templatedParent && Property is not null)
             {
             {
-                if (Converter != null)
+                if (Converter is not null)
                 {
                 {
                     value = Converter.ConvertBack(
                     value = Converter.ConvertBack(
                         value,
                         value,
@@ -114,24 +114,31 @@ namespace Avalonia.Data
         protected override void Subscribed()
         protected override void Subscribed()
         {
         {
             TemplatedParentChanged();
             TemplatedParentChanged();
-            _target.PropertyChanged += TargetPropertyChanged;
+
+            if (_target is not null)
+            {
+                _target.PropertyChanged += TargetPropertyChanged;
+            }
         }
         }
 
 
         protected override void Unsubscribed()
         protected override void Unsubscribed()
         {
         {
-            if (_target.TemplatedParent is AvaloniaObject templatedParent)
+            if (_target?.TemplatedParent is { } templatedParent)
             {
             {
                 templatedParent.PropertyChanged -= TemplatedParentPropertyChanged;
                 templatedParent.PropertyChanged -= TemplatedParentPropertyChanged;
             }
             }
 
 
-            _target.PropertyChanged -= TargetPropertyChanged;
+            if (_target is not null)
+            {
+                _target.PropertyChanged -= TargetPropertyChanged;
+            }
         }
         }
 
 
         private void PublishValue()
         private void PublishValue()
         {
         {
-            if (_target.TemplatedParent is AvaloniaObject templatedParent)
+            if (_target?.TemplatedParent is { } templatedParent)
             {
             {
-                var value = Property != null ?
+                var value = Property is not null ?
                     templatedParent.GetValue(Property) :
                     templatedParent.GetValue(Property) :
                     _target.TemplatedParent;
                     _target.TemplatedParent;
 
 
@@ -152,7 +159,7 @@ namespace Avalonia.Data
 
 
         private void TemplatedParentChanged()
         private void TemplatedParentChanged()
         {
         {
-            if (_target.TemplatedParent is AvaloniaObject templatedParent)
+            if (_target?.TemplatedParent is { } templatedParent)
             {
             {
                 templatedParent.PropertyChanged += TemplatedParentPropertyChanged;
                 templatedParent.PropertyChanged += TemplatedParentPropertyChanged;
             }
             }

+ 4 - 5
src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs

@@ -20,8 +20,7 @@ namespace Avalonia.Markup.Data
     /// </remarks>
     /// </remarks>
     public static class DelayedBinding
     public static class DelayedBinding
     {
     {
-        private static ConditionalWeakTable<StyledElement, List<Entry>> _entries = 
-            new ConditionalWeakTable<StyledElement, List<Entry>>();
+        private static readonly ConditionalWeakTable<StyledElement, List<Entry>> _entries = new();
 
 
         /// <summary>
         /// <summary>
         /// Adds a delayed binding to a control.
         /// Adds a delayed binding to a control.
@@ -57,7 +56,7 @@ namespace Avalonia.Markup.Data
         /// <param name="target">The control.</param>
         /// <param name="target">The control.</param>
         /// <param name="property">The property on the control to bind to.</param>
         /// <param name="property">The property on the control to bind to.</param>
         /// <param name="value">A function which returns the value.</param>
         /// <param name="value">A function which returns the value.</param>
-        public static void Add(StyledElement target, PropertyInfo property, Func<StyledElement, object> value)
+        public static void Add(StyledElement target, PropertyInfo property, Func<StyledElement, object?> value)
         {
         {
             if (target.IsInitialized)
             if (target.IsInitialized)
             {
             {
@@ -126,14 +125,14 @@ namespace Avalonia.Markup.Data
 
 
         private class ClrPropertyValueEntry : Entry
         private class ClrPropertyValueEntry : Entry
         {
         {
-            public ClrPropertyValueEntry(PropertyInfo property, Func<StyledElement, object> value)
+            public ClrPropertyValueEntry(PropertyInfo property, Func<StyledElement, object?> value)
             {
             {
                 Property = property;
                 Property = property;
                 Value = value;
                 Value = value;
             }
             }
 
 
             public PropertyInfo Property { get; }
             public PropertyInfo Property { get; }
-            public Func<StyledElement, object> Value { get; }
+            public Func<StyledElement, object?> Value { get; }
 
 
             public override void Apply(StyledElement control)
             public override void Apply(StyledElement control)
             {
             {

+ 2 - 4
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

@@ -6,8 +6,6 @@ using Avalonia.Utilities;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 
 
-#nullable enable
-
 namespace Avalonia.Markup.Parsers
 namespace Avalonia.Markup.Parsers
 {
 {
     internal enum SourceMode
     internal enum SourceMode
@@ -201,8 +199,8 @@ namespace Avalonia.Markup.Parsers
 
 
             nodes.Add(new AttachedPropertyNameNode
             nodes.Add(new AttachedPropertyNameNode
             {
             {
-                Namespace = ns.ToString(),
-                TypeName = owner.ToString(),
+                Namespace = ns,
+                TypeName = owner,
                 PropertyName = name.ToString()
                 PropertyName = name.ToString()
             });
             });
             return State.AfterMember;
             return State.AfterMember;

+ 4 - 4
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Markup.Parsers
     internal static class ExpressionObserverBuilder
     internal static class ExpressionObserverBuilder
     {
     {
         [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
         [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
-        internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string, string, Type>? typeResolver = null,
+        internal static (ExpressionNode Node, SourceMode Mode) Parse(string expression, bool enableValidation = false, Func<string?, string, Type>? typeResolver = null,
             INameScope? nameScope = null)
             INameScope? nameScope = null)
         {
         {
             if (string.IsNullOrWhiteSpace(expression))
             if (string.IsNullOrWhiteSpace(expression))
@@ -35,7 +35,7 @@ namespace Avalonia.Markup.Parsers
             string expression,
             string expression,
             bool enableDataValidation = false,
             bool enableDataValidation = false,
             string? description = null,
             string? description = null,
-            Func<string, string, Type>? typeResolver = null)
+            Func<string?, string, Type>? typeResolver = null)
         {
         {
             return new ExpressionObserver(
             return new ExpressionObserver(
                 root,
                 root,
@@ -49,7 +49,7 @@ namespace Avalonia.Markup.Parsers
             string expression,
             string expression,
             bool enableDataValidation = false,
             bool enableDataValidation = false,
             string? description = null,
             string? description = null,
-            Func<string, string, Type>? typeResolver = null)
+            Func<string?, string, Type>? typeResolver = null)
         {
         {
             _ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
             _ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
 
 
@@ -66,7 +66,7 @@ namespace Avalonia.Markup.Parsers
             IObservable<ValueTuple> update,
             IObservable<ValueTuple> update,
             bool enableDataValidation = false,
             bool enableDataValidation = false,
             string? description = null,
             string? description = null,
-            Func<string, string, Type>? typeResolver = null)
+            Func<string?, string, Type>? typeResolver = null)
         {
         {
             _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
             _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
 
 

+ 8 - 10
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@@ -2,8 +2,6 @@ using Avalonia.Data.Core;
 using Avalonia.Markup.Parsers.Nodes;
 using Avalonia.Markup.Parsers.Nodes;
 using Avalonia.Utilities;
 using Avalonia.Utilities;
 using System;
 using System;
-using System.Collections.Generic;
-using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 
 
@@ -15,10 +13,10 @@ namespace Avalonia.Markup.Parsers
     internal class ExpressionParser
     internal class ExpressionParser
     {
     {
         private readonly bool _enableValidation;
         private readonly bool _enableValidation;
-        private readonly Func<string, string, Type>? _typeResolver;
+        private readonly Func<string?, string, Type>? _typeResolver;
         private readonly INameScope? _nameScope;
         private readonly INameScope? _nameScope;
 
 
-        public ExpressionParser(bool enableValidation, Func<string, string, Type>? typeResolver, INameScope? nameScope)
+        public ExpressionParser(bool enableValidation, Func<string?, string, Type>? typeResolver, INameScope? nameScope)
         {
         {
             _typeResolver = typeResolver;
             _typeResolver = typeResolver;
             _nameScope = nameScope;
             _nameScope = nameScope;
@@ -37,13 +35,13 @@ namespace Avalonia.Markup.Parsers
                 ExpressionNode? nextNode = null;
                 ExpressionNode? nextNode = null;
                 switch (astNode)
                 switch (astNode)
                 {
                 {
-                    case BindingExpressionGrammar.EmptyExpressionNode _:
+                    case BindingExpressionGrammar.EmptyExpressionNode:
                         nextNode = new EmptyExpressionNode();
                         nextNode = new EmptyExpressionNode();
                         break;
                         break;
-                    case BindingExpressionGrammar.NotNode _:
+                    case BindingExpressionGrammar.NotNode:
                         nextNode = new LogicalNotNode();
                         nextNode = new LogicalNotNode();
                         break;
                         break;
-                    case BindingExpressionGrammar.StreamNode _:
+                    case BindingExpressionGrammar.StreamNode:
                         nextNode = new StreamNode();
                         nextNode = new StreamNode();
                         break;
                         break;
                     case BindingExpressionGrammar.PropertyNameNode propName:
                     case BindingExpressionGrammar.PropertyNameNode propName:
@@ -55,7 +53,7 @@ namespace Avalonia.Markup.Parsers
                     case BindingExpressionGrammar.AttachedPropertyNameNode attachedProp:
                     case BindingExpressionGrammar.AttachedPropertyNameNode attachedProp:
                         nextNode = ParseAttachedProperty(attachedProp);
                         nextNode = ParseAttachedProperty(attachedProp);
                         break;
                         break;
-                    case BindingExpressionGrammar.SelfNode _:
+                    case BindingExpressionGrammar.SelfNode:
                         nextNode = new SelfNode();
                         nextNode = new SelfNode();
                         break;
                         break;
                     case BindingExpressionGrammar.AncestorNode ancestor:
                     case BindingExpressionGrammar.AncestorNode ancestor:
@@ -90,7 +88,7 @@ namespace Avalonia.Markup.Parsers
             Type? ancestorType = null;
             Type? ancestorType = null;
             var ancestorLevel = node.Level;
             var ancestorLevel = node.Level;
 
 
-            if (!(node.Namespace is null) && !(node.TypeName is null))
+            if (!string.IsNullOrEmpty(node.TypeName))
             {
             {
                 if (_typeResolver == null)
                 if (_typeResolver == null)
                 {
                 {
@@ -106,7 +104,7 @@ namespace Avalonia.Markup.Parsers
         private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
         private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node)
         {
         {
             Type? castType = null;
             Type? castType = null;
-            if (!(node.Namespace is null) && !(node.TypeName is null))
+            if (!string.IsNullOrEmpty(node.TypeName))
             {
             {
                 if (_typeResolver == null)
                 if (_typeResolver == null)
                 {
                 {

+ 2 - 4
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

@@ -4,9 +4,7 @@ using System.Linq;
 using Avalonia.Data.Core;
 using Avalonia.Data.Core;
 using Avalonia.Utilities;
 using Avalonia.Utilities;
 
 
-#nullable enable
-
-// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the 
+// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the
 // only reason they have overridden Equals methods is for unit testing.
 // only reason they have overridden Equals methods is for unit testing.
 #pragma warning disable 659
 #pragma warning disable 659
 
 
@@ -642,7 +640,7 @@ namespace Avalonia.Markup.Parsers
         {
         {
             public override bool Equals(object? obj)
             public override bool Equals(object? obj)
             {
             {
-                return obj is CommaSyntax or;
+                return obj is CommaSyntax;
             }
             }
         }
         }
 
 

Some files were not shown because too many files changed in this diff