using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reactive.Subjects; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Converters; using Avalonia.Data.Core; using Avalonia.Input; using Avalonia.Markup.Data; using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings; using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; using Avalonia.Metadata; using Avalonia.UnitTests; using Xunit; namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions { public class CompiledBindingExtensionTests { [Fact] public void ResolvesClrPropertyBasedOnDataContextType() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesClrPropertyBasedOnDataContextType_InterfaceInheritance() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesPathPassedByProperty() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesPathPassedByPropertyWithInnerItemTemplate() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("itemsControl"); var dataContext = new TestDataContext { ListProperty = { "Hello" } }; window.DataContext = dataContext; Assert.Equal(dataContext.ListProperty, textBlock.Items); } } [Fact] public void ResolvesDataTypeFromBindingProperty() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesDataTypeFromBindingProperty_TypeExtension() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesStreamTaskBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { TaskProperty = Task.FromResult("foobar") }; window.DataContext = dataContext; Assert.Equal(dataContext.TaskProperty.Result, textBlock.Text); } } [Fact] public void ResolvesStreamObservableBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); DelayedBinding.ApplyBindings(textBlock); var subject = new Subject(); var dataContext = new TestDataContext { ObservableProperty = subject }; window.DataContext = dataContext; subject.OnNext("foobar"); Assert.Equal("foobar", textBlock.Text); } } [Fact] public void ResolvesIndexerBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { ListProperty = { "A", "B", "C", "D", "E" } }; window.DataContext = dataContext; Assert.Equal(dataContext.ListProperty[3], textBlock.Text); } } [Fact] public void ResolvesArrayIndexerBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { ArrayProperty = new[] { "A", "B", "C", "D", "E" } }; window.DataContext = dataContext; Assert.Equal(dataContext.ArrayProperty[3], textBlock.Text); } } [Fact] public void ResolvesObservableIndexerBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { ObservableCollectionProperty = { "A", "B", "C", "D", "E" } }; window.DataContext = dataContext; Assert.Equal(dataContext.ObservableCollectionProperty[3], textBlock.Text); dataContext.ObservableCollectionProperty[3] = "New Value"; Assert.Equal(dataContext.ObservableCollectionProperty[3], textBlock.Text); } } [Fact] public void InfersCompiledBindingDataContextFromDataContextBinding() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); var dataContext = new TestDataContext { StringProperty = "A" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesNonIntegerIndexerBindingCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext(); dataContext.NonIntegerIndexerProperty["Test"] = "Initial Value"; window.DataContext = dataContext; Assert.Equal(dataContext.NonIntegerIndexerProperty["Test"], textBlock.Text); dataContext.NonIntegerIndexerProperty["Test"] = "New Value"; Assert.Equal(dataContext.NonIntegerIndexerProperty["Test"], textBlock.Text); } } [Fact] public void ResolvesNonIntegerIndexerBindingFromParentInterfaceCorrectly() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext(); dataContext.NonIntegerIndexerInterfaceProperty["Test"] = "Initial Value"; window.DataContext = dataContext; Assert.Equal(dataContext.NonIntegerIndexerInterfaceProperty["Test"], textBlock.Text); dataContext.NonIntegerIndexerInterfaceProperty["Test"] = "New Value"; Assert.Equal(dataContext.NonIntegerIndexerInterfaceProperty["Test"], textBlock.Text); } } [Fact] public void InfersDataTemplateTypeFromDataTypeProperty() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("target"); var dataContext = new TestDataContext(); dataContext.StringProperty = "Initial Value"; window.DataContext = dataContext; window.ApplyTemplate(); target.ApplyTemplate(); ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); } } [Fact] public void ThrowsOnUninferrableLooseDataTemplateNoDataTypeWithCompiledBindingPath() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } [Fact] public void ThrowsOnUninferrableDataTypeFromNonCompiledDataContextBindingWithCompiledBindingPath() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } [Fact] public void InfersDataTemplateTypeFromParentCollectionItemsType() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("target"); var dataContext = new TestDataContext(); dataContext.ListProperty.Add("Test"); window.DataContext = dataContext; window.ApplyTemplate(); target.ApplyTemplate(); target.Presenter.ApplyTemplate(); Assert.Equal(dataContext.ListProperty[0], (string)((ContentPresenter)target.Presenter.Panel.Children[0]).Content); } } [Fact] public void ThrowsOnUninferrableDataTemplateInItemsControlWithoutItemsBinding() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } [Fact] public void IgnoresDataTemplateTypeFromDataTypePropertyIfXDataTypeDefined() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("target"); var dataContext = new TestDataContext(); dataContext.StringProperty = "Initial Value"; window.DataContext = dataContext; window.ApplyTemplate(); target.ApplyTemplate(); ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); } } [Fact] public void InfersCustomDataTemplateBasedOnAttribute() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("target"); var dataContext = new TestDataContext(); dataContext.StringProperty = "Initial Value"; window.DataContext = dataContext; window.ApplyTemplate(); target.ApplyTemplate(); ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); } } [Fact] public void InfersCustomDataTemplateBasedOnAttributeFromBaseClass() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("target"); var dataContext = new TestDataContext(); dataContext.StringProperty = "Initial Value"; window.DataContext = dataContext; window.ApplyTemplate(); target.ApplyTemplate(); ((ContentPresenter)target.Presenter).UpdateChild(); Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); } } [Fact] public void ResolvesElementNameBinding() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("text2"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesElementNameBindingFromLongForm() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("text2"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ResolvesRelativeSourceBindingLongForm() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("text"); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); target.ApplyTemplate(); Assert.Equal("test", target.Text); } } [Fact] public void ResolvesRelativeSourceBindingFromTemplate() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var contentControl = AvaloniaRuntimeXamlLoader.Parse(xaml); contentControl.Measure(new Size(10, 10)); var result = contentControl.GetTemplateChildren().OfType().First(); Assert.Equal("Hello", result.Content); } } [Fact] public void ResolvesElementNameInTemplate() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var contentControl = AvaloniaRuntimeXamlLoader.Parse(xaml); contentControl.Measure(new Size(10, 10)); var result = contentControl.GetTemplateChildren().OfType().First(); Assert.Equal("Hello", result.Content); } } [Fact] public void Binds_To_Source() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var target = window.FindControl("text"); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); target.ApplyTemplate(); Assert.Equal("Test".Length.ToString(), target.Text); } } [Fact] public void Binds_To_Source_StaticResource() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); Assert.Equal("foobar", textBlock.Text); } } [Fact] public void Binds_To_Source_StaticResource1() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" test "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); Assert.Equal("foobar", textBlock.Text); } } [Fact] public void Binds_To_Source_StaticResource_In_ResourceDictionary() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); Assert.Equal("foobar", textBlock.Text); } } [Fact] public void Binds_To_Source_StaticResource_In_ResourceDictionary1() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" test "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); Assert.Equal("foobar", textBlock.Text); } } [Fact] public void Binds_To_Source_xStatic() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); Assert.Equal(Brushes.Red.Color, contentControl.Content); } } [Fact] public void CompilesBindingWhenRequested() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, textBlock.Text); } } [Fact] public void ThrowsOnInvalidBindingPathOnCompiledBindingEnabledViaDirective() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } [Fact] public void SupportParentInPath() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); Assert.Equal("foo", contentControl.Content); } } [Fact] public void SupportConverterWithParameter() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); window.DataContext = new TestDataContext() { StringProperty = "Foo" }; Assert.Equal("Foo+Bar", textBlock.Text); } } [Fact] public void ThrowsOnInvalidCompileBindingsDirective() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } [Fact] public void SupportCastToTypeInExpression() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var dataContext = new TestDataContext(); window.DataContext = dataContext; Assert.Equal(dataContext, contentControl.Content); } } [Fact] public void SupportCastToTypeInExpression_DifferentTypeEvaluatesToNull() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var dataContext = "foo"; window.DataContext = dataContext; Assert.Equal(null, contentControl.Content); } } [Fact] public void SupportCastToTypeInExpressionWithProperty() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, contentControl.Content); } } [Fact] public void SupportCastToTypeInExpressionWithProperty1() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, contentControl.Content); } } [Fact] public void SupportCastToTypeInExpressionWithPropertyIndexer() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var data = new TestData() { StringProperty = "Foo" }; var dataContext = new TestDataContext { ObjectsArrayProperty = new object[] { data } }; window.DataContext = dataContext; Assert.Equal(data.StringProperty, contentControl.Content); } } [Fact] public void SupportCastToTypeInExpressionWithProperty_DifferentTypeEvaluatesToNull() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.FindControl("contentControl"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(dataContext.StringProperty, contentControl.Content); window.DataContext = "foo"; Assert.Equal(null, contentControl.Content); } } [Fact] public void SupportsEmptyPath() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text); } } [Fact] public void SupportsEmptyPathWithStringFormat() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text); } } [Fact] public void SupportsDotPath() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal(typeof(TestDataContext).FullName, textBlock.Text); } } [Fact] public void SupportsExplicitDotPathWithStringFormat() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); var dataContext = new TestDataContext { StringProperty = "foobar" }; window.DataContext = dataContext; Assert.Equal("bar-" + typeof(TestDataContext).FullName, textBlock.Text); } } [Fact] public void SupportCastToTypeInExpressionWithProperty_ExplicitPropertyCast() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var contentControl = window.GetControl("contentControl"); var dataContext = new TestDataContext(); window.DataContext = dataContext; Assert.Equal(((IHasExplicitProperty)dataContext).ExplicitProperty, contentControl.Content); } } [Fact] public void Binds_To_Self_Without_DataType() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @" "; var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); var textBlock = window.FindControl("textBlock"); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); Assert.Equal(textBlock.Name, textBlock.Text); } } [Fact] public void Binds_To_Self_In_Style() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var xaml = @"