Browse Source

Merge branch 'master' into perf-bindingoperations-closures

pull/3265/head
Dariusz Komosiński 7 years ago
committed by GitHub
parent
commit
a43474ac3d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  2. 7
      src/Markup/Avalonia.Markup/Data/Binding.cs
  3. 16
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  4. 28
      tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
  5. 18
      tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs
  6. 30
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

13
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -21,6 +21,7 @@ namespace Avalonia.Data.Core
private readonly ExpressionObserver _inner;
private readonly Type _targetType;
private readonly object _fallbackValue;
private readonly object _targetNullValue;
private readonly BindingPriority _priority;
InnerListener _innerListener;
WeakReference<object> _value;
@ -51,7 +52,7 @@ namespace Avalonia.Data.Core
IValueConverter converter,
object converterParameter = null,
BindingPriority priority = BindingPriority.LocalValue)
: this(inner, targetType, AvaloniaProperty.UnsetValue, converter, converterParameter, priority)
: this(inner, targetType, AvaloniaProperty.UnsetValue, AvaloniaProperty.UnsetValue, converter, converterParameter, priority)
{
}
@ -63,6 +64,9 @@ namespace Avalonia.Data.Core
/// <param name="fallbackValue">
/// The value to use when the binding is unable to produce a value.
/// </param>
/// <param name="targetNullValue">
/// The value to use when the binding result is null.
/// </param>
/// <param name="converter">The value converter to use.</param>
/// <param name="converterParameter">
/// A parameter to pass to <paramref name="converter"/>.
@ -72,6 +76,7 @@ namespace Avalonia.Data.Core
ExpressionObserver inner,
Type targetType,
object fallbackValue,
object targetNullValue,
IValueConverter converter,
object converterParameter = null,
BindingPriority priority = BindingPriority.LocalValue)
@ -85,6 +90,7 @@ namespace Avalonia.Data.Core
Converter = converter;
ConverterParameter = converterParameter;
_fallbackValue = fallbackValue;
_targetNullValue = targetNullValue;
_priority = priority;
}
@ -196,6 +202,11 @@ namespace Avalonia.Data.Core
/// <inheritdoc/>
private object ConvertValue(object value)
{
if (value == null && _targetNullValue != AvaloniaProperty.UnsetValue)
{
return _targetNullValue;
}
if (value == BindingOperations.DoNothing)
{
return value;

7
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -26,6 +26,7 @@ namespace Avalonia.Data
public Binding()
{
FallbackValue = AvaloniaProperty.UnsetValue;
TargetNullValue = AvaloniaProperty.UnsetValue;
}
/// <summary>
@ -60,6 +61,11 @@ namespace Avalonia.Data
/// </summary>
public object FallbackValue { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding result is null.
/// </summary>
public object TargetNullValue { get; set; }
/// <summary>
/// Gets or sets the binding mode.
/// </summary>
@ -209,6 +215,7 @@ namespace Avalonia.Data
observer,
targetType,
fallback,
TargetNullValue,
converter ?? DefaultValueConverter.Instance,
ConverterParameter,
Priority);

16
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -37,6 +37,11 @@ namespace Avalonia.Data
/// </summary>
public object FallbackValue { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding result is null.
/// </summary>
public object TargetNullValue { get; set; }
/// <summary>
/// Gets or sets the binding mode.
/// </summary>
@ -57,6 +62,12 @@ namespace Avalonia.Data
/// </summary>
public string StringFormat { get; set; }
public MultiBinding()
{
FallbackValue = AvaloniaProperty.UnsetValue;
TargetNullValue = AvaloniaProperty.UnsetValue;
}
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
@ -102,6 +113,11 @@ namespace Avalonia.Data
var culture = CultureInfo.CurrentCulture;
var converted = converter.Convert(values, targetType, ConverterParameter, culture);
if (converted == null)
{
converted = TargetNullValue;
}
if (converted == AvaloniaProperty.UnsetValue)
{
converted = FallbackValue;

28
tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs

@ -139,6 +139,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
ExpressionObserver.Create(data, o => o.StringValue),
typeof(int),
42,
AvaloniaProperty.UnsetValue,
DefaultValueConverter.Instance);
var result = await target.Take(1);
@ -160,6 +161,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
ExpressionObserver.Create(data, o => o.StringValue, true),
typeof(int),
42,
AvaloniaProperty.UnsetValue,
DefaultValueConverter.Instance);
var result = await target.Take(1);
@ -181,6 +183,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
ExpressionObserver.Create(data, o => o.StringValue),
typeof(int),
"bar",
AvaloniaProperty.UnsetValue,
DefaultValueConverter.Instance);
var result = await target.Take(1);
@ -203,6 +206,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
ExpressionObserver.Create(data, o => o.StringValue, true),
typeof(int),
"bar",
AvaloniaProperty.UnsetValue,
DefaultValueConverter.Instance);
var result = await target.Take(1);
@ -238,6 +242,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
ExpressionObserver.Create(data, o => o.DoubleValue),
typeof(string),
"9.8",
AvaloniaProperty.UnsetValue,
DefaultValueConverter.Instance);
target.OnNext("foo");
@ -353,6 +358,29 @@ namespace Avalonia.Base.UnitTests.Data.Core
GC.KeepAlive(data);
}
[Fact]
public async Task Null_Value_Should_Use_TargetNullValue()
{
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(
ExpressionObserver.Create(data, o => o.StringValue),
typeof(string),
AvaloniaProperty.UnsetValue,
"bar",
DefaultValueConverter.Instance);
object result = null;
target.Subscribe(x => result = x);
Assert.Equal("foo", result);
data.StringValue = null;
Assert.Equal("bar", result);
GC.KeepAlive(data);
}
private class Class1 : NotifyingBase
{
private string _stringValue;

18
tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs

@ -405,6 +405,24 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(42, target.Value);
}
[Fact]
public void Should_Return_TargetNullValue_When_Value_Is_Null()
{
var target = new TextBlock();
var source = new Source { Foo = null };
var binding = new Binding
{
Source = source,
Path = "Foo",
TargetNullValue = "(null)",
};
target.Bind(TextBlock.TextProperty, binding);
Assert.Equal("(null)", target.Text);
}
[Fact]
public void Null_Path_Should_Bind_To_DataContext()
{

30
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

@ -94,6 +94,28 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal("fallback", target.Text);
}
[Fact]
public void Should_Return_TargetNullValue_When_Value_Is_Null()
{
var target = new TextBlock();
var binding = new MultiBinding
{
Converter = new NullValueConverter(),
Bindings = new[]
{
new Binding { Path = "A" },
new Binding { Path = "B" },
new Binding { Path = "C" },
},
TargetNullValue = "(null)",
};
target.Bind(TextBlock.TextProperty, binding);
Assert.Equal("(null)", target.Text);
}
private class ConcatConverter : IMultiValueConverter
{
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
@ -109,5 +131,13 @@ namespace Avalonia.Markup.UnitTests.Data
return AvaloniaProperty.UnsetValue;
}
}
private class NullValueConverter : IMultiValueConverter
{
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
}

Loading…
Cancel
Save