diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 58ccb53744..bc1e95805f 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -16,7 +16,7 @@ namespace Avalonia
///
/// This class is analogous to DependencyObject in WPF.
///
- public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged, IValueSink
+ public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
{
private AvaloniaObject? _inheritanceParent;
private List? _directBindings;
@@ -524,7 +524,7 @@ namespace Avalonia
return _propertyChanged?.GetInvocationList();
}
- void IValueSink.ValueChanged(AvaloniaPropertyChangedEventArgs change)
+ internal void ValueChanged(AvaloniaPropertyChangedEventArgs change)
{
var property = (StyledPropertyBase)change.Property;
@@ -563,7 +563,7 @@ namespace Avalonia
}
}
- void IValueSink.Completed(
+ internal void Completed(
StyledPropertyBase property,
IPriorityValueEntry entry,
Optional oldValue)
@@ -574,7 +574,7 @@ namespace Avalonia
oldValue,
default,
BindingPriority.Unset);
- ((IValueSink)this).ValueChanged(change);
+ ValueChanged(change);
}
///
diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
index cffbaed6b0..9a25e98a23 100644
--- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs
@@ -18,19 +18,19 @@ namespace Avalonia.PropertyStore
/// The property type.
internal class BindingEntry : IBindingEntry, IPriorityValueEntry, IObserver>
{
- private readonly IAvaloniaObject _owner;
- private IValueSink _sink;
+ private readonly AvaloniaObject _owner;
+ private ValueOwner _sink;
private IDisposable? _subscription;
private bool _isSubscribed;
private bool _batchUpdate;
private Optional _value;
public BindingEntry(
- IAvaloniaObject owner,
+ AvaloniaObject owner,
StyledPropertyBase property,
IObservable> source,
BindingPriority priority,
- IValueSink sink)
+ ValueOwner sink)
{
_owner = owner;
Property = property;
@@ -50,7 +50,7 @@ namespace Avalonia.PropertyStore
{
_batchUpdate = false;
- if (_sink is ValueStore)
+ if (_sink.IsValueStore)
Start();
}
@@ -113,16 +113,15 @@ namespace Avalonia.PropertyStore
}
}
- public void Reparent(IValueSink sink) => _sink = sink;
+ public void Reparent(PriorityValue parent) => _sink = new(parent);
public void RaiseValueChanged(
- IValueSink sink,
- IAvaloniaObject owner,
+ AvaloniaObject owner,
AvaloniaProperty property,
Optional
internal interface IPriorityValueEntry : IValue
{
- void Reparent(IValueSink sink);
}
///
@@ -14,5 +13,6 @@
/// The property type.
internal interface IPriorityValueEntry : IPriorityValueEntry, IValue
{
+ void Reparent(PriorityValue parent);
}
}
diff --git a/src/Avalonia.Base/PropertyStore/IValue.cs b/src/Avalonia.Base/PropertyStore/IValue.cs
index a4ec06e64e..b493df92e6 100644
--- a/src/Avalonia.Base/PropertyStore/IValue.cs
+++ b/src/Avalonia.Base/PropertyStore/IValue.cs
@@ -11,8 +11,7 @@ namespace Avalonia.PropertyStore
Optional GetValue();
void Start();
void RaiseValueChanged(
- IValueSink sink,
- IAvaloniaObject owner,
+ AvaloniaObject owner,
AvaloniaProperty property,
Optional oldValue,
Optional newValue);
diff --git a/src/Avalonia.Base/PropertyStore/IValueSink.cs b/src/Avalonia.Base/PropertyStore/IValueSink.cs
deleted file mode 100644
index 738fdef267..0000000000
--- a/src/Avalonia.Base/PropertyStore/IValueSink.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Avalonia.Data;
-
-namespace Avalonia.PropertyStore
-{
- ///
- /// Represents an entity that can receive change notifications in a .
- ///
- internal interface IValueSink
- {
- void ValueChanged(AvaloniaPropertyChangedEventArgs change);
-
- void Completed(
- StyledPropertyBase property,
- IPriorityValueEntry entry,
- Optional oldValue);
- }
-}
diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
index cb5f3556d1..13ca69681f 100644
--- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
+++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
@@ -25,13 +25,12 @@ namespace Avalonia.PropertyStore
public void Start() { }
public void RaiseValueChanged(
- IValueSink sink,
- IAvaloniaObject owner,
+ AvaloniaObject owner,
AvaloniaProperty property,
Optional oldValue,
Optional newValue)
{
- sink.ValueChanged(new AvaloniaPropertyChangedEventArgs(
+ owner.ValueChanged(new AvaloniaPropertyChangedEventArgs(
owner,
(AvaloniaProperty)property,
oldValue.Cast(),
diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
index 03c83d00ed..112cf6619f 100644
--- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs
+++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs
@@ -23,10 +23,10 @@ namespace Avalonia.PropertyStore
/// entries (sorted first by priority and then in the order
/// they were added) plus a local value.
///
- internal class PriorityValue : IPriorityValue, IValue, IValueSink, IBatchUpdate
+ internal class PriorityValue : IPriorityValue, IValue, IBatchUpdate
{
- private readonly IAvaloniaObject _owner;
- private readonly IValueSink _sink;
+ private readonly AvaloniaObject _owner;
+ private readonly ValueStore _store;
private readonly List> _entries = new List>();
private readonly Func? _coerceValue;
private Optional _localValue;
@@ -35,13 +35,13 @@ namespace Avalonia.PropertyStore
private bool _batchUpdate;
public PriorityValue(
- IAvaloniaObject owner,
+ AvaloniaObject owner,
StyledPropertyBase property,
- IValueSink sink)
+ ValueStore store)
{
_owner = owner;
Property = property;
- _sink = sink;
+ _store = store;
if (property.HasCoercion)
{
@@ -51,11 +51,11 @@ namespace Avalonia.PropertyStore
}
public PriorityValue(
- IAvaloniaObject owner,
+ AvaloniaObject owner,
StyledPropertyBase property,
- IValueSink sink,
+ ValueStore store,
IPriorityValueEntry existing)
- : this(owner, property, sink)
+ : this(owner, property, store)
{
existing.Reparent(this);
_entries.Add(existing);
@@ -82,9 +82,9 @@ namespace Avalonia.PropertyStore
}
public PriorityValue(
- IAvaloniaObject owner,
+ AvaloniaObject owner,
StyledPropertyBase property,
- IValueSink sink,
+ ValueStore sink,
LocalValueEntry existing)
: this(owner, property, sink)
{
@@ -155,7 +155,7 @@ namespace Avalonia.PropertyStore
else
{
var insert = FindInsertPoint(priority);
- var entry = new ConstantValueEntry(Property, value, priority, this);
+ var entry = new ConstantValueEntry(Property, value, priority, new ValueOwner(this));
_entries.Insert(insert, entry);
result = entry;
}
@@ -172,7 +172,7 @@ namespace Avalonia.PropertyStore
public BindingEntry AddBinding(IObservable> source, BindingPriority priority)
{
- var binding = new BindingEntry(_owner, Property, source, priority, this);
+ var binding = new BindingEntry(_owner, Property, source, priority, new(this));
var insert = FindInsertPoint(binding.Priority);
_entries.Insert(insert, binding);
@@ -193,13 +193,12 @@ namespace Avalonia.PropertyStore
public void Start() => UpdateEffectiveValue(null);
public void RaiseValueChanged(
- IValueSink sink,
- IAvaloniaObject owner,
+ AvaloniaObject owner,
AvaloniaProperty property,
Optional oldValue,
Optional newValue)
{
- sink.ValueChanged(new AvaloniaPropertyChangedEventArgs(
+ owner.ValueChanged(new AvaloniaPropertyChangedEventArgs(
owner,
(AvaloniaProperty)property,
oldValue.Cast(),
@@ -207,7 +206,7 @@ namespace Avalonia.PropertyStore
Priority));
}
- void IValueSink.ValueChanged(AvaloniaPropertyChangedEventArgs change)
+ public void ValueChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Priority == BindingPriority.LocalValue)
{
@@ -220,22 +219,15 @@ namespace Avalonia.PropertyStore
}
}
- void IValueSink.Completed(
- StyledPropertyBase property,
- IPriorityValueEntry entry,
- Optional oldValue)
+ public void Completed(IPriorityValueEntry entry, Optional oldValue)
{
_entries.Remove((IPriorityValueEntry)entry);
-
- if (oldValue is Optional o)
- {
- UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs(
- _owner,
- Property,
- o,
- default,
- entry.Priority));
- }
+ UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs(
+ _owner,
+ Property,
+ oldValue,
+ default,
+ entry.Priority));
}
private int FindInsertPoint(BindingPriority priority)
@@ -315,7 +307,7 @@ namespace Avalonia.PropertyStore
var old = _value;
_value = value;
- _sink.ValueChanged(new AvaloniaPropertyChangedEventArgs(
+ _store.ValueChanged(new AvaloniaPropertyChangedEventArgs(
_owner,
Property,
old,
@@ -326,7 +318,7 @@ namespace Avalonia.PropertyStore
{
change.MarkNonEffectiveValue();
change.SetOldValue(default);
- _sink.ValueChanged(change);
+ _store.ValueChanged(change);
}
}
}
diff --git a/src/Avalonia.Base/PropertyStore/ValueOwner.cs b/src/Avalonia.Base/PropertyStore/ValueOwner.cs
new file mode 100644
index 0000000000..c68435f7a5
--- /dev/null
+++ b/src/Avalonia.Base/PropertyStore/ValueOwner.cs
@@ -0,0 +1,45 @@
+using Avalonia.Data;
+
+namespace Avalonia.PropertyStore
+{
+ ///
+ /// Represents a union type of and ,
+ /// which are the valid owners of a value store .
+ ///
+ /// The value type.
+ internal readonly struct ValueOwner
+ {
+ private readonly ValueStore? _store;
+ private readonly PriorityValue? _priorityValue;
+
+ public ValueOwner(ValueStore o)
+ {
+ _store = o;
+ _priorityValue = null;
+ }
+
+ public ValueOwner(PriorityValue v)
+ {
+ _store = null;
+ _priorityValue = v;
+ }
+
+ public bool IsValueStore => _store is not null;
+
+ public void Completed(StyledPropertyBase property, IPriorityValueEntry entry, Optional oldValue)
+ {
+ if (_store is not null)
+ _store?.Completed(property, entry, oldValue);
+ else
+ _priorityValue!.Completed(entry, oldValue);
+ }
+
+ public void ValueChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (_store is not null)
+ _store?.ValueChanged(e);
+ else
+ _priorityValue!.ValueChanged(e);
+ }
+ }
+}
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index 67149b0988..69c644dff9 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -21,16 +21,15 @@ namespace Avalonia
/// - For a single binding it will be an instance of
/// - For all other cases it will be an instance of
///
- internal class ValueStore : IValueSink
+ internal class ValueStore
{
private readonly AvaloniaObject _owner;
- private readonly IValueSink _sink;
private readonly AvaloniaPropertyValueStore _values;
private BatchUpdate? _batchUpdate;
public ValueStore(AvaloniaObject owner)
{
- _sink = _owner = owner;
+ _owner = owner;
_values = new AvaloniaPropertyValueStore();
}
@@ -122,7 +121,7 @@ namespace Avalonia
}
else
{
- var entry = new ConstantValueEntry(property, value, priority, this);
+ var entry = new ConstantValueEntry(property, value, priority, new(this));
AddValue(property, entry);
NotifyValueChanged(property, default, value, priority);
result = entry;
@@ -151,7 +150,7 @@ namespace Avalonia
}
else
{
- var entry = new BindingEntry(_owner, property, source, priority, this);
+ var entry = new BindingEntry(_owner, property, source, priority, new(this));
AddValue(property, entry);
return entry;
}
@@ -187,7 +186,7 @@ namespace Avalonia
// so there's no way to mark them for removal at the end of a batch update. Instead convert
// them to a constant value entry with Unset priority in the event of a local value being
// cleared during a batch update.
- var sentinel = new ConstantValueEntry(property, Optional.Empty, BindingPriority.Unset, _sink);
+ var sentinel = new ConstantValueEntry(property, Optional.Empty, BindingPriority.Unset, new(this));
_values.SetValue(property, sentinel);
}
@@ -222,7 +221,7 @@ namespace Avalonia
return null;
}
- void IValueSink.ValueChanged(AvaloniaPropertyChangedEventArgs change)
+ public void ValueChanged(AvaloniaPropertyChangedEventArgs change)
{
if (_batchUpdate is object)
{
@@ -233,11 +232,11 @@ namespace Avalonia
}
else
{
- _sink.ValueChanged(change);
+ _owner.ValueChanged(change);
}
}
- void IValueSink.Completed(
+ public void Completed(
StyledPropertyBase property,
IPriorityValueEntry entry,
Optional oldValue)
@@ -248,7 +247,7 @@ namespace Avalonia
if (_batchUpdate is null)
{
_values.Remove(property);
- _sink.Completed(property, entry, oldValue);
+ _owner.Completed(property, entry, oldValue);
}
else
{
@@ -352,7 +351,7 @@ namespace Avalonia
{
if (_batchUpdate is null)
{
- _sink.ValueChanged(new AvaloniaPropertyChangedEventArgs(
+ _owner.ValueChanged(new AvaloniaPropertyChangedEventArgs(
_owner,
property,
oldValue,
@@ -451,7 +450,7 @@ namespace Avalonia
};
// Call _sink.ValueChanged with an appropriately typed AvaloniaPropertyChangedEventArgs.
- slot.RaiseValueChanged(_owner._sink, _owner._owner, entry.property, oldValue, newValue);
+ slot.RaiseValueChanged(_owner._owner, entry.property, oldValue, newValue);
// During batch update values can't be removed immediately because they're needed to raise
// the _sink.ValueChanged notification. They instead mark themselves for removal by setting
diff --git a/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs b/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
index 0caa984a22..aa5993f3b2 100644
--- a/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
+++ b/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
@@ -10,8 +10,8 @@ namespace Avalonia.Base.UnitTests
{
public class PriorityValueTests
{
- private static readonly IValueSink NullSink = new MockSink();
- private static readonly IAvaloniaObject Owner = Mock.Of();
+ private static readonly AvaloniaObject Owner = new AvaloniaObject();
+ private static readonly ValueStore ValueStore = new ValueStore(Owner);
private static readonly StyledProperty TestProperty = new StyledProperty(
"Test",
typeof(PriorityValueTests),
@@ -23,12 +23,12 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue(
Owner,
TestProperty,
- NullSink,
+ ValueStore,
new ConstantValueEntry(
TestProperty,
"1",
BindingPriority.StyleTrigger,
- NullSink));
+ new(ValueStore)));
Assert.Equal("1", target.GetValue().Value);
Assert.Equal(BindingPriority.StyleTrigger, target.Priority);
@@ -40,7 +40,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue(
Owner,
TestProperty,
- NullSink);
+ ValueStore);
target.SetValue("animation", BindingPriority.Animation);
target.SetValue("local", BindingPriority.LocalValue);
@@ -60,7 +60,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue(
Owner,
TestProperty,
- NullSink);
+ ValueStore);
target.SetValue("1", BindingPriority.LocalValue);
target.SetValue("2", BindingPriority.LocalValue);
@@ -74,7 +74,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue(
Owner,
TestProperty,
- NullSink);
+ ValueStore);
target.SetValue("1", BindingPriority.Style);
target.SetValue("2", BindingPriority.Animation);
@@ -93,7 +93,7 @@ namespace Avalonia.Base.UnitTests
var target = new PriorityValue(
Owner,
TestProperty,
- NullSink);
+ ValueStore);
Assert.Equal(BindingPriority.Unset, target.Priority);
target.SetValue("style", BindingPriority.Style);
@@ -109,7 +109,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Binding_With_Same_Priority_Should_Be_Appended()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
@@ -129,7 +129,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Binding_With_Higher_Priority_Should_Be_Appended()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
@@ -149,7 +149,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Binding_With_Lower_Priority_Should_Be_Prepended()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
@@ -169,7 +169,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Second_Binding_With_Lower_Priority_Should_Be_Inserted_In_Middle()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
var source3 = new Source("3");
@@ -191,7 +191,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Competed_Binding_Should_Be_Removed()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
var source3 = new Source("3");
@@ -214,7 +214,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void Value_Should_Come_From_Last_Entry()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
var source3 = new Source("3");
@@ -229,7 +229,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void LocalValue_Should_Override_LocalValue_Binding()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.LocalValue).Start();
@@ -241,7 +241,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void LocalValue_Should_Override_Style_Binding()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.Style).Start();
@@ -253,7 +253,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void LocalValue_Should_Not_Override_Animation_Binding()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
target.AddBinding(source1, BindingPriority.Animation).Start();
@@ -265,7 +265,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void NonAnimated_Value_Should_Be_Correct_1()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
var source3 = new Source("3");
@@ -281,7 +281,7 @@ namespace Avalonia.Base.UnitTests
[Fact]
public void NonAnimated_Value_Should_Be_Correct_2()
{
- var target = new PriorityValue(Owner, TestProperty, NullSink);
+ var target = new PriorityValue(Owner, TestProperty, ValueStore);
var source1 = new Source("1");
var source2 = new Source("2");
var source3 = new Source("3");
@@ -310,16 +310,5 @@ namespace Avalonia.Base.UnitTests
public void OnCompleted() => _observer.OnCompleted();
}
-
- private class MockSink : IValueSink
- {
- public void Completed(StyledPropertyBase property, IPriorityValueEntry entry, Optional oldValue)
- {
- }
-
- public void ValueChanged(AvaloniaPropertyChangedEventArgs change)
- {
- }
- }
}
}