From 3f87b9d02d1171a0078bd9c9a17db9cee1a58302 Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Fri, 20 Apr 2018 15:24:09 +0200 Subject: [PATCH 1/6] Support StaticResource in Bindings inside DataTemplate --- .../AvaloniaXamlLoaderPortableXaml.cs | 5 +-- .../PortableXaml/AvaloniaXamlObjectWriter.cs | 26 ++++++++++++-- .../PortableXaml/portable.xaml.github | 2 +- .../Templates/TemplateContent.cs | 8 +++-- .../Templates/TemplateLoader.cs | 3 +- .../StaticResourceExtensionTests.cs | 36 ++++++++++++++++++- 6 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs index de2a79c54e..539fbeb680 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs @@ -194,11 +194,12 @@ namespace Avalonia.Markup.Xaml return result; } - internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null) + internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null) { var writer = AvaloniaXamlObjectWriter.Create( reader.SchemaContext, - context); + context, + parentAmbientProvider); XamlServices.Transform(reader, writer); writer.ApplyAllDelayedProperties(); diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs index e0e2553f46..509c52503b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs @@ -27,6 +27,26 @@ namespace Avalonia.Markup.Xaml.PortableXaml nameScope); } + public static AvaloniaXamlObjectWriter Create( + XamlSchemaContext schemaContext, + AvaloniaXamlContext context, + IAmbientProvider parentAmbientProvider) + { + var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance }; + + var writerSettings = new XamlObjectWriterSettings() + { + ExternalNameScope = nameScope, + RegisterNamesOnExternalNamescope = true, + RootObjectInstance = context?.RootInstance + }; + + return new AvaloniaXamlObjectWriter(schemaContext, + writerSettings.WithContext(context), + nameScope, + parentAmbientProvider); + } + private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper(); private AvaloniaNameScope _nameScope; @@ -34,9 +54,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml private AvaloniaXamlObjectWriter( XamlSchemaContext schemaContext, XamlObjectWriterSettings settings, - AvaloniaNameScope nameScope - ) - : base(schemaContext, settings) + AvaloniaNameScope nameScope, + IAmbientProvider parentAmbientProvider = null) + : base(schemaContext, settings, parentAmbientProvider) { _nameScope = nameScope; } diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index c066401445..e7077fa7df 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit c0664014455392ac221a765e66f9837704339b6f +Subproject commit e7077fa7df836af53f220f845f59971208c3e408 diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs index 1d4dafc413..63fb9f193c 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs @@ -10,8 +10,10 @@ namespace Avalonia.Markup.Xaml.Templates public class TemplateContent { - public TemplateContent(IEnumerable namespaces, XamlReader reader) + public TemplateContent(IEnumerable namespaces, XamlReader reader, + IAmbientProvider ambientProvider) { + ParentAmbientProvider = ambientProvider; List = new XamlNodeList(reader.SchemaContext); //we need to rpeserve all namespace and prefixes to writer @@ -26,9 +28,11 @@ namespace Avalonia.Markup.Xaml.Templates public XamlNodeList List { get; } + private IAmbientProvider ParentAmbientProvider { get; } + public IControl Load() { - return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader()); + return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader(), parentAmbientProvider: ParentAmbientProvider); } public static IControl Load(object templateContent) diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs index 1085131230..e29485ddb0 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs @@ -14,7 +14,8 @@ namespace Avalonia.Markup.Xaml.Templates { var tdc = (ITypeDescriptorContext)serviceProvider; var ns = tdc.GetService(); - return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader); + var ambientProvider = tdc.GetService(); + return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader, ambientProvider); } public override XamlReader Save(object value, IServiceProvider serviceProvider) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index 862ce2b3c0..57293d5d16 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -323,7 +323,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } - [Fact(Skip = "Not yet supported by Portable.Xaml")] + [Fact(/*Skip = "Not yet supported by Portable.Xaml"*/)] public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File() { var styleXaml = @" @@ -417,6 +417,40 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void StaticResource_Can_Be_Assigned_To_Converter_In_DataTemplate() + { + using (StyledWindow()) + { + var xaml = @" + + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + + window.DataContext = "foo"; + var presenter = window.FindControl("presenter"); + + window.Show(); + + var textBlock = (TextBlock)presenter.GetVisualChildren().Single(); + + Assert.NotNull(textBlock); + Assert.Equal("foobar", textBlock.Text); + } + } + [Fact] public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed() { From ccd62038afeb5e75afc516c0bd5c404bba9216bf Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Wed, 25 Apr 2018 21:40:54 +0200 Subject: [PATCH 2/6] Update Portable.Xaml to https://github.com/AvaloniaUI/Portable.Xaml/pull/1 --- .../Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index e7077fa7df..faa952f3a0 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit e7077fa7df836af53f220f845f59971208c3e408 +Subproject commit faa952f3a05b4bdf2986d686f4154b1ab084508a From 8d11d7a5fa0557397fd4de749f8b68d709558538 Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Thu, 26 Apr 2018 02:42:21 +0200 Subject: [PATCH 3/6] BindingExtension Source tests and sample #1521 --- samples/BindingTest/MainWindow.xaml | 8 +++ .../MarkupExtensions/BindingExtensionTests.cs | 71 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs diff --git a/samples/BindingTest/MainWindow.xaml b/samples/BindingTest/MainWindow.xaml index 3547e33181..21e3af0d22 100644 --- a/samples/BindingTest/MainWindow.xaml +++ b/samples/BindingTest/MainWindow.xaml @@ -1,4 +1,5 @@ @@ -6,6 +7,9 @@ + + + @@ -40,6 +44,10 @@ + + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs new file mode 100644 index 0000000000..4b0bee00f3 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions +{ + public class BindingExtensionTests + { + + [Fact] + public void BindingExtension_Binds_To_Source() + { + using (StyledWindow()) + { + var xaml = @" + + + foobar + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.Show(); + + Assert.Equal("foobar", textBlock.Text); + } + } + + private IDisposable StyledWindow(params (string, string)[] assets) + { + var services = TestServices.StyledWindow.With( + assetLoader: new MockAssetLoader(assets), + theme: () => new Styles + { + WindowStyle(), + }); + + return UnitTestApplication.Start(services); + } + + private Style WindowStyle() + { + return new Style(x => x.OfType()) + { + Setters = + { + new Setter( + Window.TemplateProperty, + new FuncControlTemplate(x => + new ContentPresenter + { + Name = "PART_ContentPresenter", + [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty], + })) + } + }; + } + } +} From 5dabc067edb222cdc3d3c55f295c4aab0e8572ec Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Thu, 26 Apr 2018 02:19:26 +0200 Subject: [PATCH 4/6] Added StaticResource tests for bindings within DataTemplate --- .../StaticResourceExtensionTests.cs | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index 57293d5d16..e6116ed563 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -323,7 +323,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } - [Fact(/*Skip = "Not yet supported by Portable.Xaml"*/)] + [Fact] public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File() { var styleXaml = @" @@ -418,7 +418,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } [Fact] - public void StaticResource_Can_Be_Assigned_To_Converter_In_DataTemplate() + public void StaticResource_Can_Be_Assigned_To_Binding_Converter_In_DataTemplate() { using (StyledWindow()) { @@ -429,7 +429,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions - + @@ -451,6 +451,45 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void StaticResource_Is_Correctly_Chosen_From_Within_DataTemplate() + { + // this tests if IAmbientProviders in DataTemplate contexts are in correct order + // if they wouldn't be, Purple brush would be bound to + using (StyledWindow()) + { + var xaml = @" + + + + + + + + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + + window.Show(); + + var textBlock = window.GetVisualDescendants().OfType().Single(); + + Assert.NotNull(textBlock); + Assert.Equal("White-bar", textBlock.Text); + } + } + [Fact] public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed() { From fee44d59a27596efbbf829a4709efdcb427c4520 Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Fri, 4 May 2018 15:32:07 +0200 Subject: [PATCH 5/6] Cleanup AvaloniaXamlObjectWriter factory methods --- .../PortableXaml/AvaloniaXamlObjectWriter.cs | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs index 509c52503b..240ca291a8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs @@ -9,28 +9,10 @@ namespace Avalonia.Markup.Xaml.PortableXaml { public class AvaloniaXamlObjectWriter : XamlObjectWriter { - public static AvaloniaXamlObjectWriter Create( - XamlSchemaContext schemaContext, - AvaloniaXamlContext context) - { - var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance }; - - var writerSettings = new XamlObjectWriterSettings() - { - ExternalNameScope = nameScope, - RegisterNamesOnExternalNamescope = true, - RootObjectInstance = context?.RootInstance - }; - - return new AvaloniaXamlObjectWriter(schemaContext, - writerSettings.WithContext(context), - nameScope); - } - public static AvaloniaXamlObjectWriter Create( XamlSchemaContext schemaContext, AvaloniaXamlContext context, - IAmbientProvider parentAmbientProvider) + IAmbientProvider parentAmbientProvider = null) { var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance }; @@ -55,7 +37,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml XamlSchemaContext schemaContext, XamlObjectWriterSettings settings, AvaloniaNameScope nameScope, - IAmbientProvider parentAmbientProvider = null) + IAmbientProvider parentAmbientProvider) : base(schemaContext, settings, parentAmbientProvider) { _nameScope = nameScope; From c7f7640ae711bcda44bc536ac5f52d26ab38c8a3 Mon Sep 17 00:00:00 2001 From: Amadeusz Sadowski Date: Fri, 4 May 2018 15:51:44 +0200 Subject: [PATCH 6/6] Removed comments about not working parent ambient providers --- .../MarkupExtensions/StaticResourceExtension.cs | 7 ------- .../MarkupExtensions/StaticResourceExtensionTests.cs | 4 +--- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index 9089a13656..8e71c5f81b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -36,13 +36,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions // Look upwards though the ambient context for IResourceProviders which might be able // to give us the resource. - // - // TODO: If we're in a template then only the ambient values since the root of the - // template wil be included here. We need some way to get hold of the parent ambient - // context and search that. See the test: - // - // StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File - // foreach (var ambientValue in ambientValues) { // We override XamlType.CanAssignTo in BindingXamlType so the results we get back diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs index e6116ed563..8615efd967 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs @@ -361,9 +361,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions var border = (Border)button.GetVisualChildren().Single(); var brush = (SolidColorBrush)border.Background; - - // To make this work we somehow need to be able to get hold of the parent ambient - // context from Portable.Xaml. See TODO in StaticResourceExtension. + Assert.Equal(0xff506070, brush.Color.ToUint32()); } }