Browse Source

Static resource as binding converter

Enable using a `StaticResource` as a binding converter. Fixes #818.
Steven Kirk 8 năm trước cách đây
mục cha
commit
2c1efe3773

+ 5 - 2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs

@@ -3,7 +3,7 @@
 
 using System;
 using System.ComponentModel;
-using System.Reactive;
+using System.Linq;
 using System.Reactive.Linq;
 using Avalonia.Controls;
 using Avalonia.Data;
@@ -62,7 +62,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             var schemaContext = context.GetService<IXamlSchemaContextProvider>()?.SchemaContext;
             var ambientProvider = context.GetService<IAmbientProvider>();
             var xamlType = schemaContext.GetXamlType(typeof(T));
-            return ambientProvider.GetFirstAmbientValue(xamlType) as T;
+
+            // We override XamlType.CanAssignTo in BindingXamlType so the results we get back
+            // from GetAllAmbientValues aren't necessarily of the correct type.
+            return ambientProvider.GetAllAmbientValues(xamlType).OfType<T>().FirstOrDefault();
         }
     }
 }

+ 16 - 11
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@@ -32,7 +32,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             var schemaContext = context.GetService<IXamlSchemaContextProvider>()?.SchemaContext;
             var ambientProvider = context.GetService<IAmbientProvider>();
             var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider));
-            var resourceProviders = ambientProvider.GetAllAmbientValues(resourceProviderType);
+            var ambientValues = ambientProvider.GetAllAmbientValues(resourceProviderType);
 
             // Look upwards though the ambient context for IResourceProviders which might be able
             // to give us the resource.
@@ -43,18 +43,23 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             //
             //   StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File
             //
-            foreach (IResourceProvider resourceProvider in resourceProviders)
+            foreach (var ambientValue in ambientValues)
             {
-                if (resourceProvider is IControl control && control.StylingParent != null)
+                // We override XamlType.CanAssignTo in BindingXamlType so the results we get back
+                // from GetAllAmbientValues aren't necessarily of the correct type.
+                if (ambientValue is IResourceProvider resourceProvider)
                 {
-                    // If we've got to a control that has a StylingParent then it's probably
-                    // a top level control and its StylingParent is pointing to the global
-                    // styles. If this is case just do a FindResource on it.
-                    return control.FindResource(ResourceKey);
-                }
-                else if (resourceProvider.TryGetResource(ResourceKey, out var value))
-                {
-                    return value;
+                    if (resourceProvider is IControl control && control.StylingParent != null)
+                    {
+                        // If we've got to a control that has a StylingParent then it's probably
+                        // a top level control and its StylingParent is pointing to the global
+                        // styles. If this is case just do a FindResource on it.
+                        return control.FindResource(ResourceKey);
+                    }
+                    else if (resourceProvider.TryGetResource(ResourceKey, out var value))
+                    {
+                        return value;
+                    }
                 }
             }
 

+ 27 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

@@ -332,6 +332,33 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             Assert.NotNull(listBox.ItemTemplate);
         }
 
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Converter()
+        {
+            using (StyledWindow())
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+             xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
+    <Window.Resources>
+        <local:TestValueConverter x:Key='converter' Append='bar'/>
+    </Window.Resources>
+
+    <TextBlock Name='textBlock' Text='{Binding Converter={StaticResource converter}}'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var textBlock = window.FindControl<TextBlock>("textBlock");
+
+                window.DataContext = "foo";
+                window.ApplyTemplate();
+
+                Assert.Equal("foobar", textBlock.Text);
+            }
+        }
+
         [Fact]
         public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed()
         {

+ 20 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/TestValueConverter.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+
+namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
+{
+    public class TestValueConverter : IValueConverter
+    {
+        public string Append { get; set; }
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value.ToString() + Append;
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}