Browse Source

Add BindingOperations.GetBindingExpressionBase. (#16214)

With basic unit tests.

Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/16258/head
Steven Kirk 2 years ago
committed by GitHub
parent
commit
640a9dd854
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      src/Avalonia.Base/Data/BindingOperations.cs
  2. 36
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  3. 147
      tests/Avalonia.Base.UnitTests/Data/BindingOperationsTests.cs

19
src/Avalonia.Base/Data/BindingOperations.cs

@ -101,6 +101,25 @@ namespace Avalonia.Data
return Apply(target, property, binding);
}
/// <summary>
/// Retrieves the <see cref="BindingExpressionBase"/> that is currently active on the
/// specified property.
/// </summary>
/// <param name="target">
/// The <see cref="AvaloniaObject"/> from which to retrieve the binding expression.
/// </param>
/// <param name="property">
/// The binding target property from which to retrieve the binding expression.
/// </param>
/// <returns>
/// The <see cref="BindingExpressionBase"/> object that is active on the given property or
/// null if no binding expression is active on the given property.
/// </returns>
public static BindingExpressionBase? GetBindingExpressionBase(AvaloniaObject target, AvaloniaProperty property)
{
return target.GetValueStore().GetExpression(property);
}
private sealed class TwoWayBindingDisposable : IDisposable
{
private readonly IDisposable _toTargetSubscription;

36
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -292,6 +292,42 @@ namespace Avalonia.PropertyStore
return property.GetDefaultValue(Owner);
}
public BindingExpressionBase? GetExpression(AvaloniaProperty property)
{
var evaluatedLocalValue = false;
bool TryGetLocalValue(out BindingExpressionBase? result)
{
if (!evaluatedLocalValue)
{
evaluatedLocalValue = true;
if (_localValueBindings?.TryGetValue(property.Id, out var o) == true)
{
result = o as BindingExpressionBase;
return true;
}
}
result = null;
return false;
}
for (var i = _frames.Count - 1; i >= 0; --i)
{
var frame = _frames[i];
if (frame.Priority > BindingPriority.LocalValue && TryGetLocalValue(out var localExpression))
return localExpression;
if (frame.TryGetEntryIfActive(property, out var entry, out _))
return entry as BindingExpressionBase;
}
TryGetLocalValue(out var e);
return e;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static EffectiveValue<T> CastEffectiveValue<T>(EffectiveValue value)
{

147
tests/Avalonia.Base.UnitTests/Data/BindingOperationsTests.cs

@ -0,0 +1,147 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Base.UnitTests.Data;
public class BindingOperationsTests
{
[Fact]
public void GetBindingExpressionBase_Returns_Null_When_Not_Bound()
{
var target = new Control();
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.Null(expression);
}
[Theory]
[InlineData(BindingPriority.Animation)]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
[InlineData(BindingPriority.StyleTrigger)]
public void GetBindingExpressionBase_Returns_Expression_When_Bound(BindingPriority priority)
{
var data = new { Tag = "foo" };
var target = new Control { DataContext = data };
var binding = new Binding("Tag") { Priority = priority };
target.Bind(Control.TagProperty, binding);
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Expression_When_Bound_Locally_With_Binding_Error()
{
// Target has no data context so binding will fail.
var target = new Control();
var binding = new Binding("Tag");
target.Bind(Control.TagProperty, binding);
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Expression_When_Bound_To_MultiBinding()
{
var data = new { Tag = "foo" };
var target = new Control { DataContext = data };
var binding = new MultiBinding
{
Converter = new FuncMultiValueConverter<object, string>(x => string.Join(',', x)),
Bindings =
{
new Binding("Tag"),
new Binding("Tag"),
}
};
target.Bind(Control.TagProperty, binding);
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Binding_When_Bound_Via_ControlTheme()
{
var target = new Control();
var binding = new Binding("Tag");
var theme = new ControlTheme(typeof(Control))
{
Setters = { new Setter(Control.TagProperty, binding) },
};
target.Theme = theme;
var root = new TestRoot(target);
root.UpdateLayout();
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Binding_When_Bound_Via_ControlTheme_TemplateBinding()
{
var target = new Control();
var binding = new TemplateBinding(Control.TagProperty);
var theme = new ControlTheme(typeof(Control))
{
Setters = { new Setter(Control.TagProperty, binding) },
};
target.Theme = theme;
var root = new TestRoot(target);
root.UpdateLayout();
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Binding_When_Bound_Via_ControlTheme_Style()
{
var target = new Control { Classes = { "foo" } };
var binding = new Binding("Tag");
var theme = new ControlTheme(typeof(Control))
{
Children =
{
new Style(x => x.Nesting().Class("foo"))
{
Setters = { new Setter(Control.TagProperty, binding) },
},
}
};
target.Theme = theme;
var root = new TestRoot(target);
root.UpdateLayout();
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
[Fact]
public void GetBindingExpressionBase_Returns_Binding_When_Bound_Via_Style()
{
var target = new Control();
var binding = new Binding("Tag");
var style = new Style(x => x.OfType<Control>())
{
Setters = { new Setter(Control.TagProperty, binding) },
};
var root = new TestRoot();
root.Styles.Add(style);
root.Child = target;
root.UpdateLayout();
var expression = BindingOperations.GetBindingExpressionBase(target, Control.TagProperty);
Assert.NotNull(expression);
}
}
Loading…
Cancel
Save