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>
{
public LocalValueEntry(T value) => Value = value;
public Optional<T> Value { get; set; }
private T _value;
public LocalValueEntry(T value) => _value = value;
public Optional<T> Value => _value;
public BindingPriority ValuePriority => BindingPriority.LocalValue;
Optional<object> IValue.Value => Value.ToObject();
public ConstantValueEntry<T> ToConstantValueEntry(StyledPropertyBase<T> property)
{
return new ConstantValueEntry<T>(property, Value.Value, BindingPriority.LocalValue);
}
public void SetValue(T value) => _value = value;
}
}

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 Optional<T> Value { get; private set; }
public BindingPriority ValuePriority { get; private set; }
@ -77,7 +89,11 @@ namespace Avalonia.PropertyStore
Optional<TValue> oldValue,
BindingValue<TValue> newValue)
{
_localValue = default;
if (priority == BindingPriority.LocalValue)
{
_localValue = default;
}
UpdateEffectiveValue();
}
@ -108,29 +124,37 @@ namespace Avalonia.PropertyStore
var reachedLocalValues = false;
var value = default(Optional<T>);
for (var i = _entries.Count - 1; i >= 0; --i)
if (_entries.Count > 0)
{
var entry = _entries[i];
if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue)
for (var i = _entries.Count - 1; i >= 0; --i)
{
reachedLocalValues = true;
var entry = _entries[i];
if (_localValue.HasValue)
if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue)
{
value = _localValue;
ValuePriority = BindingPriority.LocalValue;
break;
reachedLocalValues = true;
if (_localValue.HasValue)
{
value = _localValue;
ValuePriority = BindingPriority.LocalValue;
break;
}
}
}
if (entry.Value.HasValue)
{
value = entry.Value;
ValuePriority = entry.Priority;
break;
if (entry.Value.HasValue)
{
value = entry.Value;
ValuePriority = entry.Priority;
break;
}
}
}
else if (_localValue.HasValue)
{
value = _localValue;
ValuePriority = BindingPriority.LocalValue;
}
if (value != Value)
{

9
src/Avalonia.Base/ValueStore.cs

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

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

@ -101,6 +101,37 @@ namespace Avalonia.Base.UnitTests
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]
public void Setting_Style_Value_Overrides_Binding_Permanently()
{

Loading…
Cancel
Save