|
|
@@ -7,12 +7,14 @@ using Avalonia.Data.Core.Parsers;
|
|
|
using Avalonia.Data.Core.Plugins;
|
|
|
using Avalonia.Reactive;
|
|
|
|
|
|
+#nullable enable
|
|
|
+
|
|
|
namespace Avalonia.Data.Core
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// Observes and sets the value of an expression on an object.
|
|
|
/// </summary>
|
|
|
- public class ExpressionObserver : LightweightObservableBase<object>, IDescription
|
|
|
+ public class ExpressionObserver : LightweightObservableBase<object?>, IDescription
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// An ordered collection of property accessor plugins that can be used to customize
|
|
|
@@ -51,10 +53,10 @@ namespace Avalonia.Data.Core
|
|
|
|
|
|
private static readonly object UninitializedValue = new object();
|
|
|
private readonly ExpressionNode _node;
|
|
|
- private object _root;
|
|
|
- private IDisposable _rootSubscription;
|
|
|
- private WeakReference<object> _value;
|
|
|
- private IReadOnlyList<ITransformNode> _transformNodes;
|
|
|
+ private object? _root;
|
|
|
+ private IDisposable? _rootSubscription;
|
|
|
+ private WeakReference<object?>? _value;
|
|
|
+ private IReadOnlyList<ITransformNode>? _transformNodes;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
|
|
|
@@ -65,18 +67,13 @@ namespace Avalonia.Data.Core
|
|
|
/// A description of the expression.
|
|
|
/// </param>
|
|
|
public ExpressionObserver(
|
|
|
- object root,
|
|
|
+ object? root,
|
|
|
ExpressionNode node,
|
|
|
- string description = null)
|
|
|
+ string? description = null)
|
|
|
{
|
|
|
- if (root == AvaloniaProperty.UnsetValue)
|
|
|
- {
|
|
|
- root = null;
|
|
|
- }
|
|
|
-
|
|
|
_node = node;
|
|
|
Description = description;
|
|
|
- _root = new WeakReference<object>(root);
|
|
|
+ _root = new WeakReference<object?>(root == AvaloniaProperty.UnsetValue ? null : root);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -88,11 +85,11 @@ namespace Avalonia.Data.Core
|
|
|
/// A description of the expression.
|
|
|
/// </param>
|
|
|
public ExpressionObserver(
|
|
|
- IObservable<object> rootObservable,
|
|
|
+ IObservable<object?> rootObservable,
|
|
|
ExpressionNode node,
|
|
|
- string description)
|
|
|
+ string? description)
|
|
|
{
|
|
|
- Contract.Requires<ArgumentNullException>(rootObservable != null);
|
|
|
+ _ = rootObservable ??throw new ArgumentNullException(nameof(rootObservable));
|
|
|
|
|
|
_node = node;
|
|
|
Description = description;
|
|
|
@@ -109,16 +106,16 @@ namespace Avalonia.Data.Core
|
|
|
/// A description of the expression.
|
|
|
/// </param>
|
|
|
public ExpressionObserver(
|
|
|
- Func<object> rootGetter,
|
|
|
+ Func<object?> rootGetter,
|
|
|
ExpressionNode node,
|
|
|
IObservable<Unit> update,
|
|
|
- string description)
|
|
|
+ string? description)
|
|
|
{
|
|
|
- Contract.Requires<ArgumentNullException>(rootGetter != null);
|
|
|
- Contract.Requires<ArgumentNullException>(update != null);
|
|
|
+ _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
|
|
|
+
|
|
|
Description = description;
|
|
|
- _node = node;
|
|
|
- _node.Target = new WeakReference<object>(rootGetter());
|
|
|
+ _node = node ?? throw new ArgumentNullException(nameof(rootGetter));
|
|
|
+ _node.Target = new WeakReference<object?>(rootGetter());
|
|
|
_root = update.Select(x => rootGetter());
|
|
|
}
|
|
|
|
|
|
@@ -133,10 +130,10 @@ namespace Avalonia.Data.Core
|
|
|
/// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
|
|
|
/// </param>
|
|
|
public static ExpressionObserver Create<T, U>(
|
|
|
- T root,
|
|
|
+ T? root,
|
|
|
Expression<Func<T, U>> expression,
|
|
|
bool enableDataValidation = false,
|
|
|
- string description = null)
|
|
|
+ string? description = null)
|
|
|
{
|
|
|
return new ExpressionObserver(root, Parse(expression, enableDataValidation), description ?? expression.ToString());
|
|
|
}
|
|
|
@@ -154,11 +151,12 @@ namespace Avalonia.Data.Core
|
|
|
IObservable<T> rootObservable,
|
|
|
Expression<Func<T, U>> expression,
|
|
|
bool enableDataValidation = false,
|
|
|
- string description = null)
|
|
|
+ string? description = null)
|
|
|
{
|
|
|
- Contract.Requires<ArgumentNullException>(rootObservable != null);
|
|
|
+ _ = rootObservable ?? throw new ArgumentNullException(nameof(rootObservable));
|
|
|
+
|
|
|
return new ExpressionObserver(
|
|
|
- rootObservable.Select(o => (object)o),
|
|
|
+ rootObservable.Select(o => (object?)o),
|
|
|
Parse(expression, enableDataValidation),
|
|
|
description ?? expression.ToString());
|
|
|
}
|
|
|
@@ -178,9 +176,9 @@ namespace Avalonia.Data.Core
|
|
|
Expression<Func<T, U>> expression,
|
|
|
IObservable<Unit> update,
|
|
|
bool enableDataValidation = false,
|
|
|
- string description = null)
|
|
|
+ string? description = null)
|
|
|
{
|
|
|
- Contract.Requires<ArgumentNullException>(rootGetter != null);
|
|
|
+ _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter));
|
|
|
|
|
|
return new ExpressionObserver(
|
|
|
() => rootGetter(),
|
|
|
@@ -218,7 +216,7 @@ namespace Avalonia.Data.Core
|
|
|
/// before setting the target value can work, as setting the value requires the
|
|
|
/// expression to be evaluated.
|
|
|
/// </returns>
|
|
|
- public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue)
|
|
|
+ public bool SetValue(object? value, BindingPriority priority = BindingPriority.LocalValue)
|
|
|
{
|
|
|
if (Leaf is SettableNode settable)
|
|
|
{
|
|
|
@@ -238,18 +236,18 @@ namespace Avalonia.Data.Core
|
|
|
/// <summary>
|
|
|
/// Gets a description of the expression being observed.
|
|
|
/// </summary>
|
|
|
- public string Description { get; }
|
|
|
+ public string? Description { get; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets the expression being observed.
|
|
|
/// </summary>
|
|
|
- public string Expression { get; }
|
|
|
+ public string? Expression { get; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets the type of the expression result or null if the expression could not be
|
|
|
/// evaluated.
|
|
|
/// </summary>
|
|
|
- public Type ResultType => (Leaf as SettableNode)?.PropertyType;
|
|
|
+ public Type? ResultType => (Leaf as SettableNode)?.PropertyType;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets the leaf node.
|
|
|
@@ -278,7 +276,7 @@ namespace Avalonia.Data.Core
|
|
|
_node.Unsubscribe();
|
|
|
}
|
|
|
|
|
|
- protected override void Subscribed(IObserver<object> observer, bool first)
|
|
|
+ protected override void Subscribed(IObserver<object?> observer, bool first)
|
|
|
{
|
|
|
if (!first && _value != null && _value.TryGetTarget(out var value))
|
|
|
{
|
|
|
@@ -296,21 +294,21 @@ namespace Avalonia.Data.Core
|
|
|
if (_root is IObservable<object> observable)
|
|
|
{
|
|
|
_rootSubscription = observable.Subscribe(
|
|
|
- x => _node.Target = new WeakReference<object>(x != AvaloniaProperty.UnsetValue ? x : null),
|
|
|
+ x => _node.Target = new WeakReference<object?>(x != AvaloniaProperty.UnsetValue ? x : null),
|
|
|
x => PublishCompleted(),
|
|
|
() => PublishCompleted());
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _node.Target = (WeakReference<object>)_root;
|
|
|
+ _node.Target = (WeakReference<object?>)_root!;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void ValueChanged(object value)
|
|
|
+ private void ValueChanged(object? value)
|
|
|
{
|
|
|
var broken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
|
|
|
- broken?.Commit(Description);
|
|
|
- _value = new WeakReference<object>(value);
|
|
|
+ broken?.Commit(Description ?? "{empty}");
|
|
|
+ _value = new WeakReference<object?>(value);
|
|
|
PublishNext(value);
|
|
|
}
|
|
|
}
|