Browse Source

Merge pull request #2114 from donandren/issues/relsourcebindingproblem

Failing tests for Relative Source in Binding and fixes
pull/2123/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
96190158e2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  2. 16
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs
  3. 100
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_RelativeSource.cs
  4. 58
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs

12
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -88,18 +88,21 @@ namespace Avalonia.Data.Core
_subscriber(value);
}
protected void ValueChanged(object value)
protected void ValueChanged(object value) => ValueChanged(value, true);
private void ValueChanged(object value, bool notify)
{
var notification = value as BindingNotification;
if (notification == null)
{
LastValue = new WeakReference(value);
if (Next != null)
{
Next.Target = new WeakReference(value);
Next.Target = LastValue;
}
else
else if (notify)
{
_subscriber(value);
}
@ -110,7 +113,7 @@ namespace Avalonia.Data.Core
if (Next != null)
{
Next.Target = new WeakReference(notification.Value);
Next.Target = LastValue;
}
if (Next == null || notification.Error != null)
@ -136,6 +139,7 @@ namespace Avalonia.Data.Core
}
else
{
ValueChanged(AvaloniaProperty.UnsetValue, notify:false);
_listening = false;
}
}

16
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/RelativeSourceExtension.cs

@ -1,13 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Data;
using Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
using Portable.Xaml.Markup;
using System;
public class RelativeSourceExtension : MarkupExtension
{
public RelativeSourceExtension()
@ -24,10 +23,19 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
return new RelativeSource
{
Mode = Mode,
AncestorType = AncestorType,
AncestorLevel = AncestorLevel,
Tree = Tree,
};
}
[ConstructorArgument("mode")]
public RelativeSourceMode Mode { get; set; }
public RelativeSourceMode Mode { get; set; } = RelativeSourceMode.FindAncestor;
public Type AncestorType { get; set; }
public TreeType Tree { get; set; }
public int AncestorLevel { get; set; } = 1;
}
}

100
tests/Avalonia.Markup.UnitTests/Data/BindingTests_RelativeSource.cs

@ -1,9 +1,13 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Reactive.Subjects;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.Data.Core;
using Avalonia.Markup.Parsers;
using Avalonia.UnitTests;
using Xunit;
@ -162,5 +166,99 @@ namespace Avalonia.Markup.UnitTests.Data
decorator2.Child = target;
Assert.Equal("decorator2", target.Text);
}
[Fact]
public void Should_Update_When_Detached_And_Attached_To_Visual_Tree_With_BindingPath()
{
TextBlock target;
Decorator decorator1;
Decorator decorator2;
var viewModel = new { Value = "Foo" };
var root1 = new TestRoot
{
Child = decorator1 = new Decorator
{
Name = "decorator1",
Child = target = new TextBlock(),
},
DataContext = viewModel
};
var root2 = new TestRoot
{
Child = decorator2 = new Decorator
{
Name = "decorator2",
},
DataContext = viewModel
};
var binding = new Binding
{
Path = "DataContext.Value",
RelativeSource = new RelativeSource
{
AncestorType = typeof(Decorator),
}
};
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("Foo", target.Text);
decorator1.Child = null;
Assert.Null(target.Text);
decorator2.Child = target;
Assert.Equal("Foo", target.Text);
}
[Fact]
public void Should_Update_When_Detached_And_Attached_To_Visual_Tree_With_ComplexBindingPath()
{
TextBlock target;
Decorator decorator1;
Decorator decorator2;
var vm = new { Foo = new { Value = "Foo" } };
var root1 = new TestRoot
{
Child = decorator1 = new Decorator
{
Name = "decorator1",
Child = target = new TextBlock(),
},
DataContext = vm
};
var root2 = new TestRoot
{
Child = decorator2 = new Decorator
{
Name = "decorator2",
},
DataContext = vm
};
var binding = new Binding
{
Path = "DataContext.Foo.Value",
RelativeSource = new RelativeSource
{
AncestorType = typeof(Decorator),
}
};
target.Bind(TextBox.TextProperty, binding);
Assert.Equal("Foo", target.Text);
decorator1.Child = null;
Assert.Null(target.Text);
decorator2.Child = target;
Assert.Equal("Foo", target.Text);
}
}
}

58
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs

@ -1,10 +1,10 @@
using Avalonia.Data;
using Avalonia.Markup.Parsers;
using System;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Markup.Parsers;
using Xunit;
namespace Avalonia.Markup.UnitTests.Parsers
@ -38,5 +38,55 @@ namespace Avalonia.Markup.UnitTests.Parsers
GC.KeepAlive(data);
}
[Fact]
public void Should_Update_Value_After_Root_Changes()
{
var root = new { DataContext = new { Value = "Foo" } };
var subject = new Subject<object>();
var obs = ExpressionObserverBuilder.Build(subject, "DataContext.Value");
var values = new List<object>();
obs.Subscribe(v => values.Add(v));
subject.OnNext(root);
subject.OnNext(null);
subject.OnNext(root);
Assert.Equal("Foo", values[0]);
Assert.IsType<BindingNotification>(values[1]);
var bn = values[1] as BindingNotification;
Assert.Equal(AvaloniaProperty.UnsetValue, bn.Value);
Assert.Equal(BindingErrorType.Error, bn.ErrorType);
Assert.Equal(3, values.Count);
Assert.Equal("Foo", values[2]);
}
[Fact]
public void Should_Update_Value_After_Root_Changes_With_ComplexPath()
{
var root = new { DataContext = new { Foo = new { Value = "Foo" } } };
var subject = new Subject<object>();
var obs = ExpressionObserverBuilder.Build(subject, "DataContext.Foo.Value");
var values = new List<object>();
obs.Subscribe(v => values.Add(v));
subject.OnNext(root);
subject.OnNext(null);
subject.OnNext(root);
Assert.Equal("Foo", values[0]);
Assert.IsType<BindingNotification>(values[1]);
var bn = values[1] as BindingNotification;
Assert.Equal(AvaloniaProperty.UnsetValue, bn.Value);
Assert.Equal(BindingErrorType.Error, bn.ErrorType);
Assert.Equal(3, values.Count);
Assert.Equal("Foo", values[2]);
}
}
}

Loading…
Cancel
Save