Преглед на файлове

More work on static resources.

Also found a scenario that Portable.Xaml can't seem to handle...
Steven Kirk преди 8 години
родител
ревизия
4a9302b61e

+ 5 - 0
src/Avalonia.Styling/Styling/Styles.cs

@@ -48,6 +48,11 @@ namespace Avalonia.Styling
         /// <inheritdoc/>
         public bool TryGetResource(string key, out object value)
         {
+            if (_resources != null && _resources.TryGetValue(key, out value))
+            {
+                return true;
+            }
+
             for (var i = Count - 1; i >= 0; --i)
             {
                 if (this[i].TryGetResource(key, out value))

+ 9 - 2
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@@ -34,8 +34,15 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
             var resourceProviderType = schemaContext.GetXamlType(typeof(IResourceProvider));
             var resourceProviders = ambientProvider.GetAllAmbientValues(resourceProviderType);
 
-            // Look up the ambient context for IResourceProviders which might be able to give us
-            // the resource.
+            // 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 (IResourceProvider resourceProvider in resourceProviders)
             {
                 if (resourceProvider is IControl control && control.StylingParent != null)

+ 24 - 0
tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs

@@ -87,6 +87,30 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal("foo-value", target.FindResource("foo"));
         }
 
+        [Fact]
+        public void FindResource_Should_Find_Styles_Resource()
+        {
+            var target = new Control
+            {
+                Styles =
+                {
+                    new Styles
+                    {
+                        Resources =
+                        {
+                            { "foo", "foo-value" },
+                        }
+                    }
+                },
+                Resources =
+                {
+                    { "bar", "bar-value" },
+                },
+            };
+
+            Assert.Equal("foo-value", target.FindResource("foo"));
+        }
+
         [Fact]
         public void FindResource_Should_Find_Application_Style_Resource()
         {

+ 114 - 5
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StaticResourceTests.cs

@@ -2,11 +2,15 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Linq;
 using Avalonia.Controls;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
 using Avalonia.Markup.Xaml.Data;
 using Avalonia.Media;
 using Avalonia.Styling;
 using Avalonia.UnitTests;
+using Avalonia.VisualTree;
 using Xunit;
 
 namespace Avalonia.Markup.Xaml.UnitTests.Xaml
@@ -62,7 +66,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         [Fact]
         public void StaticResource_From_Application_Can_Be_Assigned_To_Property_In_Window()
         {
-            using (StyledWindowNoTheme())
+            using (StyledWindow())
             {
                 Application.Current.Resources.Add("brush", new SolidColorBrush(0xff506070));
 
@@ -111,7 +115,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         [Fact]
         public void StaticResource_Can_Be_Assigned_To_Setter()
         {
-            using (StyledWindowNoTheme())
+            using (StyledWindow())
             {
                 var xaml = @"
 <Window xmlns='https://github.com/avaloniaui'
@@ -139,7 +143,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         [Fact]
         public void StaticResource_From_Style_Can_Be_Assigned_To_Setter()
         {
-            using (StyledWindowNoTheme())
+            using (StyledWindow())
             {
                 var xaml = @"
 <Window xmlns='https://github.com/avaloniaui'
@@ -166,9 +170,114 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
             }
         }
 
-        private IDisposable StyledWindowNoTheme()
+        [Fact]
+        public void StaticResource_Can_Be_Assigned_To_Setter_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Border'>
+        <Setter Property='Background' Value='{StaticResource brush}'/>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Border Name='border'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var border = window.FindControl<Border>("border");
+                var brush = (SolidColorBrush)border.Background;
+
+                Assert.Equal(0xff506070, brush.Color.ToUint32());
+            }
+        }
+
+        [Fact(Skip = "Not yet supported by Portable.Xaml")]
+        public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
+        {
+            var styleXaml = @"
+<Styles xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Styles.Resources>
+        <SolidColorBrush x:Key='brush'>#ff506070</SolidColorBrush>
+    </Styles.Resources>
+
+    <Style Selector='Button'>
+        <Setter Property='Template'>
+            <ControlTemplate>
+                <Border Name='border' Background='{StaticResource brush}'/>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+</Styles>";
+
+            using (StyledWindow(assets: ("test:style.xaml", styleXaml)))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <Window.Styles>
+        <StyleInclude Source='test:style.xaml'/>
+    </Window.Styles>
+    <Button Name='button'/>
+</Window>";
+
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var button = window.FindControl<Button>("button");
+
+                window.Show();
+
+                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());
+            }
+        }
+
+        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 UnitTestApplication.Start(TestServices.StyledWindow.With(theme: () => new Styles()));
+            return new Style(x => x.OfType<Window>())
+            {
+                Setters =
+                {
+                    new Setter(
+                        Window.TemplateProperty,
+                        new FuncControlTemplate<Window>(x =>
+                            new ContentPresenter
+                            {
+                                Name = "PART_ContentPresenter",
+                                [!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
+                            }))
+                }
+            };
         }
     }
 }

+ 36 - 0
tests/Avalonia.UnitTests/MockAssetLoader.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Platform;
+
+namespace Avalonia.UnitTests
+{
+    public class MockAssetLoader : IAssetLoader
+    {
+        private Dictionary<Uri, string> _assets;
+
+        public MockAssetLoader(params (string, string)[] assets)
+        {
+            _assets = assets.ToDictionary(x => new Uri(x.Item1, UriKind.RelativeOrAbsolute), x => x.Item2);
+        }
+
+        public bool Exists(Uri uri, Uri baseUri = null)
+        {
+            return _assets.ContainsKey(uri);
+        }
+
+        public Stream Open(Uri uri, Uri baseUri = null)
+        {
+            return new MemoryStream(Encoding.UTF8.GetBytes(_assets[uri]));
+        }
+
+        public void SetDefaultAssembly(Assembly asm)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}