diff --git a/samples/XamlTestApplicationPcl/XamlTestApp.paml b/samples/XamlTestApplicationPcl/XamlTestApp.paml index 532199aab9..8a4a317394 100644 --- a/samples/XamlTestApplicationPcl/XamlTestApp.paml +++ b/samples/XamlTestApplicationPcl/XamlTestApp.paml @@ -3,13 +3,13 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs index 1cdc3cc4f1..b46364d4c4 100644 --- a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs +++ b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs @@ -25,30 +25,38 @@ namespace Perspex.Markup.Xaml.Converters public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value) { var s = (string)value; - var lastDot = s.LastIndexOf('.'); - if (lastDot == -1) - { - throw new NotSupportedException("PerspexProperties must currently be fully qualified."); - } + string typeName; + string propertyName; + Type type; - var typeName = s.Substring(0, lastDot); - var propertyName = s.Substring(lastDot + 1); - var type = context.TypeRepository.GetByQualifiedName(typeName)?.UnderlyingType; - var styleType = context.TypeRepository.GetXamlType(typeof(Style)); + ParseProperty(s, out typeName, out propertyName); - // ATTN: SuperJMN - //var style = ((XamlTypeConverterContext)context).TopDownValueContext.GetLastInstance(styleType); + if (typeName == null) + { + var styleType = context.TypeRepository.GetXamlType(typeof(Style)); + var style = (Style)context.TopDownValueContext.GetLastInstance(styleType); + type = style.Selector.TargetType; - if (type == null) + if (type == null) + { + throw new XamlParseException( + "Could not determine the target type. Please fully qualify the property name."); + } + } + else { - throw new XamlParseException($"Could not find type '{typeName}'."); + type = context.TypeRepository.GetByQualifiedName(typeName)?.UnderlyingType; + + if (type == null) + { + throw new XamlParseException($"Could not find type '{typeName}'."); + } } // First look for non-attached property on the type and then look for an attached property. - var property = PerspexPropertyRegistry.Instance.GetRegistered(type) - .FirstOrDefault(x => x.Name == propertyName); - + var property = PerspexPropertyRegistry.Instance.FindRegistered(type, s); + if (property == null) { property = PerspexPropertyRegistry.Instance.GetAttached(type) @@ -58,7 +66,7 @@ namespace Perspex.Markup.Xaml.Converters if (property == null) { throw new XamlParseException( - $"Could not find PerspexProperty '{typeName}'.{propertyName}."); + $"Could not find PerspexProperty '{type.Name}.{propertyName}'."); } return property; @@ -68,5 +76,25 @@ namespace Perspex.Markup.Xaml.Converters { throw new NotImplementedException(); } + + private void ParseProperty(string s, out string typeName, out string propertyName) + { + var split = s.Split('.'); + + if (split.Length == 1) + { + typeName = null; + propertyName = split[0]; + } + else if (split.Length == 2) + { + typeName = split[0]; + propertyName = split[1]; + } + else + { + throw new XamlParseException($"Invalid property name: '{s}'."); + } + } } } \ No newline at end of file diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs b/tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs index ebf192679a..9b6230df17 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Converters/PerspexPropertyConverterTest.cs @@ -1,11 +1,14 @@ // 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 Moq; using OmniXaml; +using OmniXaml.ObjectAssembler.Commands; using OmniXaml.TypeConversion; using OmniXaml.Typing; using Perspex.Markup.Xaml.Converters; +using Perspex.Styling; using Xunit; namespace Perspex.Markup.Xaml.UnitTests.Converters @@ -25,6 +28,19 @@ namespace Perspex.Markup.Xaml.UnitTests.Converters var target = new PerspexPropertyTypeConverter(); var context = CreateContext(); var result = target.ConvertFrom(context, null, "Class1.Foo"); + + Assert.Equal(Class1.FooProperty, result); + } + + [Fact] + public void ConvertFrom_Uses_Selector_TargetType() + { + var target = new PerspexPropertyTypeConverter(); + var style = new Style(x => x.OfType()); + var context = CreateContext(style); + var result = target.ConvertFrom(context, null, "Foo"); + + Assert.Equal(Class1.FooProperty, result); } [Fact] @@ -33,25 +49,50 @@ namespace Perspex.Markup.Xaml.UnitTests.Converters var target = new PerspexPropertyTypeConverter(); var context = CreateContext(); var result = target.ConvertFrom(context, null, "AttachedOwner.Attached"); + + Assert.Equal(AttachedOwner.AttachedProperty, result); } - private IXamlTypeConverterContext CreateContext() + private IXamlTypeConverterContext CreateContext(Style style = null) { var context = new Mock(); + var topDownValueContext = 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.TopDownValueContext).Returns(topDownValueContext.Object); context.Setup(x => x.TypeRepository).Returns(typeRepository.Object); + topDownValueContext.Setup(x => x.GetLastInstance(It.IsAny())).Returns(style); typeRepository.Setup(x => x.GetByQualifiedName("Class1")).Returns(class1XamlType); typeRepository.Setup(x => x.GetByQualifiedName("AttachedOwner")).Returns(attachedOwnerXamlType); return context.Object; } - private class Class1 : PerspexObject + private class Class1 : PerspexObject, IStyleable { public static readonly PerspexProperty FooProperty = PerspexProperty.Register("Foo"); + + public Classes Classes + { + get { throw new NotImplementedException(); } + } + + public string Name + { + get { throw new NotImplementedException(); } + } + + public Type StyleKey + { + get { throw new NotImplementedException(); } + } + + public ITemplatedControl TemplatedParent + { + get { throw new NotImplementedException(); } + } } private class AttachedOwner