Browse Source

Handle attached properties in PerspexPropertyConverter.

Steven Kirk 10 years ago
parent
commit
91dda4c2d0

+ 63 - 0
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<IXamlTypeConverterContext>();
+            var typeRepository = new Mock<IXamlTypeRepository>();
+            var featureProvider = new Mock<ITypeFeatureProvider>();
+            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<string> FooProperty =
+                PerspexProperty.Register<Class1, string>("Foo");
+        }
+
+        private class AttachedOwner
+        {
+            public static readonly PerspexProperty<string> AttachedProperty =
+                PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
+        }
+    }
+}

+ 1 - 0
Tests/Perspex.Markup.Xaml.UnitTests/Perspex.Markup.Xaml.UnitTests.csproj

@@ -99,6 +99,7 @@
   <ItemGroup>
     <Compile Include="BindingDefinitionBuilder.cs" />
     <Compile Include="ChangeBranchTest.cs" />
+    <Compile Include="Converters\PerspexPropertyConverterTest.cs" />
     <Compile Include="DataContextChangeSynchronizerTest.cs" />
     <Compile Include="Parsers\SelectorGrammarTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 19 - 6
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)

+ 31 - 0
src/Perspex.Base/PerspexObject.cs

@@ -273,6 +273,23 @@ namespace Perspex
             }
         }
 
+        /// <summary>
+        /// Gets all attached <see cref="PerspexProperty"/>s registered by an owner.
+        /// </summary>
+        /// <param name="ownerType">The owner type.</param>
+        /// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
+        public static IEnumerable<PerspexProperty> GetAttachedProperties(Type ownerType)
+        {
+            List<PerspexProperty> list;
+
+            if (s_attached.TryGetValue(ownerType, out list))
+            {
+                return list;
+            }
+
+            return Enumerable.Empty<PerspexProperty>();
+        }
+
         /// <summary>
         /// Registers a <see cref="PerspexProperty"/> on a type.
         /// </summary>
@@ -299,6 +316,20 @@ namespace Perspex
             {
                 list.Add(property);
             }
+
+            if (property.IsAttached)
+            {
+                if (!s_attached.TryGetValue(property.OwnerType, out list))
+                {
+                    list = new List<PerspexProperty>();
+                    s_attached.Add(property.OwnerType, list);
+                }
+
+                if (!list.Contains(property))
+                {
+                    list.Add(property);
+                }
+            }
         }
 
         /// <summary>

+ 8 - 0
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<string> FooProperty =