diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs index 086257f24c..649596a74e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs @@ -115,6 +115,10 @@ namespace Avalonia.Markup.Xaml.Data anchor, enableDataValidation); } + else if (RelativeSource.Mode == RelativeSourceMode.Self) + { + observer = CreateSourceObserver(target, pathInfo.Path, enableDataValidation); + } else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent) { observer = CreateTemplatedParentObserver(target, pathInfo.Path); diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs index 6771aaf644..c0eb581af2 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs @@ -5,6 +5,7 @@ namespace Avalonia.Markup.Xaml.Data { public enum RelativeSourceMode { + Self, DataContext, TemplatedParent, } diff --git a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs index dab5756976..51afe1ffbf 100644 --- a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs +++ b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs @@ -26,7 +26,7 @@ namespace Avalonia.Markup.Data _nodes = null; } - public bool HasNodes => _nodes.Count > 0; + public bool HasNodes => _nodes?.Count > 0; public void AddNode(string node) => _nodes.Add(node); public void Commit(string expression) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index 4ad740bab0..f820f7d5ab 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -96,6 +96,7 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs new file mode 100644 index 0000000000..e0d16a9563 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs @@ -0,0 +1,63 @@ +// 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 Moq; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Markup.Xaml.Data; +using Avalonia.Styling; +using Xunit; +using System.Reactive.Disposables; + +namespace Avalonia.Markup.Xaml.UnitTests.Data +{ + public class BindingTests_Self + { + [Fact] + public void Binding_To_Property_On_Self_Should_Work() + { + var target = new TextBlock + { + Tag = "Hello World!", + [!TextBlock.TextProperty] = new Binding("Tag") + { + RelativeSource = new RelativeSource(RelativeSourceMode.Self) + }, + }; + + Assert.Equal("Hello World!", target.Text); + } + + [Fact] + public void TwoWay_Binding_To_Property_On_Self_Should_Work() + { + var target = new TextBlock + { + Tag = "Hello World!", + [!TextBlock.TextProperty] = new Binding("Tag", BindingMode.TwoWay) + { + RelativeSource = new RelativeSource(RelativeSourceMode.Self) + }, + }; + + Assert.Equal("Hello World!", target.Text); + target.Text = "Goodbye cruel world :("; + Assert.Equal("Goodbye cruel world :(", target.Text); + } + + private Mock CreateTarget( + ITemplatedControl templatedParent = null, + string text = null) + { + var result = new Mock(); + + result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); + result.Setup(x => x.GetValue((AvaloniaProperty)Control.TemplatedParentProperty)).Returns(templatedParent); + result.Setup(x => x.GetValue((AvaloniaProperty)TextBox.TextProperty)).Returns(text); + result.Setup(x => x.Bind(It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(Disposable.Empty); + return result; + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index 868471466a..a66d6ac6d8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -145,5 +145,49 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal("foo", border.DataContext); } } + + [Fact(Skip = "OmniXaml doesn't support nested markup extensions. #119")] + public void Binding_To_Self_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } + + [Fact] + public void Longform_Binding_To_Self_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } } }