BindingEntry.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using System;
  2. using Avalonia.Data;
  3. using Avalonia.Threading;
  4. #nullable enable
  5. namespace Avalonia.PropertyStore
  6. {
  7. /// <summary>
  8. /// Represents an untyped interface to <see cref="BindingEntry{T}"/>.
  9. /// </summary>
  10. internal interface IBindingEntry : IPriorityValueEntry, IDisposable
  11. {
  12. }
  13. /// <summary>
  14. /// Stores a binding in a <see cref="ValueStore"/> or <see cref="PriorityValue{T}"/>.
  15. /// </summary>
  16. /// <typeparam name="T">The property type.</typeparam>
  17. internal class BindingEntry<T> : IBindingEntry, IPriorityValueEntry<T>, IObserver<BindingValue<T>>
  18. {
  19. private readonly IAvaloniaObject _owner;
  20. private IValueSink _sink;
  21. private IDisposable? _subscription;
  22. public BindingEntry(
  23. IAvaloniaObject owner,
  24. StyledPropertyBase<T> property,
  25. IObservable<BindingValue<T>> source,
  26. BindingPriority priority,
  27. IValueSink sink)
  28. {
  29. _owner = owner;
  30. Property = property;
  31. Source = source;
  32. Priority = priority;
  33. _sink = sink;
  34. }
  35. public StyledPropertyBase<T> Property { get; }
  36. public BindingPriority Priority { get; }
  37. public IObservable<BindingValue<T>> Source { get; }
  38. public Optional<T> Value { get; private set; }
  39. Optional<object> IValue.Value => Value.ToObject();
  40. BindingPriority IValue.ValuePriority => Priority;
  41. public void Dispose()
  42. {
  43. _subscription?.Dispose();
  44. _subscription = null;
  45. _sink.Completed(Property, this, Value);
  46. }
  47. public void OnCompleted() => _sink.Completed(Property, this, Value);
  48. public void OnError(Exception error)
  49. {
  50. throw new NotImplementedException();
  51. }
  52. public void OnNext(BindingValue<T> value)
  53. {
  54. if (Dispatcher.UIThread.CheckAccess())
  55. {
  56. UpdateValue(value);
  57. }
  58. else
  59. {
  60. // To avoid allocating closure in the outer scope we need to capture variables
  61. // locally. This allows us to skip most of the allocations when on UI thread.
  62. var instance = this;
  63. var newValue = value;
  64. Dispatcher.UIThread.Post(() => instance.UpdateValue(newValue));
  65. }
  66. }
  67. public void Start()
  68. {
  69. _subscription = Source.Subscribe(this);
  70. }
  71. public void Reparent(IValueSink sink) => _sink = sink;
  72. private void UpdateValue(BindingValue<T> value)
  73. {
  74. if (value.HasValue && Property.ValidateValue?.Invoke(value.Value) == false)
  75. {
  76. value = Property.GetDefaultValue(_owner.GetType());
  77. }
  78. if (value.Type == BindingValueType.DoNothing)
  79. {
  80. return;
  81. }
  82. var old = Value;
  83. if (value.Type != BindingValueType.DataValidationError)
  84. {
  85. Value = value.ToOptional();
  86. }
  87. _sink.ValueChanged(Property, Priority, old, value);
  88. }
  89. }
  90. }