Browse Source

Fixed issue with LocalValue and bindings.

pull/3287/head
Steven Kirk 7 years ago
parent
commit
d5dc470416
  1. 12
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  2. 56
      src/Avalonia.Base/PropertyStore/PriorityValue.cs
  3. 9
      src/Avalonia.Base/ValueStore.cs
  4. 31
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

12
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@ -6,14 +6,12 @@ namespace Avalonia.PropertyStore
{ {
internal class LocalValueEntry<T> : IValue<T> internal class LocalValueEntry<T> : IValue<T>
{ {
public LocalValueEntry(T value) => Value = value; private T _value;
public Optional<T> Value { get; set; }
public LocalValueEntry(T value) => _value = value;
public Optional<T> Value => _value;
public BindingPriority ValuePriority => BindingPriority.LocalValue; public BindingPriority ValuePriority => BindingPriority.LocalValue;
Optional<object> IValue.Value => Value.ToObject(); Optional<object> IValue.Value => Value.ToObject();
public void SetValue(T value) => _value = value;
public ConstantValueEntry<T> ToConstantValueEntry(StyledPropertyBase<T> property)
{
return new ConstantValueEntry<T>(property, Value.Value, BindingPriority.LocalValue);
}
} }
} }

56
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@ -40,6 +40,18 @@ namespace Avalonia.PropertyStore
} }
} }
public PriorityValue(
IAvaloniaObject owner,
StyledPropertyBase<T> property,
IValueSink sink,
LocalValueEntry<T> existing)
: this(owner, property, sink)
{
_localValue = existing.Value;
Value = _localValue;
ValuePriority = BindingPriority.LocalValue;
}
public StyledPropertyBase<T> Property { get; } public StyledPropertyBase<T> Property { get; }
public Optional<T> Value { get; private set; } public Optional<T> Value { get; private set; }
public BindingPriority ValuePriority { get; private set; } public BindingPriority ValuePriority { get; private set; }
@ -77,7 +89,11 @@ namespace Avalonia.PropertyStore
Optional<TValue> oldValue, Optional<TValue> oldValue,
BindingValue<TValue> newValue) BindingValue<TValue> newValue)
{ {
_localValue = default; if (priority == BindingPriority.LocalValue)
{
_localValue = default;
}
UpdateEffectiveValue(); UpdateEffectiveValue();
} }
@ -108,29 +124,37 @@ namespace Avalonia.PropertyStore
var reachedLocalValues = false; var reachedLocalValues = false;
var value = default(Optional<T>); var value = default(Optional<T>);
for (var i = _entries.Count - 1; i >= 0; --i) if (_entries.Count > 0)
{ {
var entry = _entries[i]; for (var i = _entries.Count - 1; i >= 0; --i)
if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue)
{ {
reachedLocalValues = true; var entry = _entries[i];
if (_localValue.HasValue) if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue)
{ {
value = _localValue; reachedLocalValues = true;
ValuePriority = BindingPriority.LocalValue;
break; if (_localValue.HasValue)
{
value = _localValue;
ValuePriority = BindingPriority.LocalValue;
break;
}
} }
}
if (entry.Value.HasValue) if (entry.Value.HasValue)
{ {
value = entry.Value; value = entry.Value;
ValuePriority = entry.Priority; ValuePriority = entry.Priority;
break; break;
}
} }
} }
else if (_localValue.HasValue)
{
value = _localValue;
ValuePriority = BindingPriority.LocalValue;
}
if (value != Value) if (value != Value)
{ {

9
src/Avalonia.Base/ValueStore.cs

@ -170,14 +170,12 @@ namespace Avalonia
if (priority == BindingPriority.LocalValue) if (priority == BindingPriority.LocalValue)
{ {
var old = l.Value; var old = l.Value;
l.Value = value; l.SetValue(value);
_sink.ValueChanged(property, priority, old, value); _sink.ValueChanged(property, priority, old, value);
} }
else else
{ {
var existing = l.ToConstantValueEntry(property); var priorityValue = new PriorityValue<T>(_owner, property, this, l);
var priorityValue = new PriorityValue<T>(_owner, property, this, existing);
priorityValue.SetValue(value, priority);
_values.SetValue(property, priorityValue); _values.SetValue(property, priorityValue);
} }
} }
@ -205,8 +203,7 @@ namespace Avalonia
} }
else if (slot is LocalValueEntry<T> l) else if (slot is LocalValueEntry<T> l)
{ {
var existing = l.ToConstantValueEntry(property); priorityValue = new PriorityValue<T>(_owner, property, this, l);
priorityValue = new PriorityValue<T>(_owner, property, this, existing);
} }
else else
{ {

31
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -101,6 +101,37 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("foodefault", target.GetValue(property)); Assert.Equal("foodefault", target.GetValue(property));
} }
[Fact]
public void Completing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue()
{
var target = new Class1();
var source = new BehaviorSubject<string>("bar");
target.SetValue(Class1.FooProperty, "foo");
var sub = target.Bind(Class1.FooProperty, source);
Assert.Equal("bar", target.GetValue(Class1.FooProperty));
sub.Dispose();
Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
}
[Fact]
public void Completing_Animation_Binding_Reverts_To_Set_LocalValue()
{
var target = new Class1();
var source = new Subject<string>();
var property = Class1.FooProperty;
target.SetValue(property, "foo");
target.Bind(property, source, BindingPriority.Animation);
source.OnNext("bar");
source.OnCompleted();
Assert.Equal("foo", target.GetValue(property));
}
[Fact] [Fact]
public void Setting_Style_Value_Overrides_Binding_Permanently() public void Setting_Style_Value_Overrides_Binding_Permanently()
{ {

Loading…
Cancel
Save