7 changed files with 210 additions and 92 deletions
@ -0,0 +1,175 @@ |
|||||
|
// 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.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using OmniXaml; |
||||
|
using OmniXaml.Builder; |
||||
|
using OmniXaml.TypeConversion; |
||||
|
using OmniXaml.TypeConversion.BuiltInConverters; |
||||
|
using OmniXaml.Typing; |
||||
|
using Perspex.Collections; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Markup.Xaml.Converters; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Media.Imaging; |
||||
|
using Perspex.Metadata; |
||||
|
using Perspex.Styling; |
||||
|
using OmniMetadata = OmniXaml.Typing.Metadata; |
||||
|
|
||||
|
namespace Perspex.Markup.Xaml.Context |
||||
|
{ |
||||
|
public class PerspexTypeFeatureProvider : ITypeFeatureProvider |
||||
|
{ |
||||
|
private Dictionary<Type, OmniMetadata> _metadata = |
||||
|
new Dictionary<Type, OmniMetadata>(); |
||||
|
|
||||
|
private Dictionary<Type, TypeConverterRegistration> _typeConverters = |
||||
|
new Dictionary<Type, TypeConverterRegistration>(); |
||||
|
|
||||
|
public IEnumerable<TypeConverterRegistration> TypeConverters => _typeConverters.Values; |
||||
|
|
||||
|
public PerspexTypeFeatureProvider() |
||||
|
{ |
||||
|
RegisterTypeConverters(); |
||||
|
} |
||||
|
|
||||
|
public string GetContentPropertyName(Type type) |
||||
|
{ |
||||
|
return GetMetadata(type)?.ContentProperty; |
||||
|
} |
||||
|
|
||||
|
public OmniMetadata GetMetadata(Type type) |
||||
|
{ |
||||
|
OmniMetadata result; |
||||
|
|
||||
|
if (!_metadata.TryGetValue(type, out result)) |
||||
|
{ |
||||
|
result = LoadMetadata(type); |
||||
|
_metadata.Add(type, result); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public ITypeConverter GetTypeConverter(Type type) |
||||
|
{ |
||||
|
TypeConverterRegistration result; |
||||
|
_typeConverters.TryGetValue(type, out result); |
||||
|
return result?.TypeConverter; |
||||
|
} |
||||
|
|
||||
|
public void RegisterMetadata(Type type, OmniMetadata metadata) |
||||
|
{ |
||||
|
_metadata.Add(type, metadata); |
||||
|
} |
||||
|
|
||||
|
public void RegisterTypeConverter(Type type, ITypeConverter converter) |
||||
|
{ |
||||
|
_typeConverters.Add(type, new TypeConverterRegistration(type, converter)); |
||||
|
} |
||||
|
|
||||
|
private static OmniMetadata LoadMetadata(Type type) |
||||
|
{ |
||||
|
return new OmniMetadata |
||||
|
{ |
||||
|
ContentProperty = GetContentProperty(type), |
||||
|
PropertyDependencies = GetPropertyDependencies(type), |
||||
|
RuntimePropertyName = type.GetRuntimeProperty("Name")?.Name, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private static string GetContentProperty(Type type) |
||||
|
{ |
||||
|
while (type != null) |
||||
|
{ |
||||
|
var properties = type.GetTypeInfo().DeclaredProperties |
||||
|
.Where(x => x.GetCustomAttribute<ContentAttribute>() != null); |
||||
|
string result = null; |
||||
|
|
||||
|
foreach (var property in properties) |
||||
|
{ |
||||
|
if (result != null) |
||||
|
{ |
||||
|
throw new Exception($"Content property defined more than once on {type}."); |
||||
|
} |
||||
|
|
||||
|
result = property.Name; |
||||
|
} |
||||
|
|
||||
|
if (result != null) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
type = type.GetTypeInfo().BaseType; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static DependencyRegistrations GetPropertyDependencies(Type type) |
||||
|
{ |
||||
|
var result = new List<DependencyRegistration>(); |
||||
|
|
||||
|
while (type != null) |
||||
|
{ |
||||
|
var registrations = type.GetTypeInfo().DeclaredProperties |
||||
|
.Select(x => new |
||||
|
{ |
||||
|
Property = x.Name, |
||||
|
Attributes = x.GetCustomAttributes<DependsOnAttribute>(), |
||||
|
}) |
||||
|
.Where(x => x.Attributes.Any()) |
||||
|
.SelectMany(x => x.Attributes.Select(y => new DependencyRegistration(x.Property, y.Name))); |
||||
|
|
||||
|
result.AddRange(registrations); |
||||
|
type = type.GetTypeInfo().BaseType; |
||||
|
} |
||||
|
|
||||
|
return result.Count > 0 ? new DependencyRegistrations(result) : null; |
||||
|
} |
||||
|
|
||||
|
private void RegisterTypeConverters() |
||||
|
{ |
||||
|
// HACK: For now these are hard-coded. Hopefully when the .NET Standard Platform
|
||||
|
// is available we can use the System.ComponentModel.TypeConverters so don't want to
|
||||
|
// spend time for now inventing a mechanism to register type converters if it's all
|
||||
|
// going to change.
|
||||
|
RegisterTypeConverter(typeof(string), new StringTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(int), new IntTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(long), new IntTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(short), new IntTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(double), new DoubleTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(float), new IntTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(bool), new BooleanConverter()); |
||||
|
RegisterTypeConverter(typeof(Type), new TypeTypeConverter()); |
||||
|
|
||||
|
RegisterTypeConverter(typeof(IBitmap), new BitmapTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Brush), new BrushTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Color), new ColorTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Classes), new ClassesTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(ColumnDefinitions), new ColumnDefinitionsTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Geometry), new GeometryTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(GridLength), new GridLengthTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(KeyGesture), new KeyGestureConverter()); |
||||
|
RegisterTypeConverter(typeof(PerspexList<double>), new PerspexListTypeConverter<double>()); |
||||
|
RegisterTypeConverter(typeof(IMemberSelector), new MemberSelectorTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Point), new PointTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(IList<Point>), new PointsListTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(PerspexProperty), new PerspexPropertyTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(RelativePoint), new RelativePointTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(RelativeRect), new RelativeRectTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(RowDefinitions), new RowDefinitionsTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Selector), new SelectorTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Thickness), new ThicknessTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(TimeSpan), new TimeSpanTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Uri), new UriTypeConverter()); |
||||
|
RegisterTypeConverter(typeof(Cursor), new CursorTypeConverter()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1 +1 @@ |
|||||
Subproject commit 2b28639da15e5f43f4b713e0b115d1bb41679cc6 |
Subproject commit 5ed61489b4db4578680004cd6ddbe811cdd91f8f |
||||
@ -0,0 +1,30 @@ |
|||||
|
// 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.Metadata |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Indicates that the property depends on the value of another property in markup.
|
||||
|
/// </summary>
|
||||
|
[AttributeUsage(AttributeTargets.Property)] |
||||
|
public class DependsOnAttribute : Attribute |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="DependsOnAttribute"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="propertyName">
|
||||
|
/// The name of the property that this property depends on.
|
||||
|
/// </param>
|
||||
|
public DependsOnAttribute(string propertyName) |
||||
|
{ |
||||
|
Name = propertyName; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the name of the property that this property depends on.
|
||||
|
/// </summary>
|
||||
|
public string Name { get; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue