Browse Source

Merge pull request #10608 from MrJul/markup-nullable

Nullable annotations for Markup.Xaml
pull/10801/head
Steven Kirk 4 years ago
committed by GitHub
parent
commit
c27c714ee0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.Base/Styling/ITemplate.cs
  2. 7
      src/Avalonia.Controls/Carousel.cs
  3. 7
      src/Avalonia.Controls/ComboBox.cs
  4. 5
      src/Avalonia.Controls/ContextMenu.cs
  5. 12
      src/Avalonia.Controls/ItemsControl.cs
  6. 7
      src/Avalonia.Controls/ListBox.cs
  7. 6
      src/Avalonia.Controls/Menu.cs
  8. 5
      src/Avalonia.Controls/MenuItem.cs
  9. 12
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  10. 6
      src/Avalonia.Controls/Primitives/TabStrip.cs
  11. 19
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  12. 7
      src/Avalonia.Controls/TabControl.cs
  13. 4
      src/Avalonia.Controls/Templates/FuncTemplate`1.cs
  14. 4
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  15. 4
      src/Avalonia.Controls/TreeViewItem.cs
  16. 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  17. 5
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  18. 14
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  19. 6
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs
  20. 11
      src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs
  21. 8
      src/Markup/Avalonia.Markup.Xaml/Converters/ColorToBrushConverter.cs
  22. 10
      src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs
  23. 13
      src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs
  24. 9
      src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs
  25. 13
      src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs
  26. 33
      src/Markup/Avalonia.Markup.Xaml/Extensions.cs
  27. 12
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs
  28. 10
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs
  29. 36
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
  30. 54
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  31. 12
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs
  32. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs
  33. 14
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ObservableStreamPlugin.cs
  34. 52
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
  35. 11
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorPlugin.cs
  36. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs
  37. 18
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/TaskStreamPlugin.cs
  38. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs
  39. 3
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/On.cs
  40. 6
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
  41. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs
  42. 27
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs
  43. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs
  44. 11
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResolveByNameExtension.cs
  45. 34
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  46. 8
      src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs
  47. 2
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs
  48. 3
      src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs
  49. 3
      src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
  50. 2
      src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
  51. 6
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  52. 6
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  53. 10
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  54. 8
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  55. 8
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  56. 17
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  57. 12
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  58. 4
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs
  59. 40
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  60. 3
      src/Markup/Avalonia.Markup/Data/Binding.cs
  61. 5
      src/Markup/Avalonia.Markup/Data/BindingBase.cs
  62. 27
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  63. 9
      src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs
  64. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
  65. 8
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  66. 18
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  67. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  68. 11
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

2
src/Avalonia.Base/Styling/ITemplate.cs

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

7
src/Avalonia.Controls/Carousel.cs

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

7
src/Avalonia.Controls/ComboBox.cs

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

5
src/Avalonia.Controls/ContextMenu.cs

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

12
src/Avalonia.Controls/ItemsControl.cs

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

7
src/Avalonia.Controls/ListBox.cs

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

6
src/Avalonia.Controls/Menu.cs

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

5
src/Avalonia.Controls/MenuItem.cs

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

12
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

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

6
src/Avalonia.Controls/Primitives/TabStrip.cs

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

19
src/Avalonia.Controls/Primitives/TemplatedControl.cs

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

7
src/Avalonia.Controls/TabControl.cs

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

4
src/Avalonia.Controls/Templates/FuncTemplate`1.cs

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

4
src/Avalonia.Controls/Templates/IControlTemplate.cs

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

4
src/Avalonia.Controls/TreeViewItem.cs

@ -40,8 +40,8 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<TreeViewItem, int>( AvaloniaProperty.RegisterDirect<TreeViewItem, int>(
nameof(Level), o => o.Level); nameof(Level), o => o.Level);
private static readonly ITemplate<Panel> DefaultPanel = private static readonly FuncTemplate<Panel?> DefaultPanel =
new FuncTemplate<Panel>(() => new StackPanel()); new(() => new StackPanel());
private TreeView? _treeView; private TreeView? _treeView;
private Control? _header; private Control? _header;

2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

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

5
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

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

14
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

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

6
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaUriTypeConverter.cs

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

11
src/Markup/Avalonia.Markup.Xaml/Converters/BitmapTypeConverter.cs

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

8
src/Markup/Avalonia.Markup.Xaml/Converters/ColorToBrushConverter.cs

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

10
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

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

13
src/Markup/Avalonia.Markup.Xaml/Converters/IconTypeConverter.cs

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

9
src/Markup/Avalonia.Markup.Xaml/Converters/PointsListTypeConverter.cs

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

13
src/Markup/Avalonia.Markup.Xaml/Converters/TimeSpanTypeConverter.cs

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

33
src/Markup/Avalonia.Markup.Xaml/Extensions.cs

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

12
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs

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

10
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs

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

36
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs

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

54
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

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

12
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/FindVisualAncestorNode.cs

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

6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs

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

14
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ObservableStreamPlugin.cs

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

52
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs

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

11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorPlugin.cs

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

6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs

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

18
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/TaskStreamPlugin.cs

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

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

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

3
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/On.cs

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

6
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs

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

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs

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

27
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ReflectionBindingExtension.cs

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

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs

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

11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResolveByNameExtension.cs

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

34
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

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

8
src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs

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

2
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderConfiguration.cs

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

3
src/Markup/Avalonia.Markup.Xaml/RuntimeXamlLoaderDocument.cs

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

3
src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs

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

2
src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs

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

6
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

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

6
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

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

10
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

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

8
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

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

8
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

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

17
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

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

12
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

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

4
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/IAvaloniaXamlIlXmlNamespaceInfoProviderV1.cs

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

40
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

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

3
src/Markup/Avalonia.Markup/Data/Binding.cs

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

5
src/Markup/Avalonia.Markup/Data/BindingBase.cs

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

27
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

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

9
src/Markup/Avalonia.Markup/Markup/Data/DelayedBinding.cs

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

6
src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs

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

8
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

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

18
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

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

6
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

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

11
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

@ -2,6 +2,7 @@ using System;
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Markup.Xaml.Templates; using Avalonia.Markup.Xaml.Templates;
@ -308,6 +309,16 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal("Foo", foo.Name); Assert.Equal("Foo", foo.Name);
Assert.Equal("Bar", bar.Name); Assert.Equal("Bar", bar.Name);
} }
[Fact]
public void ControlTemplate_Can_Be_Empty()
{
var xaml = "<ControlTemplate xmlns='https://github.com/avaloniaui' />";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var templateResult = template.Build(new TemplatedControl());
Assert.Null(templateResult);
}
} }
public class ListBoxHierarchyLine : Panel public class ListBoxHierarchyLine : Panel
{ {

Loading…
Cancel
Save