From 843eb65dea8dc4d462dc7fd575fc0955f9d3c9b1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 3 Oct 2015 11:49:05 +0200 Subject: [PATCH] Bind DataContext to Parent.DataContext. Need to special-case binding to DataContext as previously it was trying to bind to itself. When binding to DataContext, we're binding to the *parent* DataContext. --- .../Binding/XamlBinding.cs | 41 ++++++++++++++----- .../Binding/XamlBindingTests.cs | 30 ++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/Markup/Perspex.Markup.Xaml/Binding/XamlBinding.cs b/src/Markup/Perspex.Markup.Xaml/Binding/XamlBinding.cs index 2bf9b82bcf..cb2c999f7a 100644 --- a/src/Markup/Perspex.Markup.Xaml/Binding/XamlBinding.cs +++ b/src/Markup/Perspex.Markup.Xaml/Binding/XamlBinding.cs @@ -29,21 +29,42 @@ namespace Perspex.Markup.Xaml.Binding public void Bind(IObservablePropertyBag instance, PerspexProperty property) { - if (property == Control.DataContextProperty && instance.InheritanceParent != null) + var subject = new ExpressionSubject(CreateExpressionObserver(instance, property)); + + if (subject != null) { - instance = instance.InheritanceParent as IObservablePropertyBag ?? instance; + Bind(instance, property, subject); } - - var subject = new ExpressionSubject(CreateExpressionObserver(instance)); - Bind(instance, property, subject); } - public ExpressionObserver CreateExpressionObserver(IObservablePropertyBag instance) + public ExpressionObserver CreateExpressionObserver( + IObservablePropertyBag instance, + PerspexProperty property) { - var result = new ExpressionObserver(null, SourcePropertyPath); - var dataContext = instance.GetObservable(Control.DataContextProperty); - dataContext.Subscribe(x => result.Root = x); - return result; + IObservable dataContext = null; + + if (property != Control.DataContextProperty) + { + dataContext = instance.GetObservable(Control.DataContextProperty); + } + else + { + var parent = instance.InheritanceParent as IObservablePropertyBag; + + if (parent != null) + { + dataContext = parent.GetObservable(Control.DataContextProperty); + } + } + + if (dataContext != null) + { + var result = new ExpressionObserver(null, SourcePropertyPath); + dataContext.Subscribe(x => result.Root = x); + return result; + } + + return null; } internal void Bind(IObservablePropertyBag target, PerspexProperty property, ISubject subject) diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Binding/XamlBindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Binding/XamlBindingTests.cs index 97e0dc7741..f78a7ed4ae 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Binding/XamlBindingTests.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Binding/XamlBindingTests.cs @@ -113,6 +113,36 @@ namespace Perspex.Markup.Xaml.UnitTests.Binding BindingPriority.LocalValue)); } + [Fact] + public void DataContext_Binding_Should_Use_Parent_DataContext() + { + var parentDataContext = Mock.Of(x => x.Header == (object)"Foo"); + + var parent = new Decorator + { + Child = new Control(), + DataContext = parentDataContext, + }; + + var binding = new XamlBinding + { + SourcePropertyPath = "Header", + }; + + binding.Bind(parent.Child, Control.DataContextProperty); + + Assert.Equal("Foo", parent.Child.DataContext); + + parentDataContext = Mock.Of(x => x.Header == (object)"Bar"); + parent.DataContext = parentDataContext; + Assert.Equal("Bar", parent.Child.DataContext); + } + + private Mock CreateTarget(object dataContext) + { + return CreateTarget(dataContext: Observable.Never().StartWith(dataContext)); + } + private Mock CreateTarget( IObservable dataContext = null, IObservable text = null)