From 19af37a6af5c8116837405015761c85b1126a222 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 18 Feb 2016 22:09:58 +0100 Subject: [PATCH] Started adding StyleResource markup extension. OmniXAML issue #84 is now blocking us. --- .../StyleResourceExtension.cs | 33 ++++++++ .../Perspex.Markup.Xaml.csproj | 2 + .../Styling/StyleInclude.cs | 12 +++ src/Perspex.Styling/Perspex.Styling.csproj | 1 + src/Perspex.Styling/Styling/IStyle.cs | 9 +++ src/Perspex.Styling/Styling/Style.cs | 49 +++++++++++- .../Styling/StyleExtensions.cs | 39 ++++++++++ src/Perspex.Styling/Styling/Styles.cs | 23 ++++++ .../StyleTests.cs | 27 ++++++- .../Perspex.Styling.UnitTests.csproj | 1 + .../ResourceTests.cs | 78 +++++++++++++++++++ 11 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs create mode 100644 src/Perspex.Styling/Styling/StyleExtensions.cs create mode 100644 tests/Perspex.Styling.UnitTests/ResourceTests.cs diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs new file mode 100644 index 0000000000..c1acd8cbff --- /dev/null +++ b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs @@ -0,0 +1,33 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using OmniXaml; +using Perspex.LogicalTree; +using Perspex.Styling; + +namespace Perspex.Markup.Xaml.MarkupExtensions +{ + public class StyleResourceExtension : MarkupExtension + { + public StyleResourceExtension(string name) + { + Name = name; + } + + public override object ProvideValue(MarkupExtensionContext extensionContext) + { + var styleHost = extensionContext.TargetObject as IStyleHost; + + if (styleHost == null) + { + throw new ParseException( + $"StyleResource cannot be assigned to an object of type '{styleHost.GetType()}'."); + } + + return styleHost.FindStyleResource(Name); + } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj index 984762d50f..35aff8a3a2 100644 --- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj +++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj @@ -55,6 +55,7 @@ + @@ -78,6 +79,7 @@ + diff --git a/src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs index fee252de17..6991e48f16 100644 --- a/src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Perspex.Markup.Xaml/Styling/StyleInclude.cs @@ -35,5 +35,17 @@ namespace Perspex.Markup.Xaml.Styling Loaded.Attach(control, container); } } + + /// + /// Tries to find a named resource within the style. + /// + /// The resource name. + /// + /// The resource if found, otherwise . + /// + public object FindResource(string name) + { + return Loaded.FindResource(name); + } } } diff --git a/src/Perspex.Styling/Perspex.Styling.csproj b/src/Perspex.Styling/Perspex.Styling.csproj index 4ed57c90e3..92edaa3db6 100644 --- a/src/Perspex.Styling/Perspex.Styling.csproj +++ b/src/Perspex.Styling/Perspex.Styling.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Perspex.Styling/Styling/IStyle.cs b/src/Perspex.Styling/Styling/IStyle.cs index 4404853bf0..5847aa5ee1 100644 --- a/src/Perspex.Styling/Styling/IStyle.cs +++ b/src/Perspex.Styling/Styling/IStyle.cs @@ -16,5 +16,14 @@ namespace Perspex.Styling /// The control that contains this style. May be null. /// void Attach(IStyleable control, IStyleHost container); + + /// + /// Tries to find a named resource within the style. + /// + /// The resource name. + /// + /// The resource if found, otherwise . + /// + object FindResource(string name); } } diff --git a/src/Perspex.Styling/Styling/Style.cs b/src/Perspex.Styling/Styling/Style.cs index 0a10471f72..58fd2fa721 100644 --- a/src/Perspex.Styling/Styling/Style.cs +++ b/src/Perspex.Styling/Styling/Style.cs @@ -14,14 +14,13 @@ namespace Perspex.Styling public class Style : IStyle { private static readonly IObservable True = Observable.Never().StartWith(true); - private Lazy> _resources; + private Dictionary _resources; /// /// Initializes a new instance of the class. /// public Style() { - _resources = new Lazy>(() => new Dictionary()); } /// @@ -34,9 +33,30 @@ namespace Perspex.Styling } /// - /// Gets a dictionary of style resources. + /// Gets or sets a dictionary of style resources. /// - public IDictionary Resources => _resources.Value; + public IDictionary Resources + { + get + { + if (_resources == null) + { + _resources = new Dictionary(); + } + + return _resources; + } + + set + { + var resources = Resources; + + foreach (var i in value) + { + resources.Add(i); + } + } + } /// /// Gets or sets style's selector. @@ -83,6 +103,27 @@ namespace Perspex.Styling } } + /// + /// Tries to find a named resource within the style. + /// + /// The resource name. + /// + /// The resource if found, otherwise . + /// + public object FindResource(string name) + { + object result = null; + + if (_resources?.TryGetValue(name, out result) == true) + { + return result; + } + else + { + return PerspexProperty.UnsetValue; + } + } + /// /// Returns a string representation of the style. /// diff --git a/src/Perspex.Styling/Styling/StyleExtensions.cs b/src/Perspex.Styling/Styling/StyleExtensions.cs new file mode 100644 index 0000000000..dc30041b49 --- /dev/null +++ b/src/Perspex.Styling/Styling/StyleExtensions.cs @@ -0,0 +1,39 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Perspex.Styling +{ + public static class StyleExtensions + { + /// + /// Tries to find a named style resource. + /// + /// The control from which to find the resource. + /// The resource name. + /// + /// The resource if found, otherwise . + /// + public static object FindStyleResource(this IStyleHost control, string name) + { + Contract.Requires(control != null); + Contract.Requires(name != null); + Contract.Requires(!string.IsNullOrWhiteSpace(name)); + + while (control != null) + { + var result = control.Styles.FindResource(name); + + if (result != PerspexProperty.UnsetValue) + { + return result; + } + + control = control.StylingParent; + } + + return PerspexProperty.UnsetValue; + } + } +} diff --git a/src/Perspex.Styling/Styling/Styles.cs b/src/Perspex.Styling/Styling/Styles.cs index 877235a337..4806670369 100644 --- a/src/Perspex.Styling/Styling/Styles.cs +++ b/src/Perspex.Styling/Styling/Styles.cs @@ -1,6 +1,7 @@ // Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System.Linq; using Perspex.Collections; namespace Perspex.Styling @@ -24,5 +25,27 @@ namespace Perspex.Styling style.Attach(control, container); } } + + /// + /// Tries to find a named resource within the style. + /// + /// The resource name. + /// + /// The resource if found, otherwise . + /// + public object FindResource(string name) + { + foreach (var style in this.Reverse()) + { + var result = style.FindResource(name); + + if (result != PerspexProperty.UnsetValue) + { + return result; + } + } + + return PerspexProperty.UnsetValue; + } } } diff --git a/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs index 5a9d9bbcef..9eb0309b42 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/StyleTests.cs @@ -19,7 +19,7 @@ namespace Perspex.Markup.Xaml.UnitTests public void Color_Can_Be_Added_To_Style_Resources() { using (UnitTestApplication.Start(TestServices.MockPlatformWrapper)) - { + { var xaml = @" @@ -63,6 +63,31 @@ namespace Perspex.Markup.Xaml.UnitTests } } + [Fact] + public void StyleResource_Can_Be_Assigned_To_Property() + { + var xaml = @" + + + + + + +"; + + var loader = new PerspexXamlLoader(); + var userControl = (UserControl)loader.Load(xaml); + var border = userControl.FindControl("border"); + var brush = (SolidColorBrush)border.Background; + + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } + [Fact] public void Binding_Should_Be_Assigned_To_Setter_Value_Instead_Of_Bound() { diff --git a/tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj b/tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj index 574660616c..d775308d8d 100644 --- a/tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj +++ b/tests/Perspex.Styling.UnitTests/Perspex.Styling.UnitTests.csproj @@ -97,6 +97,7 @@ + diff --git a/tests/Perspex.Styling.UnitTests/ResourceTests.cs b/tests/Perspex.Styling.UnitTests/ResourceTests.cs new file mode 100644 index 0000000000..bbfa119150 --- /dev/null +++ b/tests/Perspex.Styling.UnitTests/ResourceTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) The Perspex Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Collections.Generic; +using Perspex.Controls; +using Xunit; + +namespace Perspex.Styling.UnitTests +{ + public class ResourceTests + { + [Fact] + public void FindStyleResource_Should_Find_Correct_Resource() + { + Border target; + + var tree = new Decorator + { + Styles = new Styles + { + new Style + { + Resources = new Dictionary + { + { "Foo", "foo resource" }, + { "Bar", "overridden" }, + } + } + }, + Child = target = new Border + { + Styles = new Styles + { + new Style + { + Resources = new Dictionary + { + { "Bar", "again overridden" }, + } + }, + new Style + { + Resources = new Dictionary + { + { "Bar", "bar resource" }, + } + } + } + } + }; + + Assert.Equal("foo resource", target.FindStyleResource("Foo")); + Assert.Equal("bar resource", target.FindStyleResource("Bar")); + } + + [Fact] + public void FindStyleResource_Should_Return_UnsetValue_For_Not_Found() + { + Border target; + + var tree = target = new Border + { + Styles = new Styles + { + new Style + { + Resources = new Dictionary + { + { "Foo", "foo" }, + } + }, + } + }; + + Assert.Equal(PerspexProperty.UnsetValue, target.FindStyleResource("Baz")); + } + } +}