Browse Source

Merge pull request #848 from AvaloniaUI/features/relativesource-self

Added RelativeSource=Self binding mode.
Steven Kirk 9 years ago
parent
commit
7b46305edb

+ 4 - 0
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);

+ 1 - 0
src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs

@@ -5,6 +5,7 @@ namespace Avalonia.Markup.Xaml.Data
 {
     public enum RelativeSourceMode
     {
+        Self,
         DataContext,
         TemplatedParent,
     }

+ 1 - 1
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)

+ 1 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -96,6 +96,7 @@
     <Compile Include="Data\BindingTests_DataValidation.cs" />
     <Compile Include="Data\BindingTests_Source.cs" />
     <Compile Include="Data\BindingTests_ElementName.cs" />
+    <Compile Include="Data\BindingTests_Self.cs" />
     <Compile Include="Data\MultiBindingTests.cs" />
     <Compile Include="Data\BindingTests_TemplatedParent.cs" />
     <Compile Include="Data\BindingTests.cs" />

+ 63 - 0
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<IControl> CreateTarget(
+            ITemplatedControl templatedParent = null,
+            string text = null)
+        {
+            var result = new Mock<IControl>();
+
+            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<AvaloniaProperty>(), It.IsAny<IObservable<object>>(), It.IsAny<BindingPriority>()))
+                .Returns(Disposable.Empty);
+            return result;
+        }
+    }
+}

+ 44 - 0
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 = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <TextBlock Name='textblock' Text='{Binding Tag, RelativeSource={RelativeSource Self}}'/>
+</Window>";
+                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 = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <TextBlock Name='textblock' Tag='foo'>
+        <TextBlock.Text>
+            <Binding RelativeSource='{RelativeSource Self}' Path='Tag'/>
+        </TextBlock.Text>
+    </TextBlock>
+</Window>";
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var textBlock = (TextBlock)window.Content;
+
+                window.ApplyTemplate();
+
+                Assert.Equal("foo", textBlock.Text);
+            }
+        }
     }
 }