Browse Source

Initial implementation of PerspexPropertyConverter.

pull/145/head
Steven Kirk 11 years ago
parent
commit
7e2a28c91f
  1. 10
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  2. 57
      src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs
  3. 34
      src/Markup/Perspex.Markup.Xaml/Converters/SelectorConverter.cs
  4. 1
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  5. 27
      src/Perspex.Base/PerspexObject.cs
  6. 5
      src/Perspex.Base/PerspexProperty.cs
  7. 28
      src/Perspex.Styling/Styling/Selector.cs
  8. 19
      tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
  9. 12
      tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs

10
src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs

@ -16,6 +16,7 @@ using Perspex.Markup.Xaml.DataBinding;
using Perspex.Markup.Xaml.MarkupExtensions; using Perspex.Markup.Xaml.MarkupExtensions;
using Perspex.Media; using Perspex.Media;
using Perspex.Media.Imaging; using Perspex.Media.Imaging;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context namespace Perspex.Markup.Xaml.Context
{ {
@ -42,6 +43,7 @@ namespace Perspex.Markup.Xaml.Context
var rootType = typeof(Control); var rootType = typeof(Control);
var bindingType = typeof(BindingExtension); var bindingType = typeof(BindingExtension);
var templateType = typeof(XamlDataTemplate); var templateType = typeof(XamlDataTemplate);
var styleType = typeof(Style);
var definitionForRoot = XamlNamespace var definitionForRoot = XamlNamespace
.Map(PerspexNs) .Map(PerspexNs)
@ -59,6 +61,11 @@ namespace Perspex.Markup.Xaml.Context
bindingType.Namespace, bindingType.Namespace,
}), }),
Route.Assembly(templateType.GetTypeInfo().Assembly).WithNamespaces( Route.Assembly(templateType.GetTypeInfo().Assembly).WithNamespaces(
new[]
{
templateType.Namespace,
}),
Route.Assembly(styleType.GetTypeInfo().Assembly).WithNamespaces(
new[] new[]
{ {
templateType.Namespace, templateType.Namespace,
@ -84,7 +91,9 @@ namespace Perspex.Markup.Xaml.Context
new TypeConverterRegistration(typeof(Brush), new BrushConverter()), new TypeConverterRegistration(typeof(Brush), new BrushConverter()),
new TypeConverterRegistration(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()), new TypeConverterRegistration(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(GridLength), new GridLengthTypeConverter()), new TypeConverterRegistration(typeof(GridLength), new GridLengthTypeConverter()),
new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyConverter()),
new TypeConverterRegistration(typeof(RowDefinitions), new RowDefinitionsTypeConverter()), new TypeConverterRegistration(typeof(RowDefinitions), new RowDefinitionsTypeConverter()),
new TypeConverterRegistration(typeof(Selector), new SelectorConverter()),
new TypeConverterRegistration(typeof(Thickness), new ThicknessConverter()), new TypeConverterRegistration(typeof(Thickness), new ThicknessConverter()),
}; };
@ -101,6 +110,7 @@ namespace Perspex.Markup.Xaml.Context
new ContentPropertyDefinition(typeof(Decorator), "Child"), new ContentPropertyDefinition(typeof(Decorator), "Child"),
new ContentPropertyDefinition(typeof(ItemsControl), "Items"), new ContentPropertyDefinition(typeof(ItemsControl), "Items"),
new ContentPropertyDefinition(typeof(Panel), "Children"), new ContentPropertyDefinition(typeof(Panel), "Children"),
new ContentPropertyDefinition(typeof(Style), "Setters"),
new ContentPropertyDefinition(typeof(TextBlock), "Text"), new ContentPropertyDefinition(typeof(TextBlock), "Text"),
new ContentPropertyDefinition(typeof(TextBox), "Text"), new ContentPropertyDefinition(typeof(TextBox), "Text"),
new ContentPropertyDefinition(typeof(XamlDataTemplate), "Content"), new ContentPropertyDefinition(typeof(XamlDataTemplate), "Content"),

57
src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyConverter.cs

@ -0,0 +1,57 @@
// 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 System.Globalization;
using System.Linq;
using OmniXaml.ObjectAssembler;
using OmniXaml.TypeConversion;
using Perspex.Markup.Xaml.Parsers;
namespace Perspex.Markup.Xaml.Converters
{
public class PerspexPropertyConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
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.");
}
var typeName = s.Substring(0, lastDot);
var propertyName = s.Substring(lastDot + 1);
// TODO: Doesn't handle xml namespaces - use GetByQualifiedName when it works with the
// default namespace.
var type = context.TypeRepository.GetByPrefix("", typeName)?.UnderlyingType;
if (type == null)
{
throw new InvalidOperationException($"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);
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

34
src/Markup/Perspex.Markup.Xaml/Converters/SelectorConverter.cs

@ -0,0 +1,34 @@
// 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 System.Globalization;
using OmniXaml.TypeConversion;
using Perspex.Markup.Xaml.Parsers;
namespace Perspex.Markup.Xaml.Converters
{
public class SelectorConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
{
var parser = new SelectorParser((t, ns) => context.TypeRepository.GetByPrefix(ns ?? "", t).UnderlyingType);
return parser.Parse((string)value);
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

1
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -40,6 +40,7 @@
</Compile> </Compile>
<Compile Include="Converters\RowDefinitionsTypeConverter.cs" /> <Compile Include="Converters\RowDefinitionsTypeConverter.cs" />
<Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" /> <Compile Include="Converters\ColumnDefinitionsTypeConverter.cs" />
<Compile Include="Converters\PerspexPropertyConverter.cs" />
<Compile Include="Converters\SelectorConverter.cs" /> <Compile Include="Converters\SelectorConverter.cs" />
<Compile Include="Converters\ThicknessConverter.cs" /> <Compile Include="Converters\ThicknessConverter.cs" />
<Compile Include="Context\PerspexWiringContext.cs" /> <Compile Include="Context\PerspexWiringContext.cs" />

27
src/Perspex.Base/PerspexObject.cs

@ -71,6 +71,12 @@ namespace Perspex
private static readonly Dictionary<Type, List<PerspexProperty>> s_registered = private static readonly Dictionary<Type, List<PerspexProperty>> s_registered =
new Dictionary<Type, List<PerspexProperty>>(); new Dictionary<Type, List<PerspexProperty>>();
/// <summary>
/// The registered attached properties by owner type.
/// </summary>
private static readonly Dictionary<Type, List<PerspexProperty>> s_attached =
new Dictionary<Type, List<PerspexProperty>>();
/// <summary> /// <summary>
/// The parent object that inherited values are inherited from. /// The parent object that inherited values are inherited from.
/// </summary> /// </summary>
@ -154,7 +160,7 @@ namespace Perspex
_inheritanceParent.PropertyChanged -= ParentPropertyChanged; _inheritanceParent.PropertyChanged -= ParentPropertyChanged;
} }
var inherited = (from property in GetProperties(GetType()) var inherited = (from property in GetRegisteredProperties(GetType())
where property.Inherits where property.Inherits
select new select new
{ {
@ -245,7 +251,7 @@ namespace Perspex
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns> /// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
public static IEnumerable<PerspexProperty> GetProperties(Type type) public static IEnumerable<PerspexProperty> GetRegisteredProperties(Type type)
{ {
Contract.Requires<NullReferenceException>(type != null); Contract.Requires<NullReferenceException>(type != null);
@ -432,22 +438,7 @@ namespace Perspex
/// </returns> /// </returns>
public IEnumerable<PerspexProperty> GetRegisteredProperties() public IEnumerable<PerspexProperty> GetRegisteredProperties()
{ {
Type type = GetType(); return GetRegisteredProperties(GetType());
while (type != null)
{
List<PerspexProperty> list;
if (s_registered.TryGetValue(type, out list))
{
foreach (var p in list)
{
yield return p;
}
}
type = type.GetTypeInfo().BaseType;
}
} }
/// <summary> /// <summary>

5
src/Perspex.Base/PerspexProperty.cs

@ -68,6 +68,11 @@ namespace Perspex
Contract.Requires<NullReferenceException>(valueType != null); Contract.Requires<NullReferenceException>(valueType != null);
Contract.Requires<NullReferenceException>(ownerType != null); Contract.Requires<NullReferenceException>(ownerType != null);
if (name.Contains("."))
{
throw new ArgumentException("'name' may not contain periods.");
}
Name = name; Name = name;
PropertyType = valueType; PropertyType = valueType;
OwnerType = ownerType; OwnerType = ownerType;

28
src/Perspex.Styling/Styling/Selector.cs

@ -6,6 +6,34 @@ using System.Collections.Generic;
namespace Perspex.Styling namespace Perspex.Styling
{ {
/// <summary>
/// A selector in a <see cref="Style"/>.
/// </summary>
/// <remarks>
/// Selectors represented in markup using a CSS-like syntax, e.g. "Button &lt; .dark" which
/// means "A child of a Button with the 'dark' class applied. The preceeding example would be
/// stored in 3 <see cref="Selector"/> objects, linked by the <see cref="Previous"/> property:
/// <list type="number">
/// <item>
/// <term>.dark</term>
/// <description>
/// A selector that selects a control with the 'dark' class applied.
/// </description>
/// </item>
/// <item>
/// <term>&lt;</term>
/// <description>
/// A selector that selects a child of the previous selector.
/// </description>
/// </item>
/// <item>
/// <term>Button</term>
/// <description>
/// A selector that selects a Button type.
/// </description>
/// </item>
/// </list>
/// </remarks>
public class Selector public class Selector
{ {
private readonly Func<IStyleable, SelectorMatch> _evaluate; private readonly Func<IStyleable, SelectorMatch> _evaluate;

19
tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs

@ -15,22 +15,23 @@ namespace Perspex.Base.UnitTests
PerspexProperty p; PerspexProperty p;
p = Class1.FooProperty; p = Class1.FooProperty;
p = Class2.BarProperty; p = Class2.BarProperty;
p = AttachedOwner.AttachedProperty;
} }
[Fact] [Fact]
public void GetProperties_Returns_Registered_Properties() public void GetRegisteredProperties_Returns_Registered_Properties()
{ {
string[] names = PerspexObject.GetProperties(typeof(Class1)).Select(x => x.Name).ToArray(); string[] names = PerspexObject.GetRegisteredProperties(typeof(Class1)).Select(x => x.Name).ToArray();
Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names); Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names);
} }
[Fact] [Fact]
public void GetProperties_Returns_Registered_Properties_For_Base_Types() public void GetRegisteredProperties_Returns_Registered_Properties_For_Base_Types()
{ {
string[] names = PerspexObject.GetProperties(typeof(Class2)).Select(x => x.Name).ToArray(); string[] names = PerspexObject.GetRegisteredProperties(typeof(Class2)).Select(x => x.Name).ToArray();
Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names); Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names);
} }
private class Class1 : PerspexObject private class Class1 : PerspexObject
@ -56,5 +57,11 @@ namespace Perspex.Base.UnitTests
public static readonly PerspexProperty<double?> FredProperty = public static readonly PerspexProperty<double?> FredProperty =
PerspexProperty.Register<Class2, double?>("Fred"); PerspexProperty.Register<Class2, double?>("Fred");
} }
private class AttachedOwner
{
public static readonly PerspexProperty<string> AttachedProperty =
PerspexProperty.RegisterAttached<AttachedOwner, Class1, string>("Attached");
}
} }
} }

12
tests/Perspex.Base.UnitTests/PerspexPropertyTests.cs

@ -25,6 +25,18 @@ namespace Perspex.Base.UnitTests
Assert.Equal(false, target.Inherits); Assert.Equal(false, target.Inherits);
} }
[Fact]
public void Name_Cannot_Contain_Periods()
{
Assert.Throws<ArgumentException>(() => new PerspexProperty<string>(
"Foo.Bar",
typeof(Class1),
"Foo",
false,
BindingMode.OneWay,
null));
}
[Fact] [Fact]
public void GetDefaultValue_Returns_Registered_Value() public void GetDefaultValue_Returns_Registered_Value()
{ {

Loading…
Cancel
Save