From 91dda4c2d005fcc7742f6ab57674c7fa3fb922c9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 15 Sep 2015 22:30:00 +0200 Subject: [PATCH] Handle attached properties in PerspexPropertyConverter. --- .../PerspexPropertyConverterTest.cs | 63 +++++++++++++++++++ .../Perspex.Markup.Xaml.UnitTests.csproj | 1 + .../Converters/PerspexPropertyConverter.cs | 25 ++++++-- src/Perspex.Base/PerspexObject.cs | 31 +++++++++ .../PerspexObjectTests_Metadata.cs | 8 +++ 5 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs diff --git a/Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs b/Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs new file mode 100644 index 0000000000..3193c0d459 --- /dev/null +++ b/Tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs @@ -0,0 +1,63 @@ +// 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 Moq; +using OmniXaml; +using OmniXaml.TypeConversion; +using OmniXaml.Typing; +using Perspex.Markup.Xaml.Converters; +using Xunit; + +namespace Perspex.Markup.Xaml.UnitTests.Converters +{ + public class PerspexPropertyConverterTest + { + public PerspexPropertyConverterTest() + { + // Ensure properties are registered. + var foo = Class1.FooProperty; + var attached = AttachedOwner.AttachedProperty; + } + + [Fact] + public void ConvertFrom_Finds_Fully_Qualified_Property() + { + var target = new PerspexPropertyConverter(); + var context = CreateContext(); + var result = target.ConvertFrom(context, null, "Class1.Foo"); + } + + [Fact] + public void ConvertFrom_Finds_Attached_Property() + { + var target = new PerspexPropertyConverter(); + var context = CreateContext(); + var result = target.ConvertFrom(context, null, "AttachedOwner.Attached"); + } + + private IXamlTypeConverterContext CreateContext() + { + var context = new Mock(); + var typeRepository = new Mock(); + var featureProvider = new Mock(); + var class1XamlType = new XamlType(typeof(Class1), typeRepository.Object, null, featureProvider.Object); + var attachedOwnerXamlType = new XamlType(typeof(AttachedOwner), typeRepository.Object, null, featureProvider.Object); + context.Setup(x => x.TypeRepository).Returns(typeRepository.Object); + typeRepository.Setup(x => x.GetByQualifiedName("Class1")).Returns(class1XamlType); + typeRepository.Setup(x => x.GetByQualifiedName("AttachedOwner")).Returns(attachedOwnerXamlType); + return context.Object; + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo"); + } + + private class AttachedOwner + { + public static readonly PerspexProperty AttachedProperty = + PerspexProperty.RegisterAttached("Attached"); + } + } +} diff --git a/Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj b/Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj index b4a1355c00..219251a7f0 100644 --- a/Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj +++ b/Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj @@ -99,6 +99,7 @@ + diff --git a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs index e7f91c4902..ad10495bb9 100644 --- a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs +++ b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs @@ -4,9 +4,8 @@ using System; using System.Globalization; using System.Linq; -using OmniXaml.ObjectAssembler; +using OmniXaml; using OmniXaml.TypeConversion; -using Perspex.Markup.Xaml.Parsers; using Perspex.Styling; namespace Perspex.Markup.Xaml.Converters @@ -43,12 +42,26 @@ namespace Perspex.Markup.Xaml.Converters if (type == null) { - throw new InvalidOperationException($"Could not find type '{typeName}'."); + throw new XamlParseException($"Could not find type '{typeName}'."); } - // TODO: Handle attached properties. - // TODO: Give decent error message for not found property. - return PerspexObject.GetRegisteredProperties(type).Single(x => x.Name == propertyName); + // First look for non-attached property on the type and then look for an attached property. + var property = PerspexObject.GetRegisteredProperties(type) + .FirstOrDefault(x => x.Name == propertyName && !x.IsAttached); + + if (property == null) + { + property = PerspexObject.GetAttachedProperties(type) + .FirstOrDefault(x => x.Name == propertyName); + } + + if (property == null) + { + throw new XamlParseException( + $"Could not find PerspexProperty '{typeName}'.{propertyName}."); + } + + return property; } public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType) diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs index 83021a62d4..8a1d05fcb7 100644 --- a/src/Perspex.Base/PerspexObject.cs +++ b/src/Perspex.Base/PerspexObject.cs @@ -273,6 +273,23 @@ namespace Perspex } } + /// + /// Gets all attached s registered by an owner. + /// + /// The owner type. + /// A collection of definitions. + public static IEnumerable GetAttachedProperties(Type ownerType) + { + List list; + + if (s_attached.TryGetValue(ownerType, out list)) + { + return list; + } + + return Enumerable.Empty(); + } + /// /// Registers a on a type. /// @@ -299,6 +316,20 @@ namespace Perspex { list.Add(property); } + + if (property.IsAttached) + { + if (!s_attached.TryGetValue(property.OwnerType, out list)) + { + list = new List(); + s_attached.Add(property.OwnerType, list); + } + + if (!list.Contains(property)) + { + list.Add(property); + } + } } /// diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs index f92fb12a66..b905f8b5ba 100644 --- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs +++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs @@ -34,6 +34,14 @@ namespace Perspex.Base.UnitTests Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names); } + [Fact] + public void GetAttachedProperties_Returns_Registered_Properties_For_Base_Types() + { + string[] names = PerspexObject.GetAttachedProperties(typeof(AttachedOwner)).Select(x => x.Name).ToArray(); + + Assert.Equal(new[] { "Attached" }, names); + } + private class Class1 : PerspexObject { public static readonly PerspexProperty FooProperty =