Browse Source

Merge branch 'master' into fixes/getTextBounds

Benedikt Stebner 2 years ago
parent
commit
8677eb6a4d
100 changed files with 903 additions and 607 deletions
  1. 17 0
      .editorconfig
  2. 1 1
      Avalonia.sln
  3. 1 1
      build/DevAnalyzers.props
  4. 15 9
      nukebuild/RefAssemblyGenerator.cs
  5. 5 0
      nukebuild/numerge.config
  6. 4 0
      packages/Avalonia/Avalonia.csproj
  7. 5 6
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  8. 1 0
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  9. 2 2
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  10. 1 1
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  11. 7 5
      src/Avalonia.Base/Animation/Animation.cs
  12. 2 0
      src/Avalonia.Base/ClassBindingManager.cs
  13. 1 2
      src/Avalonia.Base/Controls/IThemeVariantProvider.cs
  14. 1 2
      src/Avalonia.Base/Input/DragEventArgs.cs
  15. 1 2
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  16. 4 8
      src/Avalonia.Base/Input/PointerEventArgs.cs
  17. 1 2
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  18. 4 4
      src/Avalonia.Base/Layout/LayoutManager.cs
  19. 9 2
      src/Avalonia.Base/Media/Color.cs
  20. 2 0
      src/Avalonia.Base/Media/DashStyle.cs
  21. 1 1
      src/Avalonia.Base/Media/GeometryDrawing.cs
  22. 2 0
      src/Avalonia.Base/Media/GradientBrush.cs
  23. 3 33
      src/Avalonia.Base/Media/HslColor.cs
  24. 0 1
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  25. 14 2
      src/Avalonia.Base/Media/PlatformDrawingContext.cs
  26. 2 0
      src/Avalonia.Base/Media/PolyLineSegment.cs
  27. 2 0
      src/Avalonia.Base/Media/TransformGroup.cs
  28. 21 1
      src/Avalonia.Base/Metadata/UnstableAttribute.cs
  29. 31 264
      src/Avalonia.Base/Platform/AssetLoader.cs
  30. 1 1
      src/Avalonia.Base/Platform/IAssetLoader.cs
  31. 0 7
      src/Avalonia.Base/Platform/IDrawingContextImpl.cs
  32. 255 0
      src/Avalonia.Base/Platform/StandardAssetLoader.cs
  33. 1 1
      src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs
  34. 0 5
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  35. 12 2
      src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs
  36. 1 0
      src/Avalonia.Base/StyledElement.cs
  37. 4 0
      src/Avalonia.Base/Styling/ThemeVariant.cs
  38. 23 24
      src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
  39. 1 0
      src/Avalonia.Base/Visual.cs
  40. 2 0
      src/Avalonia.Controls/Application.cs
  41. 2 0
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  42. 1 1
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
  43. 1 1
      src/Avalonia.Controls/ColumnDefinition.cs
  44. 2 0
      src/Avalonia.Controls/ContextMenu.cs
  45. 2 10
      src/Avalonia.Controls/DefinitionBase.cs
  46. 2 0
      src/Avalonia.Controls/Documents/Span.cs
  47. 1 1
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
  48. 2 2
      src/Avalonia.Controls/LayoutTransformControl.cs
  49. 8 0
      src/Avalonia.Controls/ListBox.cs
  50. 0 6
      src/Avalonia.Controls/MenuItem.cs
  51. 6 3
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  52. 1 1
      src/Avalonia.Controls/Primitives/UniformGrid.cs
  53. 3 0
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  54. 74 21
      src/Avalonia.Controls/ProgressBar.cs
  55. 1 0
      src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs
  56. 2 2
      src/Avalonia.Controls/RowDefinition.cs
  57. 38 2
      src/Avalonia.Controls/Screens.cs
  58. 3 3
      src/Avalonia.Controls/SplitView/SplitView.cs
  59. 5 1
      src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs
  60. 2 0
      src/Avalonia.Controls/TabControl.cs
  61. 3 1
      src/Avalonia.Controls/TabItem.cs
  62. 1 1
      src/Avalonia.Controls/ToggleSwitch.cs
  63. 2 2
      src/Avalonia.Controls/TopLevel.cs
  64. 3 3
      src/Avalonia.Controls/Window.cs
  65. 2 2
      src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
  66. 1 0
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  67. 1 0
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  68. 1 0
      src/Avalonia.ReactiveUI/ReactiveWindow.cs
  69. 1 0
      src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
  70. 14 11
      src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml
  71. 16 16
      src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml
  72. 1 0
      src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj
  73. 2 2
      src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml
  74. 1 1
      src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs
  75. 1 6
      src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  76. 2 2
      src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs
  77. 2 2
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  78. 1 1
      src/Headless/Avalonia.Headless/IHeadlessWindow.cs
  79. 2 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  80. 7 0
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  81. 0 9
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  82. 1 4
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  83. 22 0
      src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj
  84. 0 0
      src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs
  85. 11 13
      src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.cs
  86. 8 0
      src/tools/Avalonia.Analyzers/GlobalSuppressions.cs
  87. 59 0
      src/tools/Avalonia.Analyzers/OnPropertyChangedOverrideAnalyzer.cs
  88. 1 1
      src/tools/Avalonia.Generators/GeneratorContextExtensions.cs
  89. 0 17
      src/tools/PublicAnalyzers/Avalonia.Analyzers.csproj
  90. 9 14
      tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs
  91. 33 0
      tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs
  92. 26 6
      tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs
  93. 1 1
      tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
  94. 5 0
      tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
  95. 26 26
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
  96. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs
  97. 16 16
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs
  98. 1 1
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs
  99. 2 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
  100. 1 1
      tests/Avalonia.RenderTests/TestBase.cs

+ 17 - 0
.editorconfig

@@ -186,6 +186,23 @@ csharp_wrap_before_ternary_opsigns = false
 # Avalonia DevAnalyzer preferences
 dotnet_diagnostic.AVADEV2001.severity = error
 
+# Avalonia PublicAnalyzer preferences
+dotnet_diagnostic.AVP1000.severity = error
+dotnet_diagnostic.AVP1001.severity = error
+dotnet_diagnostic.AVP1002.severity = error
+dotnet_diagnostic.AVP1010.severity = error
+dotnet_diagnostic.AVP1011.severity = error
+dotnet_diagnostic.AVP1012.severity = warning
+dotnet_diagnostic.AVP1013.severity = error
+dotnet_diagnostic.AVP1020.severity = error
+dotnet_diagnostic.AVP1021.severity = error
+dotnet_diagnostic.AVP1022.severity = error
+dotnet_diagnostic.AVP1030.severity = error
+dotnet_diagnostic.AVP1031.severity = error
+dotnet_diagnostic.AVP1032.severity = error
+dotnet_diagnostic.AVP1040.severity = error
+dotnet_diagnostic.AVA2001.severity = error
+
 # Xaml files
 [*.{xaml,axaml}]
 indent_size = 2

+ 1 - 1
Avalonia.sln

@@ -233,7 +233,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
 	ProjectSection(SolutionItems) = preProject

+ 1 - 1
build/DevAnalyzers.props

@@ -5,7 +5,7 @@
                       ReferenceOutputAssembly="false"
                       OutputItemType="Analyzer"
                       SetTargetFramework="TargetFramework=netstandard2.0"/>
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj"
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
                       PrivateAssets="all"
                       ReferenceOutputAssembly="false"
                       OutputItemType="Analyzer"

+ 15 - 9
nukebuild/RefAssemblyGenerator.cs

@@ -96,7 +96,7 @@ public class RefAssemblyGenerator
                     | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
             }
 
-            var forceUnstable = type.CustomAttributes.Any(a =>
+            var forceUnstable = type.CustomAttributes.FirstOrDefault(a =>
                 a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
             
             foreach (var m in type.Methods)
@@ -109,22 +109,28 @@ public class RefAssemblyGenerator
         }
     }
 
-    static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force)
+    static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute unstableAttribute)
     {
-        if (!force && (
-            def.HasCustomAttributes == false
-            || def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")))
+        if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
             return;
 
-        if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
+        unstableAttribute = def.CustomAttributes.FirstOrDefault(a =>
+            a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute") ?? unstableAttribute;
+
+        if (unstableAttribute is null)
             return;
 
+        var message = unstableAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
+        if (string.IsNullOrEmpty(message))
+        {
+            message = "This is a part of unstable API and can be changed in minor releases. Consider replacing it with alternatives or reach out developers on GitHub.";
+        }
+        
         def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor)
         {
             ConstructorArguments =
             {
-                new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String,
-                    "This is a part of unstable API and can be changed in minor releases. You have been warned")
+                new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, message)
             }
         });
     }
@@ -168,4 +174,4 @@ public class RefAssemblyGenerator
                 }
         }
     }
-}
+}

+ 5 - 0
nukebuild/numerge.config

@@ -16,6 +16,11 @@
           "Id": "Avalonia.Generators",
           "IgnoreMissingFrameworkBinaries": true,
           "DoNotMergeDependencies": true
+        },
+        {
+          "Id": "Avalonia.Analyzers",
+          "IgnoreMissingFrameworkBinaries": true,
+          "DoNotMergeDependencies": true
         }
       ]
     }

+ 4 - 0
packages/Avalonia/Avalonia.csproj

@@ -16,6 +16,10 @@
                         ReferenceOutputAssembly="false"
                         PrivateAssets="all"
                         OutputItemType="Analyzer" />
+      <ProjectReference Include="..\..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
+                        ReferenceOutputAssembly="false"
+                        PrivateAssets="all"
+                        OutputItemType="Analyzer" />
   </ItemGroup>
 
   <PropertyGroup>

+ 5 - 6
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@@ -19,18 +19,16 @@ namespace ControlCatalog.Pages
         public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
         public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
 
-        public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
+        public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation),
+            coerce: (_, val) => val % (Math.PI * 2));
+
         /// <summary>
         /// Rotation, measured in Radians!
         /// </summary>
         public double Rotation
         {
             get => GetValue(RotationProperty);
-            set
-            {
-                double valueToUse = value % (Math.PI * 2);
-                SetValue(RotationProperty, valueToUse);
-            }
+            set => SetValue(RotationProperty, value);
         }
 
         public static readonly StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
@@ -213,5 +211,6 @@ namespace ControlCatalog.Pages
 
             return workingPoint;
         }
+
     }
 }

+ 1 - 0
samples/IntegrationTestApp/IntegrationTestApp.csproj

@@ -3,6 +3,7 @@
     <OutputType>WinExe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
     <Nullable>enable</Nullable>
+    <NoWarn>$(NoWarn);AVP1012</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup>

+ 2 - 2
samples/RenderDemo/Pages/CustomSkiaPage.cs

@@ -44,9 +44,9 @@ namespace RenderDemo.Pages
             public bool HitTest(Point p) => false;
             public bool Equals(ICustomDrawOperation other) => false;
             static Stopwatch St = Stopwatch.StartNew();
-            public void Render(IDrawingContextImpl context)
+            public void Render(ImmediateDrawingContext context)
             {
-                var leaseFeature = context.GetFeature<ISkiaSharpApiLeaseFeature>();
+                var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
                 if (leaseFeature == null)
                     context.DrawGlyphRun(Brushes.Black, _noSkia.PlatformImpl);
                 else

+ 1 - 1
samples/RenderDemo/Pages/WriteableBitmapPage.cs

@@ -59,7 +59,7 @@ namespace RenderDemo.Pages
                                 color = new Color(fillAlpha, r, g, b);
                             }
 
-                            data[y * fb.Size.Width + x] = (int) color.ToUint32();
+                            data[y * fb.Size.Width + x] = (int) color.ToUInt32();
                         }
                     }
 

+ 7 - 5
src/Avalonia.Base/Animation/Animation.cs

@@ -200,7 +200,7 @@ namespace Avalonia.Animation
         /// </summary>
         /// <param name="setter">The animation setter.</param>
         /// <param name="value">The property animator value.</param>
-        public static void SetAnimator(IAnimationSetter setter, 
+        public static void SetAnimator(IAnimationSetter setter,
             [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
             Type value)
         {
@@ -319,7 +319,7 @@ namespace Avalonia.Animation
             if (animators.Count == 1)
             {
                 var subscription = animators[0].Apply(this, control, clock, match, onComplete);
-                
+
                 if (subscription is not null)
                 {
                     subscriptions.Add(subscription);
@@ -348,9 +348,11 @@ namespace Avalonia.Animation
 
                 if (onComplete != null)
                 {
-                    Task.WhenAll(completionTasks!).ContinueWith(
-                        (_, state) => ((Action)state!).Invoke(),
-                        onComplete);
+                    Task.WhenAll(completionTasks!)
+                        .ContinueWith((_, state) => ((Action)state!).Invoke()
+                            , onComplete
+                            , TaskScheduler.FromCurrentSynchronizationContext()
+                            );
                 }
             }
             return new CompositeDisposable(subscriptions);

+ 2 - 0
src/Avalonia.Base/ClassBindingManager.cs

@@ -17,6 +17,8 @@ namespace Avalonia
             return target.Bind(prop, source, anchor);
         }
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1001:The same AvaloniaProperty should not be registered twice",
+            Justification = "Classes.attr binding feature is implemented using intermediate avalonia properties for each class")]
         private static AvaloniaProperty RegisterClassProxyProperty(string className)
         {
             var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);

+ 1 - 2
src/Avalonia.Base/Controls/IThemeVariantProvider.cs

@@ -10,9 +10,8 @@ namespace Avalonia.Controls;
 /// <remarks>
 /// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions.
 /// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
-/// This API might be removed in the future minor updates.
 /// </remarks>
-[Unstable]
+[Unstable("This XAML-only API might be removed in the future minor updates.")]
 public interface IThemeVariantProvider : IResourceProvider
 {
     /// <summary>

+ 1 - 2
src/Avalonia.Base/Input/DragEventArgs.cs

@@ -25,8 +25,7 @@ namespace Avalonia.Input
             return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0);
         }
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
         public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers)
             : base(routedEvent)
         {

+ 1 - 2
src/Avalonia.Base/Input/PointerDeltaEventArgs.cs

@@ -9,8 +9,7 @@ namespace Avalonia.Input
     {
         public Vector Delta { get; }
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0.")]
+        [Unstable("This constructor might be removed in 12.0.")]
         public PointerDeltaEventArgs(RoutedEvent routedEvent, object? source, 
             IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta) 

+ 4 - 8
src/Avalonia.Base/Input/PointerEventArgs.cs

@@ -14,8 +14,7 @@ namespace Avalonia.Input
         private readonly PointerPointProperties _properties;
         private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
         public PointerEventArgs(RoutedEvent routedEvent,
             object? source,
             IPointer pointer,
@@ -129,8 +128,7 @@ namespace Avalonia.Input
 
     public class PointerPressedEventArgs : PointerEventArgs
     {
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
         public PointerPressedEventArgs(
             object source,
             IPointer pointer,
@@ -150,8 +148,7 @@ namespace Avalonia.Input
 
     public class PointerReleasedEventArgs : PointerEventArgs
     {
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
         public PointerReleasedEventArgs(
             object source, IPointer pointer,
             Visual rootVisual, Point rootVisualPosition, ulong timestamp,
@@ -173,8 +170,7 @@ namespace Avalonia.Input
     {
         public IPointer Pointer { get; }
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")]
+        [Unstable("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")]
         public PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
         {
             Pointer = pointer;

+ 1 - 2
src/Avalonia.Base/Input/PointerWheelEventArgs.cs

@@ -9,8 +9,7 @@ namespace Avalonia.Input
     {
         public Vector Delta { get; }
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow.MouseWheel.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow.MouseWheel.")]
         public PointerWheelEventArgs(object source, IPointer pointer, Visual rootVisual,
             Point rootVisualPosition, ulong timestamp,
             PointerPointProperties properties, KeyModifiers modifiers, Vector delta)

+ 4 - 4
src/Avalonia.Base/Layout/LayoutManager.cs

@@ -249,10 +249,12 @@ namespace Avalonia.Layout
             {
                 var control = _toMeasure.Dequeue();
 
-                if (!control.IsMeasureValid && control.IsAttachedToVisualTree)
+                if (!control.IsMeasureValid)
                 {
                     Measure(control);
                 }
+
+                _toArrange.Enqueue(control);
             }
         }
 
@@ -262,7 +264,7 @@ namespace Avalonia.Layout
             {
                 var control = _toArrange.Dequeue();
 
-                if (!control.IsArrangeValid && control.IsAttachedToVisualTree)
+                if (!control.IsArrangeValid)
                 {
                     Arrange(control);
                 }
@@ -297,8 +299,6 @@ namespace Avalonia.Layout
                 {
                     control.Measure(control.PreviousMeasure.Value);
                 }
-
-                _toArrange.Enqueue(control);
             }
 
             return true;

+ 9 - 2
src/Avalonia.Base/Media/Color.cs

@@ -449,7 +449,7 @@ namespace Avalonia.Media
         /// </returns>
         public override string ToString()
         {
-            uint rgb = ToUint32();
+            uint rgb = ToUInt32();
             return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb.ToString("x8", CultureInfo.InvariantCulture)}";
         }
 
@@ -459,11 +459,18 @@ namespace Avalonia.Media
         /// <returns>
         /// The integer representation of the color.
         /// </returns>
-        public uint ToUint32()
+        public uint ToUInt32()
         {
             return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
         }
 
+        /// <inheritdoc cref="Color.ToUInt32"/>
+        [Obsolete("Use Color.ToUInt32() instead.")]
+        public uint ToUint32()
+        {
+            return ToUInt32();
+        }
+
         /// <summary>
         /// Returns the HSL color model equivalent of this RGB color.
         /// </summary>

+ 2 - 0
src/Avalonia.Base/Media/DashStyle.cs

@@ -44,6 +44,8 @@ namespace Avalonia.Media
         /// </summary>
         /// <param name="dashes">The dashes collection.</param>
         /// <param name="offset">The dash sequence offset.</param>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", 
+            Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
         public DashStyle(IEnumerable<double>? dashes, double offset)
         {
             Dashes = (dashes as AvaloniaList<double>) ?? new AvaloniaList<double>(dashes ?? Array.Empty<double>());

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

@@ -10,7 +10,7 @@ namespace Avalonia.Media
     public class GeometryDrawing : Drawing
     {
         // Adding the Pen's stroke thickness here could yield wrong results due to transforms.
-        private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0);
+        private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUInt32(), 0);
 
         /// <summary>
         /// Defines the <see cref="Geometry"/> property.

+ 2 - 0
src/Avalonia.Base/Media/GradientBrush.cs

@@ -38,6 +38,8 @@ namespace Avalonia.Media
         /// <summary>
         /// Initializes a new instance of the <see cref="GradientBrush"/> class.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", 
+            Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
         public GradientBrush()
         {
             this.GradientStops = new GradientStops();

+ 3 - 33
src/Avalonia.Base/Media/HslColor.cs

@@ -98,43 +98,13 @@ namespace Avalonia.Media
             L = hsl.L;
         }
 
-        /// <summary>
-        /// Gets the Alpha (transparency) component in the range from 0..1 (percentage).
-        /// </summary>
-        /// <remarks>
-        /// <list type="bullet">
-        ///   <item>0 is fully transparent.</item>
-        ///   <item>1 is fully opaque.</item>
-        /// </list>
-        /// </remarks>
+        /// <inheritdoc cref="HsvColor.A"/>
         public double A { get; }
 
-        /// <summary>
-        /// Gets the Hue component in the range from 0..360 (degrees).
-        /// This is the color's location, in degrees, on a color wheel/circle from 0 to 360.
-        /// Note that 360 is equivalent to 0 and will be adjusted automatically.
-        /// </summary>
-        /// <remarks>
-        /// <list type="bullet">
-        ///   <item>0/360 degrees is Red.</item>
-        ///   <item>60 degrees is Yellow.</item>
-        ///   <item>120 degrees is Green.</item>
-        ///   <item>180 degrees is Cyan.</item>
-        ///   <item>240 degrees is Blue.</item>
-        ///   <item>300 degrees is Magenta.</item>
-        /// </list>
-        /// </remarks>
+        /// <inheritdoc cref="HsvColor.H"/>
         public double H { get; }
 
-        /// <summary>
-        /// Gets the Saturation component in the range from 0..1 (percentage).
-        /// </summary>
-        /// <remarks>
-        /// <list type="bullet">
-        ///   <item>0 is a shade of gray (no color).</item>
-        ///   <item>1 is the full color.</item>
-        /// </list>
-        /// </remarks>
+        /// <inheritdoc cref="HsvColor.S"/>
         public double S { get; }
 
         /// <summary>

+ 0 - 1
src/Avalonia.Base/Media/ImmediateDrawingContext.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
 using Avalonia.Threading;
 using Avalonia.Utilities;
 using Avalonia.Media.Imaging;

+ 14 - 2
src/Avalonia.Base/Media/PlatformDrawingContext.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using Avalonia.Logging;
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Immutable;
 using Avalonia.Platform;
@@ -41,8 +42,19 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
         BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) =>
         _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
 
-    public override void Custom(ICustomDrawOperation custom) =>
-        custom.Render(_impl);
+    public override void Custom(ICustomDrawOperation custom)
+    {
+        using var immediateDrawingContext = new ImmediateDrawingContext(_impl, false);
+        try
+        {
+            custom.Render(immediateDrawingContext);
+        }
+        catch (Exception e)
+        {
+            Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
+                ?.Log(custom, $"Exception in {custom.GetType().Name}.{nameof(ICustomDrawOperation.Render)} {{0}}", e);
+        }
+    }
 
     public override void DrawGlyphRun(IBrush? foreground, GlyphRun glyphRun)
     {

+ 2 - 0
src/Avalonia.Base/Media/PolyLineSegment.cs

@@ -28,6 +28,8 @@ namespace Avalonia.Media
         /// <summary>
         /// Initializes a new instance of the <see cref="PolyLineSegment"/> class.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", 
+            Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
         public PolyLineSegment()
         {
             Points = new Points();

+ 2 - 0
src/Avalonia.Base/Media/TransformGroup.cs

@@ -11,6 +11,8 @@ namespace Avalonia.Media
         public static readonly StyledProperty<Transforms> ChildrenProperty =
             AvaloniaProperty.Register<TransformGroup, Transforms>(nameof(Children));
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", 
+            Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
         public TransformGroup()
         {
             Children = new Transforms();

+ 21 - 1
src/Avalonia.Base/Metadata/UnstableAttribute.cs

@@ -1,4 +1,4 @@
-using System;
+using System;
 
 namespace Avalonia.Metadata
 {
@@ -9,5 +9,25 @@ namespace Avalonia.Metadata
     [AttributeUsage(AttributeTargets.All)]
     public sealed class UnstableAttribute : Attribute
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UnstableAttribute"/> class.
+        /// </summary>
+        public UnstableAttribute()
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UnstableAttribute"/> class.
+        /// </summary>
+        /// <param name="message">The text string that describes alternative workarounds.</param>
+        public UnstableAttribute(string? message)
+        {
+            Message = message;
+        }
+
+        /// <summary>
+        /// Gets a value that indicates whether the compiler will treat usage of the obsolete program element as an error.
+        /// </summary>
+        public string? Message { get; }
     }
 }

+ 31 - 264
src/Avalonia.Base/Platform/AssetLoader.cs

@@ -1,281 +1,48 @@
-using System;
+using System;
 using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
 using System.IO;
-using System.Linq;
 using System.Reflection;
-#if !BUILDTASK
-using Avalonia.Platform.Internal;
-using Avalonia.Utilities;
-#endif
 
-namespace Avalonia.Platform
-{
-    /// <summary>
-    /// Loads assets compiled into the application binary.
-    /// </summary>
-    public class AssetLoader
+namespace Avalonia.Platform;
+
 #if !BUILDTASK
-        : IAssetLoader
+/// <inheritdoc cref="IAssetLoader"/>
 #endif
-    {
+public static class AssetLoader
+{
 #if !BUILDTASK
-        private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver();
-
-        private AssemblyDescriptor? _defaultResmAssembly;
-
-        /// <remarks>
-        /// Introduced for tests.
-        /// </remarks>
-        internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) =>
-            s_assemblyDescriptorResolver = resolver;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="AssetLoader"/> class.
-        /// </summary>
-        /// <param name="assembly">
-        /// The default assembly from which to load resm: assets for which no assembly is specified.
-        /// </param>
-        public AssetLoader(Assembly? assembly = null)
-        {
-            if (assembly == null)
-                assembly = Assembly.GetEntryAssembly();
-            if (assembly != null)
-                _defaultResmAssembly = new AssemblyDescriptor(assembly);
-        }
-
-        /// <summary>
-        /// Sets the default assembly from which to load assets for which no assembly is specified.
-        /// </summary>
-        /// <param name="assembly">The default assembly.</param>
-        public void SetDefaultAssembly(Assembly assembly)
-        {
-            _defaultResmAssembly = new AssemblyDescriptor(assembly);
-        }
-
-        /// <summary>
-        /// Checks if an asset with the specified URI exists.
-        /// </summary>
-        /// <param name="uri">The URI.</param>
-        /// <param name="baseUri">
-        /// A base URI to use if <paramref name="uri"/> is relative.
-        /// </param>
-        /// <returns>True if the asset could be found; otherwise false.</returns>
-        public bool Exists(Uri uri, Uri? baseUri = null)
-        {
-            return TryGetAsset(uri, baseUri, out _);
-        }
-
-        /// <summary>
-        /// Opens the asset with the requested URI.
-        /// </summary>
-        /// <param name="uri">The URI.</param>
-        /// <param name="baseUri">
-        /// A base URI to use if <paramref name="uri"/> is relative.
-        /// </param>
-        /// <returns>A stream containing the asset contents.</returns>
-        /// <exception cref="FileNotFoundException">
-        /// The asset could not be found.
-        /// </exception>
-        public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
-
-        /// <summary>
-        /// Opens the asset with the requested URI and returns the asset stream and the
-        /// assembly containing the asset.
-        /// </summary>
-        /// <param name="uri">The URI.</param>
-        /// <param name="baseUri">
-        /// A base URI to use if <paramref name="uri"/> is relative.
-        /// </param>
-        /// <returns>
-        /// The stream containing the resource contents together with the assembly.
-        /// </returns>
-        /// <exception cref="FileNotFoundException">
-        /// The asset could not be found.
-        /// </exception>
-        public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
-        {
-            if (TryGetAsset(uri, baseUri, out var assetDescriptor))
-            {
-                return (assetDescriptor.GetStream(), assetDescriptor.Assembly);
-            }
-
-            throw new FileNotFoundException($"The resource {uri} could not be found.");
-        }
-
-        public Assembly? GetAssembly(Uri uri, Uri? baseUri)
-        {
-            if (!uri.IsAbsoluteUri && baseUri != null)
-            {
-                uri = new Uri(baseUri, uri);
-            }
-
-            if (TryGetAssembly(uri, out var assemblyDescriptor))
-            {
-                return assemblyDescriptor.Assembly;
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Gets all assets of a folder and subfolders that match specified uri.
-        /// </summary>
-        /// <param name="uri">The URI.</param>
-        /// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
-        /// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
-        public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
-        {
-            if (uri.IsAbsoluteResm())
-            {
-                if (!TryGetAssembly(uri, out var assembly))
-                {
-                    assembly = _defaultResmAssembly;
-                }
-
-                return assembly?.Resources?
-                        .Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath()))
-                        .Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
-                    Enumerable.Empty<Uri>();
-            }
-
-            uri = uri.EnsureAbsolute(baseUri);
-
-            if (uri.IsAvares())
-            {
-                if (!TryGetResAsmAndPath(uri, out var assembly, out var path))
-                {
-                    return Enumerable.Empty<Uri>();
-                }
+    private static IAssetLoader GetAssetLoader() => AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
 
-                if (assembly?.AvaloniaResources == null)
-                {
-                    return Enumerable.Empty<Uri>();
-                }
+    /// <inheritdoc cref="IAssetLoader.SetDefaultAssembly"/>
+    public static void SetDefaultAssembly(Assembly assembly) => GetAssetLoader().SetDefaultAssembly(assembly);
 
-                if (path.Length > 0 && path[path.Length - 1] != '/')
-                {
-                    path += '/';
-                }
+    /// <inheritdoc cref="IAssetLoader.Exists"/>
+    public static bool Exists(Uri uri, Uri? baseUri = null) => GetAssetLoader().Exists(uri, baseUri);
 
-                return assembly.AvaloniaResources
-                    .Where(r => r.Key.StartsWith(path, StringComparison.Ordinal))
-                    .Select(x => new Uri($"avares://{assembly.Name}{x.Key}"));
-            }
+    /// <inheritdoc cref="IAssetLoader.Open"/>
+    public static Stream Open(Uri uri, Uri? baseUri = null) => GetAssetLoader().Open(uri, baseUri);
 
-            return Enumerable.Empty<Uri>();
-        }
+    /// <inheritdoc cref="IAssetLoader.OpenAndGetAssembly"/>
+    public static (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
+        => GetAssetLoader().OpenAndGetAssembly(uri, baseUri);
 
-        private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor)
-        {
-            assetDescriptor = null;
+    /// <inheritdoc cref="IAssetLoader.GetAssembly"/>
+    public static Assembly? GetAssembly(Uri uri, Uri? baseUri = null)
+        => GetAssetLoader().GetAssembly(uri, baseUri);
 
-            if (uri.IsAbsoluteResm())
-            {
-                if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly))
-                {
-                    assembly = _defaultResmAssembly;
-                }
-
-                if (assembly?.Resources != null)
-                {
-                    var resourceKey = uri.AbsolutePath;
-
-                    if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            uri = uri.EnsureAbsolute(baseUri);
-
-            if (uri.IsAvares())
-            {
-                if (TryGetResAsmAndPath(uri, out var assembly, out var path))
-                {
-                    if (assembly.AvaloniaResources == null)
-                    {
-                        return false;
-                    }
-
-                    if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        private static bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path)
-        {
-            path = uri.GetUnescapeAbsolutePath();
-
-            if (TryLoadAssembly(uri.Authority, out assembly))
-            {
-                return true;
-            }
-
-            return false;
-        }
-
-        private static bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
-        {
-            assembly = null;
-
-            if (uri != null)
-            {
-                if (!uri.IsAbsoluteUri)
-                {
-                    return false;
-                }
-
-                if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _))
-                {
-                    return true;
-                }
-
-                if (uri.IsResm())
-                {
-                    var assemblyName = uri.GetAssemblyNameFromQuery();
-
-                    if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        private static bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
-        {
-            assembly = null;
-
-            try
-            {
-                assembly = s_assemblyDescriptorResolver.GetAssembly(assemblyName);
-
-                return true;
-            }
-            catch (Exception) { }
-
-            return false;
-        }
+    /// <inheritdoc cref="IAssetLoader.GetAssets"/>
+    public static IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
+        => GetAssetLoader().GetAssets(uri, baseUri);
 #endif
 
-        public static void RegisterResUriParsers()
-        {
-            if (!UriParser.IsKnownScheme("avares"))
-                UriParser.Register(new GenericUriParser(
-                    GenericUriParserOptions.GenericAuthority |
-                    GenericUriParserOptions.NoUserInfo |
-                    GenericUriParserOptions.NoPort |
-                    GenericUriParserOptions.NoQuery |
-                    GenericUriParserOptions.NoFragment), "avares", -1);
-        }
+    internal static void RegisterResUriParsers()
+    {
+        if (!UriParser.IsKnownScheme("avares"))
+            UriParser.Register(new GenericUriParser(
+                GenericUriParserOptions.GenericAuthority |
+                GenericUriParserOptions.NoUserInfo |
+                GenericUriParserOptions.NoPort |
+                GenericUriParserOptions.NoQuery |
+                GenericUriParserOptions.NoFragment), "avares", -1);
     }
 }

+ 1 - 1
src/Avalonia.Base/Platform/IAssetLoader.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Platform
     /// <summary>
     /// Loads assets compiled into the application binary.
     /// </summary>
-    [Unstable]
+    [Unstable("IAssetLoader interface and AvaloniaLocator usage is considered unstable. Please use AssetLoader static class instead.")]
     public interface IAssetLoader
     {
         /// <summary>

+ 0 - 7
src/Avalonia.Base/Platform/IDrawingContextImpl.cs

@@ -1,6 +1,5 @@
 using System;
 using Avalonia.Media;
-using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 using Avalonia.Media.Imaging;
 using Avalonia.Metadata;
@@ -168,12 +167,6 @@ namespace Avalonia.Platform
         /// </summary>
         void PopBitmapBlendMode();
 
-        /// <summary>
-        /// Adds a custom draw operation
-        /// </summary>
-        /// <param name="custom">Custom draw operation</param>
-        void Custom(ICustomDrawOperation custom);
-
         /// <summary>
         /// Attempts to get an optional feature from the drawing context implementation
         /// </summary>

+ 255 - 0
src/Avalonia.Base/Platform/StandardAssetLoader.cs

@@ -0,0 +1,255 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Avalonia.Platform.Internal;
+using Avalonia.Utilities;
+
+namespace Avalonia.Platform;
+
+/// <summary>
+/// Loads assets compiled into the application binary.
+/// </summary>
+internal class StandardAssetLoader : IAssetLoader
+{
+    private readonly IAssemblyDescriptorResolver _assemblyDescriptorResolver;
+    private AssemblyDescriptor? _defaultResmAssembly;
+
+    public StandardAssetLoader(IAssemblyDescriptorResolver resolver, Assembly? assembly = null)
+    {
+        if (assembly == null)
+            assembly = Assembly.GetEntryAssembly();
+        if (assembly != null)
+            _defaultResmAssembly = new AssemblyDescriptor(assembly);
+        _assemblyDescriptorResolver = resolver;
+    }
+
+    public StandardAssetLoader(Assembly? assembly = null) : this(new AssemblyDescriptorResolver(), assembly)
+    {
+            
+    }
+
+    /// <summary>
+    /// Sets the default assembly from which to load assets for which no assembly is specified.
+    /// </summary>
+    /// <param name="assembly">The default assembly.</param>
+    public void SetDefaultAssembly(Assembly assembly)
+    {
+        _defaultResmAssembly = new AssemblyDescriptor(assembly);
+    }
+
+    /// <summary>
+    /// Checks if an asset with the specified URI exists.
+    /// </summary>
+    /// <param name="uri">The URI.</param>
+    /// <param name="baseUri">
+    /// A base URI to use if <paramref name="uri"/> is relative.
+    /// </param>
+    /// <returns>True if the asset could be found; otherwise false.</returns>
+    public bool Exists(Uri uri, Uri? baseUri = null)
+    {
+        return TryGetAsset(uri, baseUri, out _);
+    }
+
+    /// <summary>
+    /// Opens the asset with the requested URI.
+    /// </summary>
+    /// <param name="uri">The URI.</param>
+    /// <param name="baseUri">
+    /// A base URI to use if <paramref name="uri"/> is relative.
+    /// </param>
+    /// <returns>A stream containing the asset contents.</returns>
+    /// <exception cref="FileNotFoundException">
+    /// The asset could not be found.
+    /// </exception>
+    public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
+
+    /// <summary>
+    /// Opens the asset with the requested URI and returns the asset stream and the
+    /// assembly containing the asset.
+    /// </summary>
+    /// <param name="uri">The URI.</param>
+    /// <param name="baseUri">
+    /// A base URI to use if <paramref name="uri"/> is relative.
+    /// </param>
+    /// <returns>
+    /// The stream containing the resource contents together with the assembly.
+    /// </returns>
+    /// <exception cref="FileNotFoundException">
+    /// The asset could not be found.
+    /// </exception>
+    public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
+    {
+        if (TryGetAsset(uri, baseUri, out var assetDescriptor))
+        {
+            return (assetDescriptor.GetStream(), assetDescriptor.Assembly);
+        }
+
+        throw new FileNotFoundException($"The resource {uri} could not be found.");
+    }
+
+    public Assembly? GetAssembly(Uri uri, Uri? baseUri)
+    {
+        if (!uri.IsAbsoluteUri && baseUri != null)
+        {
+            uri = new Uri(baseUri, uri);
+        }
+
+        if (TryGetAssembly(uri, out var assemblyDescriptor))
+        {
+            return assemblyDescriptor.Assembly;
+        }
+
+        return null;
+    }
+
+    /// <summary>
+    /// Gets all assets of a folder and subfolders that match specified uri.
+    /// </summary>
+    /// <param name="uri">The URI.</param>
+    /// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
+    /// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
+    public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
+    {
+        if (uri.IsAbsoluteResm())
+        {
+            if (!TryGetAssembly(uri, out var assembly))
+            {
+                assembly = _defaultResmAssembly;
+            }
+
+            return assembly?.Resources?
+                       .Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath()))
+                       .Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
+                   Enumerable.Empty<Uri>();
+        }
+
+        uri = uri.EnsureAbsolute(baseUri);
+
+        if (uri.IsAvares())
+        {
+            if (!TryGetResAsmAndPath(uri, out var assembly, out var path))
+            {
+                return Enumerable.Empty<Uri>();
+            }
+
+            if (assembly?.AvaloniaResources == null)
+            {
+                return Enumerable.Empty<Uri>();
+            }
+
+            if (path.Length > 0 && path[path.Length - 1] != '/')
+            {
+                path += '/';
+            }
+
+            return assembly.AvaloniaResources
+                .Where(r => r.Key.StartsWith(path, StringComparison.Ordinal))
+                .Select(x => new Uri($"avares://{assembly.Name}{x.Key}"));
+        }
+
+        return Enumerable.Empty<Uri>();
+    }
+
+    private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor)
+    {
+        assetDescriptor = null;
+
+        if (uri.IsAbsoluteResm())
+        {
+            if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly))
+            {
+                assembly = _defaultResmAssembly;
+            }
+
+            if (assembly?.Resources != null)
+            {
+                var resourceKey = uri.AbsolutePath;
+
+                if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor))
+                {
+                    return true;
+                }
+            }
+        }
+
+        uri = uri.EnsureAbsolute(baseUri);
+
+        if (uri.IsAvares())
+        {
+            if (TryGetResAsmAndPath(uri, out var assembly, out var path))
+            {
+                if (assembly.AvaloniaResources == null)
+                {
+                    return false;
+                }
+
+                if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path)
+    {
+        path = uri.GetUnescapeAbsolutePath();
+
+        if (TryLoadAssembly(uri.Authority, out assembly))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    private bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
+    {
+        assembly = null;
+
+        if (uri != null)
+        {
+            if (!uri.IsAbsoluteUri)
+            {
+                return false;
+            }
+
+            if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _))
+            {
+                return true;
+            }
+
+            if (uri.IsResm())
+            {
+                var assemblyName = uri.GetAssemblyNameFromQuery();
+
+                if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly))
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
+    {
+        assembly = null;
+
+        try
+        {
+            assembly = _assemblyDescriptorResolver.GetAssembly(assemblyName);
+
+            return true;
+        }
+        catch (Exception) { }
+
+        return false;
+    }
+}

+ 1 - 1
src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Platform
             AssetLoader.RegisterResUriParsers();
             AvaloniaLocator.CurrentMutable
                 .Bind<IRuntimePlatform>().ToConstant(standardPlatform)
-                .Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly))
+                .Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly))
                 .Bind<IDynamicLibraryLoader>().ToConstant(
 #if NET6_0_OR_GREATER
                     new Net6Loader()

+ 0 - 5
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@@ -143,11 +143,6 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
         _impl.PopBitmapBlendMode();
     }
 
-    public void Custom(ICustomDrawOperation custom)
-    {
-        _impl.Custom(custom);
-    }
-
     public object? GetFeature(Type t) => _impl.GetFeature(t);
     
 

+ 12 - 2
src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs

@@ -1,4 +1,5 @@
 using System;
+using Avalonia.Logging;
 using Avalonia.Media;
 using Avalonia.Platform;
 
@@ -17,7 +18,16 @@ namespace Avalonia.Rendering.SceneGraph
 
         public override void Render(IDrawingContextImpl context)
         {
-            Custom.Render(context);
+            using var immediateDrawingContext = new ImmediateDrawingContext(context, false);
+            try
+            {
+                Custom.Render(immediateDrawingContext);
+            }
+            catch (Exception e)
+            {
+                Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
+                    ?.Log(Custom, $"Exception in {Custom.GetType().Name}.{nameof(ICustomDrawOperation.Render)} {{0}}", e);
+            }
         }
 
         public override void Dispose() => Custom.Dispose();
@@ -48,6 +58,6 @@ namespace Avalonia.Rendering.SceneGraph
         /// Renders the node to a drawing context.
         /// </summary>
         /// <param name="context">The drawing context.</param>
-        void Render(IDrawingContextImpl context);
+        void Render(ImmediateDrawingContext context);
     }
 }

+ 1 - 0
src/Avalonia.Base/StyledElement.cs

@@ -289,6 +289,7 @@ namespace Avalonia
         public StyledElement? Parent { get; private set; }
 
         /// <inheritdoc />
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030:StyledProperty accessors should not have side effects", Justification = "False positive?")]
         public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty);
         
         /// <summary>

+ 4 - 0
src/Avalonia.Base/Styling/ThemeVariant.cs

@@ -9,6 +9,10 @@ namespace Avalonia.Styling;
 /// Specifies a UI theme variant that should be used for the Control and Application types.
 /// </summary>
 [TypeConverter(typeof(ThemeVariantTypeConverter))]
+[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010:AvaloniaProperty objects should be owned by the type in which they are stored",
+    Justification = "ActualThemeVariant and RequestedThemeVariant properties are shared Avalonia.Base and Avalonia.Controls projects," +
+    "but shouldn't be visible on the StyledElement class." +
+    "Ideally we woould introduce readonly styled properties.")]
 public sealed record ThemeVariant
 {
     /// <summary>

+ 23 - 24
src/Avalonia.Base/Threading/Dispatcher.Invoke.cs

@@ -1,5 +1,4 @@
 using System;
-using System.ComponentModel;
 using System.Diagnostics;
 using System.Threading;
 using System.Threading.Tasks;
@@ -118,11 +117,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> synchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; synchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <returns>
     ///     The return value from the delegate being invoked.
@@ -136,11 +135,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> synchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; synchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -156,11 +155,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> synchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; synchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -183,11 +182,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> synchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; synchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -317,11 +316,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> asynchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; asynchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <returns>
     ///     An operation representing the queued delegate to be invoked.
@@ -335,11 +334,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> asynchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; asynchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -355,11 +354,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<TResult> asynchronously on the
+    ///     Executes the specified Func&lt;TResult&gt; asynchronously on the
     ///     thread that the Dispatcher was created on.
     /// </summary>
     /// <param name="callback">
-    ///     A Func<TResult> delegate to invoke through the dispatcher.
+    ///     A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -479,7 +478,7 @@ public partial class Dispatcher
             // operation has already started when the timeout expires,
             // we still wait for it to complete.  This is different
             // than simply waiting on the operation with a timeout
-            // because we are the ones queueing the dispatcher
+            // because we are the ones queuing the dispatcher
             // operation, not the caller.  We can't leave the operation
             // in a state that it might execute if we return that it did not
             // invoke.
@@ -492,12 +491,12 @@ public partial class Dispatcher
 
                 // Old async semantics return from Wait without
                 // throwing an exception if the operation was aborted.
-                // There is no need to test the timout condition, since
+                // There is no need to test the timeout condition, since
                 // the old async semantics would just return the result,
                 // which would be null.
 
                 // This should not block because either the operation
-                // is using the old async sematics, or the operation
+                // is using the old async semantics, or the operation
                 // completed successfully.
                 result = operation.GetResult();
             }
@@ -543,11 +542,11 @@ public partial class Dispatcher
     }
 
     /// <summary>
-    ///     Executes the specified Func<Task> asynchronously on the
+    ///     Executes the specified Func&lt;Task&gt; asynchronously on the
     ///     thread that the Dispatcher was created on
     /// </summary>
     /// <param name="callback">
-    ///     A Func<Task> delegate to invoke through the dispatcher.
+    ///     A Func&lt;Task&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -564,11 +563,11 @@ public partial class Dispatcher
     }
     
     /// <summary>
-    ///     Executes the specified Func<Task<TResult>> asynchronously on the
+    ///     Executes the specified Func&lt;Task&lt;TResult&gt;&gt; asynchronously on the
     ///     thread that the Dispatcher was created on
     /// </summary>
-    /// <param name="callback">
-    ///     A Func<Task<TResult>> delegate to invoke through the dispatcher.
+    /// <param name="action">
+    ///     A Func&lt;Task&lt;TResult&gt;&gt; delegate to invoke through the dispatcher.
     /// </param>
     /// <param name="priority">
     ///     The priority that determines in what order the specified
@@ -595,4 +594,4 @@ public partial class Dispatcher
         _ = action ?? throw new ArgumentNullException(nameof(action));
         InvokeAsyncImpl(new SendOrPostCallbackDispatcherOperation(this, priority, action, arg, true), CancellationToken.None);
     }
-}
+}

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

@@ -329,6 +329,7 @@ namespace Avalonia
         /// <summary>
         /// Gets the control's parent visual.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "GetVisualParent extension method is supposed to be used instead.")]
         internal Visual? VisualParent => _visualParent;
 
         /// <summary>

+ 2 - 0
src/Avalonia.Controls/Application.cs

@@ -94,6 +94,8 @@ namespace Avalonia
         }
         
         /// <inheritdoc />
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
         public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
 
         /// <summary>

+ 2 - 0
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@@ -2042,6 +2042,8 @@ namespace Avalonia.Controls
             /// <summary>
             /// Identifies the Value dependency property.
             /// </summary>
+            [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002:AvaloniaProperty objects should not be owned by a generic type",
+                Justification = "This property is not supposed to be used from XAML.")]
             public static readonly StyledProperty<T> ValueProperty =
                 AvaloniaProperty.Register<BindingEvaluator<T>, T>(nameof(Value));
 

+ 1 - 1
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Controls
 {
     /// <summary>
     /// Specifies how text in the text box portion of the <see cref="AutoCompleteBox" />
-    /// control is used to filter items specified by the <see cref="AutoCompleteBox.Items" />
+    /// control is used to filter items specified by the <see cref="AutoCompleteBox.ItemsSource" />
     /// property for display in the drop-down.
     /// </summary>
     public enum AutoCompleteFilterMode

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

@@ -46,8 +46,8 @@ namespace Avalonia.Controls
         /// <param name="value">The width of the column.</param>
         /// <param name="type">The width unit of the column.</param>
         public ColumnDefinition(double value, GridUnitType type)
+            : this(new GridLength(value, type))
         {
-            Width = new GridLength(value, type);
         }
 
         /// <summary>

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

@@ -56,6 +56,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// Defines the <see cref="Placement"/> property.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
+            Justification = "We keep PlacementModeProperty for backward compatibility.")]
         public static readonly StyledProperty<PlacementMode> PlacementProperty =
             Popup.PlacementProperty.AddOwner<ContextMenu>();
 

+ 2 - 10
src/Avalonia.Controls/DefinitionBase.cs

@@ -37,7 +37,7 @@ namespace Avalonia.Controls
             {
                 //  start with getting SharedSizeGroup value. 
                 //  this property is NOT inherited which should result in better overall perf.
-                if (SharedSizeGroup is { } sharedSizeGroupId && PrivateSharedSizeScope is { } privateSharedSizeScope)
+                if (SharedSizeGroup is { } sharedSizeGroupId && GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
                 {
                     _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
                     _sharedState.AddMember(this);
@@ -333,7 +333,7 @@ namespace Avalonia.Controls
 
                 if (definition._sharedState == null
                     && sharedSizeGroupId != null
-                    && definition.PrivateSharedSizeScope is { } privateSharedSizeScope)
+                    && definition.GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
                 {
                     //  if definition is not registered and both: shared size group id AND private shared scope
                     //  are available, then register definition.
@@ -412,14 +412,6 @@ namespace Avalonia.Controls
             }
         }
 
-        /// <summary>
-        /// Private getter of shared state collection dynamic property.
-        /// </summary>
-        private SharedSizeScope? PrivateSharedSizeScope
-        {
-            get { return GetValue(PrivateSharedSizeScopeProperty); }
-        }
-
         /// <summary>
         /// Convenience accessor to UseSharedMinimum flag
         /// </summary>

+ 2 - 0
src/Avalonia.Controls/Documents/Span.cs

@@ -18,6 +18,8 @@ namespace Avalonia.Controls.Documents
             AvaloniaProperty.Register<Span, InlineCollection>(
                 nameof(Inlines));
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012", 
+            Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
         public Span()
         {
             Inlines = new InlineCollection

+ 1 - 1
src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs

@@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives
         /// Defines the <see cref="OverlayInputPassThroughElement"/> property
         /// </summary>
         public static readonly StyledProperty<IInputElement?> OverlayInputPassThroughElementProperty =
-            Popup.OverlayInputPassThroughElementProperty.AddOwner<FlyoutBase>();
+            Popup.OverlayInputPassThroughElementProperty.AddOwner<PopupFlyoutBase>();
         
         private readonly Lazy<Popup> _popupLazy;
         private Rect? _enlargedPopupRect;

+ 2 - 2
src/Avalonia.Controls/LayoutTransformControl.cs

@@ -63,7 +63,7 @@ namespace Avalonia.Controls
         {
             if (TransformRoot == null || LayoutTransform == null)
             {
-                LayoutTransform = RenderTransform;
+                SetCurrentValue(LayoutTransformProperty, RenderTransform);
                 return base.ArrangeOverride(finalSize);
             }
 
@@ -176,7 +176,7 @@ namespace Avalonia.Controls
                 else
                 {
                     _renderTransformChangedEvent?.Dispose();
-                    LayoutTransform = null;
+                    ClearValue(LayoutTransformProperty);
                 }
             }
         }

+ 8 - 0
src/Avalonia.Controls/ListBox.cs

@@ -29,18 +29,24 @@ namespace Avalonia.Controls
         /// <summary>
         /// Defines the <see cref="SelectedItems"/> property.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+            Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
         public static readonly new DirectProperty<SelectingItemsControl, IList?> SelectedItemsProperty =
             SelectingItemsControl.SelectedItemsProperty;
 
         /// <summary>
         /// Defines the <see cref="Selection"/> property.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+            Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
         public static readonly new DirectProperty<SelectingItemsControl, ISelectionModel> SelectionProperty =
             SelectingItemsControl.SelectionProperty;
 
         /// <summary>
         /// Defines the <see cref="SelectionMode"/> property.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+            Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
         public static readonly new StyledProperty<SelectionMode> SelectionModeProperty = 
             SelectingItemsControl.SelectionModeProperty;
 
@@ -84,6 +90,8 @@ namespace Avalonia.Controls
         /// Note that the selection mode only applies to selections made via user interaction.
         /// Multiple selections can be made programmatically regardless of the value of this property.
         /// </remarks>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+            Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
         public new SelectionMode SelectionMode
         {
             get { return base.SelectionMode; }

+ 0 - 6
src/Avalonia.Controls/MenuItem.cs

@@ -53,12 +53,6 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<KeyGesture?> InputGestureProperty =
             AvaloniaProperty.Register<MenuItem, KeyGesture?>(nameof(InputGesture));
 
-        /// <summary>
-        /// Defines the <see cref="IsSelected"/> property.
-        /// </summary>
-        public static readonly StyledProperty<bool> IsSelectedProperty =
-            SelectingItemsControl.IsSelectedProperty.AddOwner<MenuItem>();
-
         /// <summary>
         /// Defines the <see cref="IsSubMenuOpen"/> property.
         /// </summary>

+ 6 - 3
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@@ -1,5 +1,5 @@
 using System;
-
+using Avalonia.Collections;
 using Avalonia.Controls.Documents;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Primitives;
@@ -442,7 +442,7 @@ namespace Avalonia.Controls.Presenters
             var contentTemplate = ContentTemplate;
             var oldChild = Child;
             var newChild = CreateChild(content, oldChild, contentTemplate);
-            var logicalChildren = Host?.LogicalChildren ?? LogicalChildren;
+            var logicalChildren = GetEffectiveLogicalChildren();
 
             // Remove the old child if we're not recycling it.
             if (newChild != oldChild)
@@ -488,6 +488,9 @@ namespace Avalonia.Controls.Presenters
 
         }
 
+        private IAvaloniaList<ILogical> GetEffectiveLogicalChildren()
+            => Host?.LogicalChildren ?? LogicalChildren;
+
         /// <inheritdoc/>
         protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
         {
@@ -692,7 +695,7 @@ namespace Avalonia.Controls.Presenters
             else if (Child != null)
             {
                 VisualChildren.Remove(Child);
-                LogicalChildren.Remove(Child);
+                GetEffectiveLogicalChildren().Remove(Child);
                 ((ISetInheritanceParent)Child).SetParent(Child.Parent);
                 Child = null;
                 _recyclingDataTemplate = null;

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

@@ -123,7 +123,7 @@ namespace Avalonia.Controls.Primitives
 
             if (FirstColumn >= Columns)
             {
-                FirstColumn = 0;
+                SetCurrentValue(FirstColumnProperty, 0);
             }
 
             var itemCount = FirstColumn;

+ 3 - 0
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@@ -29,6 +29,9 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030")]
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031",
+            Justification = "A hack to make ChromeOverlayLayer lazily creatable. It is expected that GetValue(ChromeOverlayLayerProperty) alone won't work.")]
         public ChromeOverlayLayer ChromeOverlayLayer
         {
             get

+ 74 - 21
src/Avalonia.Controls/ProgressBar.cs

@@ -17,7 +17,13 @@ namespace Avalonia.Controls
     [PseudoClasses(":vertical", ":horizontal", ":indeterminate")]
     public class ProgressBar : RangeBase
     {
-        public class ProgressBarTemplateProperties : AvaloniaObject
+        /// <summary>
+        /// Provides calculated values for use with the <see cref="ProgressBar"/>'s control theme or template.
+        /// </summary>
+        /// <remarks>
+        /// This class is NOT intended for general use outside of control templates.
+        /// </remarks>
+        public class ProgressBarTemplateSettings : AvaloniaObject
         {
             private double _container2Width;
             private double _containerWidth;
@@ -26,38 +32,38 @@ namespace Avalonia.Controls
             private double _container2AnimationStartPosition;
             private double _container2AnimationEndPosition;
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerAnimationStartPositionProperty =
-           AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerAnimationStartPositionProperty =
+           AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                nameof(ContainerAnimationStartPosition),
                p => p.ContainerAnimationStartPosition,
                (p, o) => p.ContainerAnimationStartPosition = o, 0d);
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerAnimationEndPositionProperty =
-                AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerAnimationEndPositionProperty =
+                AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                     nameof(ContainerAnimationEndPosition),
                     p => p.ContainerAnimationEndPosition,
                     (p, o) => p.ContainerAnimationEndPosition = o, 0d);
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2AnimationStartPositionProperty =
-                AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2AnimationStartPositionProperty =
+                AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                     nameof(Container2AnimationStartPosition),
                     p => p.Container2AnimationStartPosition,
                     (p, o) => p.Container2AnimationStartPosition = o, 0d);
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2AnimationEndPositionProperty =
-                AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2AnimationEndPositionProperty =
+                AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                     nameof(Container2AnimationEndPosition),
                     p => p.Container2AnimationEndPosition,
                     (p, o) => p.Container2AnimationEndPosition = o);
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2WidthProperty =
-                AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2WidthProperty =
+                AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                     nameof(Container2Width),
                     p => p.Container2Width,
                     (p, o) => p.Container2Width = o);
 
-            public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerWidthProperty =
-                AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>(
+            public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerWidthProperty =
+                AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
                     nameof(ContainerWidth),
                     p => p.ContainerWidth,
                     (p, o) => p.ContainerWidth = o);
@@ -103,29 +109,57 @@ namespace Avalonia.Controls
         private Border? _indicator;
         private IDisposable? _trackSizeChangedListener;
 
+        /// <summary>
+        /// Defines the <see cref="IsIndeterminate"/> property.
+        /// </summary>
         public static readonly StyledProperty<bool> IsIndeterminateProperty =
             AvaloniaProperty.Register<ProgressBar, bool>(nameof(IsIndeterminate));
 
+        /// <summary>
+        /// Defines the <see cref="ShowProgressText"/> property.
+        /// </summary>
         public static readonly StyledProperty<bool> ShowProgressTextProperty =
             AvaloniaProperty.Register<ProgressBar, bool>(nameof(ShowProgressText));
 
+        /// <summary>
+        /// Defines the <see cref="ProgressTextFormat"/> property.
+        /// </summary>
         public static readonly StyledProperty<string> ProgressTextFormatProperty =
             AvaloniaProperty.Register<ProgressBar, string>(nameof(ProgressTextFormat), "{1:0}%");
 
+        /// <summary>
+        /// Defines the <see cref="Orientation"/> property.
+        /// </summary>
         public static readonly StyledProperty<Orientation> OrientationProperty =
             AvaloniaProperty.Register<ProgressBar, Orientation>(nameof(Orientation), Orientation.Horizontal);
 
+        /// <summary>
+        /// Defines the <see cref="Percentage"/> property.
+        /// </summary>
         public static readonly DirectProperty<ProgressBar, double> PercentageProperty =
             AvaloniaProperty.RegisterDirect<ProgressBar, double>(
                 nameof(Percentage),
                 o => o.Percentage);
 
+        /// <summary>
+        /// Defines the <see cref="IndeterminateStartingOffset"/> property.
+        /// </summary>
         public static readonly StyledProperty<double> IndeterminateStartingOffsetProperty =
             AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateStartingOffset));
 
+        /// <summary>
+        /// Defines the <see cref="IndeterminateEndingOffset"/> property.
+        /// </summary>
         public static readonly StyledProperty<double> IndeterminateEndingOffsetProperty =
             AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateEndingOffset));
 
+        /// <summary>
+        /// Gets the overall percentage complete of the progress 
+        /// </summary>
+        /// <remarks>
+        /// This read-only property is automatically calculated using the current <see cref="RangeBase.Value"/> and
+        /// the effective range (<see cref="RangeBase.Maximum"/> - <see cref="RangeBase.Minimum"/>).
+        /// </remarks>
         public double Percentage
         {
             get { return _percentage; }
@@ -154,31 +188,50 @@ namespace Avalonia.Controls
             OrientationProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
         }
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ProgressBar"/> class.
+        /// </summary>
         public ProgressBar()
         {
             UpdatePseudoClasses(IsIndeterminate, Orientation);
         }
 
-        public ProgressBarTemplateProperties TemplateProperties { get; } = new ProgressBarTemplateProperties();
+        /// <summary>
+        /// Gets or sets the TemplateSettings for the <see cref="ProgressBar"/>.
+        /// </summary>
+        public ProgressBarTemplateSettings TemplateSettings { get; } = new ProgressBarTemplateSettings();
 
+        /// <summary>
+        /// Gets or sets a value indicating whether the progress bar shows the actual value or a generic,
+        /// continues progress indicator (indeterminate state).
+        /// </summary>
         public bool IsIndeterminate
         {
             get => GetValue(IsIndeterminateProperty);
             set => SetValue(IsIndeterminateProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether progress text will be shown.
+        /// </summary>
         public bool ShowProgressText
         {
             get => GetValue(ShowProgressTextProperty);
             set => SetValue(ShowProgressTextProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets the format string applied to the internally calculated progress text before it is shown.
+        /// </summary>
         public string ProgressTextFormat
         {
             get => GetValue(ProgressTextFormatProperty);
             set => SetValue(ProgressTextFormatProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets the orientation of the <see cref="ProgressBar"/>.
+        /// </summary>
         public Orientation Orientation
         {
             get => GetValue(OrientationProperty);
@@ -193,6 +246,7 @@ namespace Avalonia.Controls
             return result;
         }
 
+        /// <inheritdoc/>
         protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
         {
             base.OnPropertyChanged(change);
@@ -242,15 +296,14 @@ namespace Avalonia.Controls
                     var barIndicatorWidth = dim * 0.4; // Indicator width at 40% of ProgressBar
                     var barIndicatorWidth2 = dim * 0.6; // Indicator width at 60% of ProgressBar
 
-                    TemplateProperties.ContainerWidth = barIndicatorWidth;
-                    TemplateProperties.Container2Width = barIndicatorWidth2;
-
-                    TemplateProperties.ContainerAnimationStartPosition = barIndicatorWidth * -1.8; // Position at -180%
-                    TemplateProperties.ContainerAnimationEndPosition = barIndicatorWidth * 3.0; // Position at 300%
+                    TemplateSettings.ContainerWidth = barIndicatorWidth;
+                    TemplateSettings.Container2Width = barIndicatorWidth2;
 
-                    TemplateProperties.Container2AnimationStartPosition = barIndicatorWidth2 * -1.5; // Position at -150%
-                    TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
+                    TemplateSettings.ContainerAnimationStartPosition = barIndicatorWidth * -1.8; // Position at -180%
+                    TemplateSettings.ContainerAnimationEndPosition = barIndicatorWidth * 3.0; // Position at 300%
 
+                    TemplateSettings.Container2AnimationStartPosition = barIndicatorWidth2 * -1.5; // Position at -150%
+                    TemplateSettings.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
 
                     // Remove these properties when we switch to fluent as default and removed the old one.
                     SetCurrentValue(IndeterminateStartingOffsetProperty,-dim);

+ 1 - 0
src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs

@@ -71,6 +71,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets or sets a value that indicates the refresh state of the visualizer.
         /// </summary>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "False positive")]
         protected RefreshVisualizerState RefreshVisualizerState
         {
             get

+ 2 - 2
src/Avalonia.Controls/RowDefinition.cs

@@ -46,8 +46,8 @@ namespace Avalonia.Controls
         /// <param name="value">The height of the row.</param>
         /// <param name="type">The height unit of the column.</param>
         public RowDefinition(double value, GridUnitType type)
+            : this(new GridLength(value, type))
         {
-            Height = new GridLength(value, type);
         }
 
         /// <summary>
@@ -56,7 +56,7 @@ namespace Avalonia.Controls
         /// <param name="height">The height of the column.</param>
         public RowDefinition(GridLength height)
         {
-            Height = height;
+            SetCurrentValue(HeightProperty, height);
         }
 
         /// <summary>

+ 38 - 2
src/Avalonia.Controls/Screens.cs

@@ -1,8 +1,7 @@
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using Avalonia.Platform;
-using Avalonia.VisualTree;
 
 #nullable enable
 
@@ -38,21 +37,58 @@ namespace Avalonia.Controls
             _iScreenImpl = iScreenImpl;
         }
 
+        /// <summary>
+        /// Retrieves a Screen for the display that contains the rectangle.
+        /// </summary>
+        /// <param name="bounds">Bounds that specifies the area for which to retrieve the display.</param>
+        /// <returns>The <see cref="Screen"/>.</returns>
         public Screen? ScreenFromBounds(PixelRect bounds)
         {
             return _iScreenImpl.ScreenFromRect(bounds);
         }
 
+        /// <summary>
+        /// Retrieves a Screen for the display that contains the specified <see cref="WindowBase"/>.
+        /// </summary>
+        /// <param name="window">The window for which to retrieve the Screen.</param>
+        /// <exception cref="ObjectDisposedException">Window platform implementation was already disposed.</exception>
+        /// <returns>The <see cref="Screen"/>.</returns>
+        public Screen? ScreenFromWindow(WindowBase window)
+        {
+            if (window.PlatformImpl is null)
+            {
+                throw new ObjectDisposedException("Window platform implementation was already disposed.");
+            }
+
+            return _iScreenImpl.ScreenFromWindow(window.PlatformImpl);
+        }
+
+        /// <summary>
+        /// Retrieves a Screen for the display that contains the specified <see cref="IWindowBaseImpl"/>.
+        /// </summary>
+        /// <param name="window">The window impl for which to retrieve the Screen.</param>
+        /// <returns>The <see cref="Screen"/>.</returns>
+        [Obsolete("Use ScreenFromWindow(WindowBase) overload.")]
         public Screen? ScreenFromWindow(IWindowBaseImpl window)
         {
             return _iScreenImpl.ScreenFromWindow(window);
         }
 
+        /// <summary>
+        /// Retrieves a Screen for the display that contains the specified point.
+        /// </summary>
+        /// <param name="point">A Point that specifies the location for which to retrieve a Screen.</param>
+        /// <returns>The <see cref="Screen"/>.</returns>
         public Screen? ScreenFromPoint(PixelPoint point)
         {
             return _iScreenImpl.ScreenFromPoint(point);
         }
 
+        /// <summary>
+        /// Retrieves a Screen for the display that contains the specified <see cref="Visual"/>.
+        /// </summary>
+        /// <param name="visual">A Visual for which to retrieve a Screen.</param>
+        /// <returns>The <see cref="Screen"/>.</returns>
         public Screen? ScreenFromVisual(Visual visual)
         {
             var tl = visual.PointToScreen(visual.Bounds.TopLeft);

+ 3 - 3
src/Avalonia.Controls/SplitView/SplitView.cs

@@ -217,8 +217,8 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled
         /// <para>When enabled, and the pane is open in Overlay or CompactOverlay mode,
-        /// the contents of the splitview are darkened to visually separate the open pane
-        /// and the rest of the SplitView</para>
+        /// the contents of the <see cref="SplitView"/> are darkened to visually separate the open pane
+        /// and the rest of the <see cref="SplitView"/>.</para>
         /// </summary>
         public bool UseLightDismissOverlayMode
         {
@@ -227,7 +227,7 @@ namespace Avalonia.Controls
         }
 
         /// <summary>
-        /// Gets or sets the TemplateSettings for the SplitView
+        /// Gets or sets the TemplateSettings for the <see cref="SplitView"/>.
         /// </summary>
         public SplitViewTemplateSettings TemplateSettings
         {

+ 5 - 1
src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs

@@ -2,8 +2,10 @@
 {
     /// <summary>
     /// Provides calculated values for use with the <see cref="SplitView"/>'s control theme or template.
-    /// This class is NOT intended for general use.
     /// </summary>
+    /// <remarks>
+    /// This class is NOT intended for general use outside of control templates.
+    /// </remarks>
     public class SplitViewTemplateSettings : AvaloniaObject
     {
         internal SplitViewTemplateSettings() { }
@@ -17,12 +19,14 @@
             AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>(
                 nameof(PaneColumnGridLength));
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
         public double ClosedPaneWidth
         {
             get => GetValue(ClosedPaneWidthProperty);
             internal set => SetValue(ClosedPaneWidthProperty, value);
         }
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
         public GridLength PaneColumnGridLength
         {
             get => GetValue(PaneColumnGridLengthProperty);

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

@@ -115,6 +115,7 @@ namespace Avalonia.Controls
         /// <value>
         /// The content of the selected tab.
         /// </value>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
         public object? SelectedContent
         {
             get { return GetValue(SelectedContentProperty); }
@@ -127,6 +128,7 @@ namespace Avalonia.Controls
         /// <value>
         /// The content template of the selected tab.
         /// </value>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
         public IDataTemplate? SelectedContentTemplate
         {
             get { return GetValue(SelectedContentTemplateProperty); }

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

@@ -42,6 +42,8 @@ namespace Avalonia.Controls
         /// <value>
         /// The tab strip placement.
         /// </value>
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031",
+            Justification = "This property is supposed to be inherited only and settable on parent TabControl.")]
         public Dock TabStripPlacement
         {
             get { return GetValue(TabStripPlacementProperty); }
@@ -83,7 +85,7 @@ namespace Avalonia.Controls
                 {
                     Header = obj.NewValue;
                 }
-            }          
+            }
         }
     }
 }

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

@@ -201,7 +201,7 @@ namespace Avalonia.Controls
                 }
                 else
                 {
-                    IsChecked = shouldBecomeChecked;
+                    SetCurrentValue(IsCheckedProperty, shouldBecomeChecked);
                 }
             }
             else

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

@@ -83,11 +83,11 @@ namespace Avalonia.Controls
 
         /// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
         public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
-            ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
+            ThemeVariantScope.ActualThemeVariantProperty.AddOwner<TopLevel>();
         
         /// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
         public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
-            ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
+            ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<TopLevel>();
 
         /// <summary>
         /// Defines the SystemBarColor attached property.

+ 3 - 3
src/Avalonia.Controls/Window.cs

@@ -713,7 +713,7 @@ namespace Avalonia.Controls
                 Owner = owner;
                 owner?.AddChild(this, false);
 
-                SetWindowStartupLocation(owner?.PlatformImpl);
+                SetWindowStartupLocation(owner);
 
                 PlatformImpl?.Show(ShowActivated, false);
                 Renderer.Start();
@@ -789,7 +789,7 @@ namespace Avalonia.Controls
                 Owner = owner;
                 owner.AddChild(this, true);
 
-                SetWindowStartupLocation(owner.PlatformImpl);
+                SetWindowStartupLocation(owner);
 
                 PlatformImpl?.Show(ShowActivated, true);
 
@@ -870,7 +870,7 @@ namespace Avalonia.Controls
             }
         }
 
-        private void SetWindowStartupLocation(IWindowBaseImpl? owner = null)
+        private void SetWindowStartupLocation(Window? owner = null)
         {
             var startupLocation = WindowStartupLocation;
 

+ 2 - 2
src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs

@@ -37,7 +37,7 @@ namespace Avalonia.Diagnostics.Controls
                 _ => null
             };
 
-            RequestedThemeVariant = application.RequestedThemeVariant;
+            SetCurrentValue(RequestedThemeVariantProperty, application.RequestedThemeVariant);
             _application.PropertyChanged += ApplicationOnPropertyChanged;
         }
 
@@ -131,7 +131,7 @@ namespace Avalonia.Diagnostics.Controls
         {
             if (e.Property == Avalonia.Application.RequestedThemeVariantProperty)
             {
-                RequestedThemeVariant = e.GetNewValue<ThemeVariant>();
+                SetCurrentValue(RequestedThemeVariantProperty, e.GetNewValue<ThemeVariant>());
             }
         }
 

+ 1 - 0
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@@ -12,4 +12,5 @@
   <Import Project="..\..\build\ApiDiff.props" />
   <Import Project="..\..\build\NullableEnable.props" />
   <Import Project="..\..\build\TrimmingEnable.props" />
+  <Import Project="..\..\build\DevAnalyzers.props" />
 </Project>

+ 1 - 0
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@@ -17,6 +17,7 @@ namespace Avalonia.ReactiveUI
     /// <typeparam name="TViewModel">ViewModel type.</typeparam>
     public class ReactiveUserControl<TViewModel> : UserControl, IViewFor<TViewModel> where TViewModel : class
     {
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")]
         public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty
             .Register<ReactiveUserControl<TViewModel>, TViewModel?>(nameof(ViewModel));
 

+ 1 - 0
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@@ -17,6 +17,7 @@ namespace Avalonia.ReactiveUI
     /// <typeparam name="TViewModel">ViewModel type.</typeparam>
     public class ReactiveWindow<TViewModel> : Window, IViewFor<TViewModel> where TViewModel : class
     {
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002", Justification = "Generic avalonia property is expected here.")]
         public static readonly StyledProperty<TViewModel?> ViewModelProperty = AvaloniaProperty
             .Register<ReactiveWindow<TViewModel>, TViewModel?>(nameof(ViewModel));
 

+ 1 - 0
src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj

@@ -15,4 +15,5 @@
   <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\ApiDiff.props" />
   <Import Project="..\..\build\TrimmingEnable.props" />
+  <Import Project="..\..\build\DevAnalyzers.props" />
 </Project>

+ 14 - 11
src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml

@@ -1,18 +1,21 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
-                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:generic="using:System.Collections.Generic">
   <Design.PreviewWith>
     <Border Padding="20">
       <AutoCompleteBox Width="200">
-        <AutoCompleteBox.Items>
-          Alabama
-          Alaska
-          Arizona
-          Arkansas
-          California
-          Colorado
-          Connecticut
-          Delaware
-        </AutoCompleteBox.Items>
+        <AutoCompleteBox.ItemsSource>
+          <generic:List x:TypeArguments="x:String">
+            Alabama
+            Alaska
+            Arizona
+            Arkansas
+            California
+            Colorado
+            Connecticut
+            Delaware
+          </generic:List>
+        </AutoCompleteBox.ItemsSource>
       </AutoCompleteBox>
     </Border>
   </Design.PreviewWith>

+ 16 - 16
src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml

@@ -125,13 +125,13 @@
       <Style.Animations>
         <Animation IterationCount="Infinite" Duration="0:0:2">
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationStartPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.5">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationEndPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationEndPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:2">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationEndPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationEndPosition}" />
           </KeyFrame>
         </Animation>
       </Style.Animations>
@@ -140,13 +140,13 @@
       <Style.Animations>
         <Animation IterationCount="Infinite" Duration="0:0:2">
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationStartPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0.75">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationStartPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:2">
-            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationEndPosition}" />
+            <Setter Property="TranslateTransform.X" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationEndPosition}" />
           </KeyFrame>
         </Animation>
       </Style.Animations>
@@ -155,13 +155,13 @@
       <Style.Animations>
         <Animation IterationCount="Infinite" Duration="0:0:2">
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationStartPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.5">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationEndPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationEndPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:2">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerAnimationEndPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerAnimationEndPosition}" />
           </KeyFrame>
         </Animation>
       </Style.Animations>
@@ -170,28 +170,28 @@
       <Style.Animations>
         <Animation IterationCount="Infinite" Duration="0:0:2">
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationStartPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:0.75">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationStartPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationStartPosition}" />
           </KeyFrame>
           <KeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:2">
-            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2AnimationEndPosition}" />
+            <Setter Property="TranslateTransform.Y" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2AnimationEndPosition}" />
           </KeyFrame>
         </Animation>
       </Style.Animations>
     </Style>
     <Style Selector="^:horizontal /template/ Border#IndeterminateProgressBarIndicator">
-      <Setter Property="Width" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerWidth}" />
+      <Setter Property="Width" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerWidth}" />
     </Style>
     <Style Selector="^:horizontal /template/ Border#IndeterminateProgressBarIndicator2">
-      <Setter Property="Width" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2Width}" />
+      <Setter Property="Width" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2Width}" />
     </Style>
     <Style Selector="^:vertical /template/ Border#IndeterminateProgressBarIndicator">
-      <Setter Property="Height" Value="{Binding $parent[ProgressBar].TemplateProperties.ContainerWidth}" />
+      <Setter Property="Height" Value="{Binding $parent[ProgressBar].TemplateSettings.ContainerWidth}" />
     </Style>
     <Style Selector="^:vertical /template/ Border#IndeterminateProgressBarIndicator2">
-      <Setter Property="Height" Value="{Binding $parent[ProgressBar].TemplateProperties.Container2Width}" />
+      <Setter Property="Height" Value="{Binding $parent[ProgressBar].TemplateSettings.Container2Width}" />
     </Style>
   </ControlTheme>
 </ResourceDictionary>

+ 1 - 0
src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj

@@ -15,4 +15,5 @@
   <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\ApiDiff.props" />
   <Import Project="..\..\build\TrimmingEnable.props" />
+  <Import Project="..\..\build\DevAnalyzers.props" />
 </Project>

+ 2 - 2
src/Avalonia.Themes.Simple/Controls/ProgressBar.xaml

@@ -93,7 +93,7 @@
           </KeyFrame>
         </Animation>
       </Style.Animations>
-      <Setter Property="Width" Value="{Binding TemplateProperties.ContainerWidth, RelativeSource={RelativeSource TemplatedParent}}" />
+      <Setter Property="Width" Value="{Binding TemplateSettings.ContainerWidth, RelativeSource={RelativeSource TemplatedParent}}" />
     </Style>
     <Style Selector="^:vertical:indeterminate /template/ Border#PART_IndeterminateIndicator">
       <Style.Animations>
@@ -108,7 +108,7 @@
           </KeyFrame>
         </Animation>
       </Style.Animations>
-      <Setter Property="Height" Value="{Binding TemplateProperties.ContainerWidth, RelativeSource={RelativeSource TemplatedParent}}" />
+      <Setter Property="Height" Value="{Binding TemplateSettings.ContainerWidth, RelativeSource={RelativeSource TemplatedParent}}" />
     </Style>
   </ControlTheme>
 </ResourceDictionary>

+ 1 - 1
src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs

@@ -37,7 +37,7 @@ namespace Avalonia.Browser
                 return false;
             }
 
-            public void Render(IDrawingContextImpl context)
+            public void Render(ImmediateDrawingContext context)
             {
                 _hasRendered = true;
                 _onFirstRender();

+ 1 - 6
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@@ -433,12 +433,7 @@ namespace Avalonia.Headless
             {
                 
             }
-
-            public void Custom(ICustomDrawOperation custom)
-            {
-
-            }
-
+            
             public object? GetFeature(Type t)
             {
                 return null;

+ 2 - 2
src/Headless/Avalonia.Headless/HeadlessWindowExtensions.cs

@@ -17,7 +17,7 @@ public static class HeadlessWindowExtensions
     /// Triggers a renderer timer tick and captures last rendered frame.
     /// </summary>
     /// <returns>Bitmap with last rendered frame. Null, if nothing was rendered.</returns>
-    public static Bitmap? CaptureRenderedFrame(this TopLevel topLevel)
+    public static WriteableBitmap? CaptureRenderedFrame(this TopLevel topLevel)
     {
         Dispatcher.UIThread.RunJobs();
         AvaloniaHeadlessPlatform.ForceRenderTimerTick();
@@ -29,7 +29,7 @@ public static class HeadlessWindowExtensions
     /// Note, in order to trigger rendering timer, call <see cref="AvaloniaHeadlessPlatform.ForceRenderTimerTick"/> method.  
     /// </summary>
     /// <returns>Bitmap with last rendered frame. Null, if nothing was rendered.</returns>
-    public static Bitmap? GetLastRenderedFrame(this TopLevel topLevel)
+    public static WriteableBitmap? GetLastRenderedFrame(this TopLevel topLevel)
     {
         if (AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is HeadlessPlatformRenderInterface)
         {

+ 2 - 2
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@@ -214,7 +214,7 @@ namespace Avalonia.Headless
             });
         }
 
-        public Bitmap? GetLastRenderedFrame()
+        public WriteableBitmap? GetLastRenderedFrame()
         {
             lock (_sync)
             {
@@ -224,7 +224,7 @@ namespace Avalonia.Headless
                 }
 
                 using var lockedFramebuffer = _lastRenderedFrame.Lock();
-                return new Bitmap(lockedFramebuffer.Format, AlphaFormat.Opaque, lockedFramebuffer.Address,
+                return new WriteableBitmap(lockedFramebuffer.Format, AlphaFormat.Opaque, lockedFramebuffer.Address,
                     lockedFramebuffer.Size, lockedFramebuffer.Dpi, lockedFramebuffer.RowBytes);
             }
         }

+ 1 - 1
src/Headless/Avalonia.Headless/IHeadlessWindow.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Headless
 {
     internal interface IHeadlessWindow
     {
-        Bitmap? GetLastRenderedFrame();
+        WriteableBitmap? GetLastRenderedFrame();
         void KeyPress(Key key, RawInputModifiers modifiers);
         void KeyRelease(Key key, RawInputModifiers modifiers);
         void MouseDown(Point point, MouseButton button, RawInputModifiers modifiers = RawInputModifiers.None);

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@@ -155,7 +155,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 result = new XamlStaticOrTargetedReturnMethodCallNode(node,
                     type.GetMethod(
                         new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }),
-                    new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
+                    new[] { new XamlConstantNode(node, types.UInt, color.ToUInt32()) });
 
                 return true;
             }
@@ -242,7 +242,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
 
                     result = new XamlAstNewClrObjectNode(node, brushTypeRef,
                         types.ImmutableSolidColorBrushConstructorColor,
-                        new List<IXamlAstValueNode> { new XamlConstantNode(node, types.UInt, color.ToUint32()) });
+                        new List<IXamlAstValueNode> { new XamlConstantNode(node, types.UInt, color.ToUInt32()) });
 
                     return true;
                 }

+ 7 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -50,6 +50,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
             private readonly IServiceProvider? _parentProvider;
             private readonly List<IResourceNode>? _parentResourceNodes;
             private readonly INameScope _nameScope;
+            private IRuntimePlatform? _runtimePlatform;
 
             public DeferredParentServiceProvider(IServiceProvider? parentProvider, List<IResourceNode>? parentResourceNodes,
                 object rootObject, INameScope nameScope)
@@ -80,6 +81,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
                     return this;
                 if (serviceType == typeof(IAvaloniaXamlIlControlTemplateProvider))
                     return this;
+                if (serviceType == typeof(IRuntimePlatform))
+                {
+                    if(_runtimePlatform == null)
+                        _runtimePlatform = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
+                    return _runtimePlatform;
+                }
                 return _parentProvider?.GetService(serviceType);
             }
 

+ 0 - 9
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -5,12 +5,9 @@ using System.Linq;
 using System.Threading;
 using Avalonia.Media;
 using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.Rendering.SceneGraph;
 using Avalonia.Rendering.Utilities;
 using Avalonia.Utilities;
 using Avalonia.Media.Imaging;
-using Avalonia.Skia.Helpers;
 using SkiaSharp;
 using ISceneBrush = Avalonia.Media.ISceneBrush;
 
@@ -667,12 +664,6 @@ namespace Avalonia.Skia
             _currentBlendingMode = _blendingModeStack.Pop();
         }
 
-        public void Custom(ICustomDrawOperation custom)
-        {
-            CheckLease();
-            custom.Render(this);
-        }
-
         /// <inheritdoc />
         public void PushOpacityMask(IBrush mask, Rect bounds)
         {

+ 1 - 4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -3,8 +3,6 @@ using System.Collections.Generic;
 using System.Numerics;
 using Avalonia.Media;
 using Avalonia.Platform;
-using Avalonia.Rendering;
-using Avalonia.Rendering.SceneGraph;
 using Avalonia.Utilities;
 using Avalonia.Media.Imaging;
 using SharpDX;
@@ -608,8 +606,7 @@ namespace Avalonia.Direct2D1.Media
         {
             PopLayer();
         }
-        
-        public void Custom(ICustomDrawOperation custom) => custom.Render(this);
+
         public object GetFeature(Type t) => null;
     }
 }

+ 22 - 0
src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj

@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <IncludeBuildOutput>false</IncludeBuildOutput>
+    <PackageId>Avalonia.Analyzers</PackageId>
+    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
+    <IsPackable>true</IsPackable>
+    <IsRoslynComponent>true</IsRoslynComponent>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/>
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
+  </ItemGroup>
+
+  <Import Project="..\..\..\build\TrimmingEnable.props" />
+  <Import Project="..\..\..\build\NullableEnable.props" />
+</Project>

+ 0 - 0
src/tools/PublicAnalyzers/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs → src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.CompileAnalyzer.cs


+ 11 - 13
src/tools/PublicAnalyzers/AvaloniaPropertyAnalyzer.cs → src/tools/Avalonia.Analyzers/AvaloniaPropertyAnalyzer.cs

@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Collections.ObjectModel;
-using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Runtime.Serialization;
 using Microsoft.CodeAnalysis;
@@ -14,7 +13,6 @@ using Microsoft.CodeAnalysis.Operations;
 namespace Avalonia.Analyzers;
 
 [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
-[SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking")]
 public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
 {
     private const string Category = "AvaloniaProperty";
@@ -68,7 +66,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Type mismatch: AvaloniaProperty owner is {0}, which is not the containing type",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The owner of an AvaloniaProperty should generally be the containing type. This ensures that the property can be used as expected in XAML.",
         TypeMismatchTag);
 
@@ -78,7 +76,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Unexpected property use: {0} is neither owned by nor attached to {1}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "It is possible to use any AvaloniaProperty with any AvaloniaObject. However, each AvaloniaProperty an object uses on itself should be either owned by that object, or attached to that object.",
         InappropriateReadWriteTag);
 
@@ -88,7 +86,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Inappropriate assignment: An AvaloniaObject should use SetCurrentValue when setting its own StyledProperty or AttachedProperty values",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The standard means of setting an AvaloniaProperty is to call the SetValue method (often via a CLR property setter). This will forcibly overwrite values from sources like styles and templates, " +
         "which is something that should only be done by consumers of the control, not the control itself. Controls which want to set their own values should instead call the SetCurrentValue method, or " +
         "refactor the property into a DirectProperty. An assignment is exempt from this diagnostic in two scenarios: when it is forwarding a constructor parameter, and when the target object is derived " +
@@ -101,7 +99,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Superfluous owner: {0} is already an owner of {1} via {2}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "Ownership of an AvaloniaProperty is inherited along the type hierarchy. There is no need for a derived type to assert ownership over a base type's properties. This diagnostic can be a symptom of an incorrect property owner elsewhere.",
         InappropriateReadWriteTag);
 
@@ -111,7 +109,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Name collision: {0} has the same name as {1}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "Querying for an AvaloniaProperty by name requires that each property associated with a type have a unique name.",
         NameCollisionTag);
 
@@ -121,7 +119,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Name collision: {0} owns multiple Avalonia properties with the name '{1}' {2}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "It is unclear which AvaloniaProperty this CLR property refers to. Ensure that each AvaloniaProperty associated with a type has a unique name. If you need to change behaviour of a base property in your class, call its OverrideMetadata or OverrideDefaultValue methods.",
         NameCollisionTag);
 
@@ -131,7 +129,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Bad name: An AvaloniaProperty named '{0}' is being assigned to {1}. These names do not relate.",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "An AvaloniaProperty should be stored in a field or property which contains its name. For example, a property named \"Brush\" should be assigned to a field called \"BrushProperty\".\nPrivate symbols are exempt from this diagnostic.",
         NameCollisionTag);
 
@@ -141,7 +139,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Side effects: '{0}' is an AvaloniaProperty which can be {1} without the use of this CLR property. This {2} accessor should do nothing except call {3}.",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The AvaloniaObject.GetValue and AvaloniaObject.SetValue methods are public, and do not call any user CLR properties. To execute code before or after the property is set, consider: 1) adding a Coercion method, b) adding a static observer with AvaloniaProperty.Changed.AddClassHandler, and/or c) overriding the AvaloniaObject.OnPropertyChanged method.",
         AssociatedClrPropertyTag);
 
@@ -151,7 +149,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Missing accessor: {0} is {1}, but this CLR property lacks a {2} accessor",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The AvaloniaObject.GetValue and AvaloniaObject.SetValue methods are public, and do not call CLR properties on the owning type. Not providing both CLR property accessors is ineffective.",
         AssociatedClrPropertyTag);
 
@@ -161,7 +159,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Inconsistent accessibility: CLR {0} accessibility does not match accessibility of {1}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The AvaloniaObject.GetValue and AvaloniaObject.SetValue methods are public, and do not call CLR properties on the owning type. Defining a CLR property with different accessibility from its associated AvaloniaProperty is ineffective.",
         AssociatedClrPropertyTag);
 
@@ -171,7 +169,7 @@ public partial class AvaloniaPropertyAnalyzer : DiagnosticAnalyzer
         "Type mismatch: CLR property type differs from the value type of {0} {1}",
         Category,
         DiagnosticSeverity.Warning,
-        isEnabledByDefault: true,
+        isEnabledByDefault: false, // TODO: autogenerate property metadata preserved in ref assembly
         "The AvaloniaObject.GetValue and AvaloniaObject.SetValue methods are public, and do not call CLR properties on the owning type. A CLR property changing the value type (even when an implicit cast is possible) is ineffective and can lead to InvalidCastException to be thrown.",
         TypeMismatchTag, AssociatedClrPropertyTag);
 

+ 8 - 0
src/tools/Avalonia.Analyzers/GlobalSuppressions.cs

@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking")]

+ 59 - 0
src/tools/Avalonia.Analyzers/OnPropertyChangedOverrideAnalyzer.cs

@@ -0,0 +1,59 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Avalonia.Analyzers;
+
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class OnPropertyChangedOverrideAnalyzer : DiagnosticAnalyzer
+{
+    public const string DiagnosticId = "AVA2001";
+
+    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
+        DiagnosticId,
+        "Missing invoke base.OnPropertyChanged",
+        "Method '{0}' do not invoke base.{0}",
+        "Potential issue",
+        DiagnosticSeverity.Warning,
+        isEnabledByDefault: true,
+        description: "The OnPropertyChanged of the base class was not invoked in the override method declaration, which could lead to unwanted behavior.");
+
+    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+    public override void Initialize(AnalysisContext context)
+    {
+        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+        context.EnableConcurrentExecution();
+        context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
+    }
+
+    private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
+    {
+        var method = (MethodDeclarationSyntax)context.Node;
+        if (context.SemanticModel.GetDeclaredSymbol(method, context.CancellationToken) is IMethodSymbol currentMethod
+            && currentMethod.Name == "OnPropertyChanged"
+            && currentMethod.OverriddenMethod is IMethodSymbol originalMethod)
+        {
+            var baseInvocations = method.Body?.DescendantNodes().OfType<BaseExpressionSyntax>();
+            if (baseInvocations?.Any() == true)
+            {
+                foreach (var baseInvocation in baseInvocations)
+                {
+                    if (baseInvocation.Parent is SyntaxNode parent)
+                    {
+                        var targetSymbol = context.SemanticModel.GetSymbolInfo(parent, context.CancellationToken);
+                        if (SymbolEqualityComparer.Default.Equals(targetSymbol.Symbol, originalMethod))
+                        {
+                            return;
+                        }
+                    }
+                }
+            }
+            context.ReportDiagnostic(Diagnostic.Create(Rule, currentMethod.Locations[0], currentMethod.Name));
+        }
+    }
+
+}

+ 1 - 1
src/tools/Avalonia.Generators/GeneratorContextExtensions.cs

@@ -20,7 +20,7 @@ internal static class GeneratorContextExtensions
     public static void ReportNameGeneratorUnhandledError(this GeneratorExecutionContext context, Exception error) =>
         context.Report(UnhandledErrorDescriptorId,
             "Unhandled exception occured while generating typed Name references. " +
-            "Please file an issue: https://github.com/avaloniaui/Avalonia.Generators",
+            "Please file an issue: https://github.com/avaloniaui/Avalonia",
             error.ToString());
 
     public static void ReportNameGeneratorInvalidType(this GeneratorExecutionContext context, string typeName) =>

+ 0 - 17
src/tools/PublicAnalyzers/Avalonia.Analyzers.csproj

@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
-    <Nullable>enable</Nullable>
-    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-    </PackageReference>
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
-  </ItemGroup>
-
-</Project>

+ 9 - 14
tests/Avalonia.Base.UnitTests/AssetLoaderTests.cs

@@ -7,8 +7,10 @@ using Xunit;
 
 namespace Avalonia.Base.UnitTests;
 
-public class AssetLoaderTests : IDisposable
+public class AssetLoaderTests
 {
+    private IAssemblyDescriptorResolver _resolver;
+
     public class MockAssembly : Assembly { }
 
     private const string AssemblyNameWithWhitespace = "Awesome Library";
@@ -17,22 +19,20 @@ public class AssetLoaderTests : IDisposable
 
     public AssetLoaderTests()
     {
-        var resolver = Mock.Of<IAssemblyDescriptorResolver>();
+        _resolver = Mock.Of<IAssemblyDescriptorResolver>();
 
         var descriptor = CreateAssemblyDescriptor(AssemblyNameWithWhitespace);
-        Mock.Get(resolver).Setup(x => x.GetAssembly(AssemblyNameWithWhitespace)).Returns(descriptor);
+        Mock.Get(_resolver).Setup(x => x.GetAssembly(AssemblyNameWithWhitespace)).Returns(descriptor);
 
         descriptor = CreateAssemblyDescriptor(AssemblyNameWithNonAscii);
-        Mock.Get(resolver).Setup(x => x.GetAssembly(AssemblyNameWithNonAscii)).Returns(descriptor);
-
-        AssetLoader.SetAssemblyDescriptorResolver(resolver);
+        Mock.Get(_resolver).Setup(x => x.GetAssembly(AssemblyNameWithNonAscii)).Returns(descriptor);
     }
 
     [Fact]
     public void AssemblyName_With_Whitespace_Should_Load_Resm()
     {
         var uri = new Uri($"resm:Avalonia.Base.UnitTests.Assets.something?assembly={AssemblyNameWithWhitespace}");
-        var loader = new AssetLoader();
+        var loader = new StandardAssetLoader(_resolver);
 
         var assemblyActual = loader.GetAssembly(uri, null);
 
@@ -43,7 +43,7 @@ public class AssetLoaderTests : IDisposable
     public void AssemblyName_With_Non_ASCII_Should_Load_Avares()
     {
         var uri = new Uri($"avares://{AssemblyNameWithNonAscii}/Assets/something");
-        var loader = new AssetLoader();
+        var loader = new StandardAssetLoader(_resolver);
 
         var assemblyActual = loader.GetAssembly(uri, null);
 
@@ -54,7 +54,7 @@ public class AssetLoaderTests : IDisposable
     public void Invalid_AssemblyName_Should_Yield_Empty_Enumerable()
     {
         var uri = new Uri($"avares://InvalidAssembly");
-        var loader = new AssetLoader();
+        var loader = new StandardAssetLoader(_resolver);
 
         var assemblyActual = loader.GetAssets(uri, null);
 
@@ -71,9 +71,4 @@ public class AssetLoaderTests : IDisposable
         Mock.Get(descriptor).Setup(x => x.Assembly).Returns(assembly);
         return descriptor;
     }
-
-    public void Dispose()
-    {
-        AssetLoader.SetAssemblyDescriptorResolver(new AssemblyDescriptorResolver());
-    }
 }

+ 33 - 0
tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs

@@ -514,5 +514,38 @@ namespace Avalonia.Base.UnitTests.Layout
             Assert.True(parent.IsMeasureValid);
             Assert.True(parent.IsArrangeValid);
         }
+
+        [Fact]
+        public void Grandparent_Can_Invalidate_Root_Measure_During_Arrange()
+        {
+            // Issue #11161.
+            var child = new LayoutTestControl();
+            var parent = new LayoutTestControl { Child = child };
+            var grandparent = new LayoutTestControl { Child = parent };
+            var root = new LayoutTestRoot { Child = grandparent };
+
+            root.LayoutManager.ExecuteInitialLayoutPass();
+
+            grandparent.DoArrangeOverride = (_, s) =>
+            {
+                root.InvalidateMeasure();
+                return s;
+            };
+            grandparent.CallBaseArrange = true;
+
+            child.InvalidateMeasure();
+            grandparent.InvalidateMeasure();
+
+            root.LayoutManager.ExecuteLayoutPass();
+
+            Assert.True(child.IsMeasureValid);
+            Assert.True(child.IsArrangeValid);
+            Assert.True(parent.IsMeasureValid);
+            Assert.True(parent.IsArrangeValid);
+            Assert.True(grandparent.IsMeasureValid);
+            Assert.True(grandparent.IsArrangeValid);
+            Assert.True(root.IsMeasureValid);
+            Assert.True(root.IsArrangeValid);
+        }
     }
 }

+ 26 - 6
tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs

@@ -10,21 +10,41 @@ namespace Avalonia.Base.UnitTests.Layout
         public bool Arranged { get; set; }
         public Func<Layoutable, Size, Size> DoMeasureOverride { get; set; }
         public Func<Layoutable, Size, Size> DoArrangeOverride { get; set; }
+        public bool CallBaseMeasure { get; set; }
+        public bool CallBaseArrange { get; set; }
 
         protected override Size MeasureOverride(Size availableSize)
         {
             Measured = true;
-            return DoMeasureOverride != null ?
-                DoMeasureOverride(this, availableSize) :
-                base.MeasureOverride(availableSize);
+
+            if (DoMeasureOverride is not null)
+            {
+                var overrideResult = DoMeasureOverride(this, availableSize);
+                return CallBaseMeasure ?
+                    base.MeasureOverride(overrideResult) :
+                    overrideResult;
+            }
+            else
+            {
+                return base.MeasureOverride(availableSize);
+            }
         }
 
         protected override Size ArrangeOverride(Size finalSize)
         {
             Arranged = true;
-            return DoArrangeOverride != null ?
-                DoArrangeOverride(this, finalSize) :
-                base.ArrangeOverride(finalSize);
+
+            if (DoArrangeOverride is not null)
+            {
+                var overrideResult = DoArrangeOverride(this, finalSize);
+                return CallBaseArrange ?
+                    base.ArrangeOverride(overrideResult) :
+                    overrideResult;
+            }
+            else
+            {
+                return base.ArrangeOverride(finalSize);
+            }
         }
     }
 }

+ 1 - 1
tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs

@@ -17,7 +17,7 @@ namespace Avalonia.Benchmarks.Styling
         private static IDisposable CreateApp()
         {
             var services = new TestServices(
-                assetLoader: new AssetLoader(),
+                assetLoader: new StandardAssetLoader(),
                 globalClock: new MockGlobalClock(),
                 platform: new AppBuilder().RuntimePlatform,
                 renderInterface: new MockPlatformRenderInterface(),

+ 5 - 0
tests/Avalonia.Controls.UnitTests/ContentControlTests.cs

@@ -359,16 +359,21 @@ namespace Avalonia.Controls.UnitTests
             target.Presenter.ApplyTemplate();
 
             Assert.Equal(target, target.Presenter.Child.GetLogicalParent());
+            Assert.Equal(new[] { target.Presenter.Child }, target.LogicalChildren);
 
             root.Child = null;
 
             Assert.Null(target.Template);
 
             target.Content = null;
+
+            Assert.Empty(target.LogicalChildren);
+
             root.Child = target;
             target.Content = "Bar";
 
             Assert.Equal(target, target.Presenter.Child.GetLogicalParent());
+            Assert.Equal(new[] { target.Presenter.Child }, target.LogicalChildren);
         }
 
         private static FuncControlTemplate GetTemplate()

+ 26 - 26
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@@ -34,7 +34,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -81,7 +81,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -109,7 +109,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -141,7 +141,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -161,7 +161,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = window.FindControl<Border>("border");
 
                 var brush = (SolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -187,7 +187,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 window.Show();
 
                 var brush = (SolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -214,7 +214,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var button = window.FindControl<Button>("button");
                 var brush = (ISolidColorBrush)button.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -243,7 +243,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var button = window.FindControl<Button>("button");
                 var brush = (ISolidColorBrush)button.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -279,7 +279,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = window.FindControl<Border>("border");
                 var brush = (ISolidColorBrush)border.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -323,7 +323,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = (Border)button.GetVisualChildren().Single();
                 var brush = (ISolidColorBrush)border.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -347,7 +347,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (SolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -365,7 +365,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var application = (Application)AvaloniaRuntimeXamlLoader.Load(xaml);
             var brush = (SolidColorBrush)application.Resources["brush"];
 
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -411,7 +411,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -434,7 +434,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -461,7 +461,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -491,7 +491,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -519,7 +519,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -551,7 +551,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
             var brush = (SolidColorBrush)border.Background;
             Assert.NotNull(brush);
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -592,7 +592,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var borderBrush = (ISolidColorBrush)border.Background;
 
                 Assert.NotNull(borderBrush);
-                Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
+                Assert.Equal(0xffff0000, borderBrush.Color.ToUInt32());
             }
         }
 
@@ -632,7 +632,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var borderBrush = (ISolidColorBrush)border.Background;
 
                 Assert.NotNull(borderBrush);
-                Assert.Equal(0xffff0000, borderBrush.Color.ToUint32());
+                Assert.Equal(0xffff0000, borderBrush.Color.ToUInt32());
             }
         }
 
@@ -657,7 +657,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 DelayedBinding.ApplyBindings(border);
 
                 var brush = (ISolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
 
                 window.Content = null;
 
@@ -666,7 +666,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 window.Content = border;
 
                 brush = (ISolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -689,7 +689,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (SolidColorBrush)border.Background;
-            Assert.Equal(0u, brush.Color.ToUint32());
+            Assert.Equal(0u, brush.Color.ToUInt32());
 
             brush.GetObservable(SolidColorBrush.ColorProperty).Subscribe(_ => { });
 
@@ -735,7 +735,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (SolidColorBrush)border.Background;
-            Assert.Equal(0u, brush.Color.ToUint32());
+            Assert.Equal(0u, brush.Color.ToUInt32());
 
             brush.GetObservable(SolidColorBrush.ColorProperty).Subscribe(_ => { });
 
@@ -779,7 +779,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             DelayedBinding.ApplyBindings(border);
 
             var brush = (SolidColorBrush)border.Background;
-            Assert.Equal(0u, brush.Color.ToUint32());
+            Assert.Equal(0u, brush.Color.ToUInt32());
 
             using (UnitTestApplication.Start(TestServices.StyledWindow))
             {
@@ -815,7 +815,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs

@@ -43,7 +43,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                     var border = userControl.FindControl<Border>("border");
 
                     var brush = (ISolidColorBrush)border.Background;
-                    Assert.Equal(0xff506070, brush.Color.ToUint32());
+                    Assert.Equal(0xff506070, brush.Color.ToUInt32());
                 }
             }
 

+ 16 - 16
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

@@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -73,7 +73,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -93,7 +93,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = window.FindControl<Border>("border");
 
                 var brush = (SolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -120,7 +120,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -150,7 +150,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -175,7 +175,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 window.Show();
 
                 var brush = (SolidColorBrush)border.Background;
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -202,7 +202,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var button = window.FindControl<Button>("button");
                 var brush = (ISolidColorBrush)button.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -231,7 +231,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var button = window.FindControl<Button>("button");
                 var brush = (ISolidColorBrush)button.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -268,7 +268,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = window.FindControl<Border>("border");
                 var brush = (ISolidColorBrush)border.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -290,7 +290,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (SolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -308,7 +308,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var styles = (Styles)AvaloniaRuntimeXamlLoader.Load(xaml);
             var brush = (SolidColorBrush)styles.Resources["brush"];
 
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -352,7 +352,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var border = (Border)button.GetVisualChildren().Single();
                 var brush = (ISolidColorBrush)border.Background;
                 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 
@@ -491,12 +491,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
 
             userControl.Content = null;
 
             brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
 
         [Fact]
@@ -516,7 +516,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             var border = userControl.FindControl<Border>("border");
 
             var brush = (ISolidColorBrush)border.Background;
-            Assert.Equal(0xff506070, brush.Color.ToUint32());
+            Assert.Equal(0xff506070, brush.Color.ToUInt32());
         }
         
         [Fact]
@@ -542,7 +542,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 var button = window.FindControl<Button>("button");
                 var brush = (ISolidColorBrush)button.Background;
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 

+ 1 - 1
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleIncludeTests.cs

@@ -282,7 +282,7 @@ public class StyleIncludeTests
     public void StyleInclude_From_CodeBehind_Resolves_Compiled()
     {
         using var locatorScope = AvaloniaLocator.EnterScope();
-        AvaloniaLocator.CurrentMutable.BindToSelf<IAssetLoader>(new AssetLoader(GetType().Assembly));
+        AvaloniaLocator.CurrentMutable.BindToSelf<IAssetLoader>(new StandardAssetLoader(GetType().Assembly));
         
         var sp = new TestServiceProvider();
         var styleInclude = new StyleInclude(sp)

+ 2 - 2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@@ -40,7 +40,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
                 var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
                 var color = (Color)((Style)userControl.Styles[0]).Resources["color"];
 
-                Assert.Equal(0xff506070, color.ToUint32());
+                Assert.Equal(0xff506070, color.ToUInt32());
             }
         }
 
@@ -112,7 +112,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
                 var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
                 var brush = (ISolidColorBrush)((Style)userControl.Styles[0]).Resources["brush"];
 
-                Assert.Equal(0xff506070, brush.Color.ToUint32());
+                Assert.Equal(0xff506070, brush.Color.ToUInt32());
             }
         }
 

+ 1 - 1
tests/Avalonia.RenderTests/TestBase.cs

@@ -45,7 +45,7 @@ namespace Avalonia.Direct2D1.RenderTests
         private static readonly TestDispatcherImpl threadingInterface =
             new TestDispatcherImpl();
 
-        private static readonly IAssetLoader assetLoader = new AssetLoader();
+        private static readonly IAssetLoader assetLoader = new StandardAssetLoader();
         
         static TestBase()
         {

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