Browse Source

Make broken DataContext bindings produce null.

This prevents incorrect DataContexts cascading down to children when the
DataContext binding is invalid, e.g. when things are being set up.
pull/545/head
Steven Kirk 10 years ago
parent
commit
0b28e10f21
  1. 15
      src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
  2. 2
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  3. 28
      src/Markup/Avalonia.Markup/Data/ExpressionSubject.cs
  4. 8
      tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs

15
src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

@ -20,6 +20,7 @@ namespace Avalonia.Markup.Xaml.Data
/// </summary>
public Binding()
{
FallbackValue = AvaloniaProperty.UnsetValue;
}
/// <summary>
@ -27,6 +28,7 @@ namespace Avalonia.Markup.Xaml.Data
/// </summary>
/// <param name="path">The binding path.</param>
public Binding(string path)
: this()
{
Path = path;
}
@ -122,12 +124,23 @@ namespace Avalonia.Markup.Xaml.Data
throw new NotSupportedException();
}
var fallback = FallbackValue;
// If we're binding to DataContext and our fallback is UnsetValue then override
// the fallback value to null, as broken bindings to DataContext must reset the
// DataContext in order to not propagate incorrect DataContexts to child controls.
// See Avalonia.Markup.Xaml.UnitTests.Data.DataContext_Binding_Should_Produce_Correct_Results.
if (targetProperty == Control.DataContextProperty && fallback == AvaloniaProperty.UnsetValue)
{
fallback = null;
}
var subject = new ExpressionSubject(
observer,
targetProperty?.PropertyType ?? typeof(object),
fallback,
Converter ?? DefaultValueConverter.Instance,
ConverterParameter,
FallbackValue,
Priority);
return new InstancedBinding(subject, Mode, Priority);

2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -36,7 +36,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public object FallbackValue { get; set; }
public object FallbackValue { get; set; } = AvaloniaProperty.UnsetValue;
public BindingMode Mode { get; set; }
public string Path { get; set; }
public BindingPriority Priority { get; set; } = BindingPriority.LocalValue;

28
src/Markup/Avalonia.Markup/Data/ExpressionSubject.cs

@ -41,16 +41,36 @@ namespace Avalonia.Markup.Data
/// <param name="converterParameter">
/// A parameter to pass to <paramref name="converter"/>.
/// </param>
/// <param name="priority">The binding priority.</param>
public ExpressionSubject(
ExpressionObserver inner,
Type targetType,
IValueConverter converter,
object converterParameter = null,
BindingPriority priority = BindingPriority.LocalValue)
: this(inner, targetType, AvaloniaProperty.UnsetValue, converter, converterParameter, priority)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
/// </summary>
/// <param name="inner">The <see cref="ExpressionObserver"/>.</param>
/// <param name="targetType">The type to convert the value to.</param>
/// <param name="fallbackValue">
/// The value to use when the binding is unable to produce a value.
/// </param>
/// <param name="converter">The value converter to use.</param>
/// <param name="converterParameter">
/// A parameter to pass to <paramref name="converter"/>.
/// </param>
/// <param name="priority">The binding priority.</param>
public ExpressionSubject(
ExpressionObserver inner,
Type targetType,
Type targetType,
object fallbackValue,
IValueConverter converter,
object converterParameter = null,
object fallbackValue = null,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(inner != null);
@ -117,7 +137,7 @@ namespace Avalonia.Markup.Data
_inner.Expression,
error.Exception.Message);
if (_fallbackValue != null)
if (_fallbackValue != AvaloniaProperty.UnsetValue)
{
if (TypeUtilities.TryConvert(
type,
@ -162,7 +182,7 @@ namespace Avalonia.Markup.Data
ConverterParameter,
CultureInfo.CurrentUICulture);
if (_fallbackValue != null &&
if (_fallbackValue != AvaloniaProperty.UnsetValue &&
(converted == AvaloniaProperty.UnsetValue ||
converted is BindingError))
{

8
tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs

@ -123,8 +123,8 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionSubject(
new ExpressionObserver(data, "DoubleValue"),
typeof(string),
DefaultValueConverter.Instance,
fallbackValue: "9.8");
"9.8",
DefaultValueConverter.Instance);
target.OnNext("foo");
@ -162,7 +162,7 @@ namespace Avalonia.Markup.UnitTests.Data
new ExpressionObserver(data, "DoubleValue"),
typeof(string),
converter.Object,
"foo");
converterParameter: "foo");
target.Subscribe(_ => { });
@ -178,7 +178,7 @@ namespace Avalonia.Markup.UnitTests.Data
new ExpressionObserver(data, "DoubleValue"),
typeof(string),
converter.Object,
"foo");
converterParameter: "foo");
target.OnNext("bar");

Loading…
Cancel
Save