diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs b/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
index 36e1d03ea8..8dc4999a12 100644
--- a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
+++ b/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
@@ -88,7 +88,7 @@ namespace Perspex.Markup.Xaml.Context
if (attached == null)
{
propertyName = _xamlMember.Name;
- property = perspexObject.GetRegisteredProperties()
+ property = PerspexPropertyRegistry.Instance.GetRegistered(perspexObject)
.FirstOrDefault(x => x.Name == propertyName);
}
else
@@ -98,7 +98,7 @@ namespace Perspex.Markup.Xaml.Context
propertyName = attached.DeclaringType.UnderlyingType.Name + '.' + _xamlMember.Name;
- property = perspexObject.GetRegisteredProperties()
+ property = PerspexPropertyRegistry.Instance.GetRegistered(perspexObject)
.Where(x => x.IsAttached && x.OwnerType == attached.DeclaringType.UnderlyingType)
.FirstOrDefault(x => x.Name == _xamlMember.Name);
}
diff --git a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs
index f492e01111..1cdc3cc4f1 100644
--- a/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs
+++ b/src/Markup/Perspex.Markup.Xaml/Converters/PerspexPropertyTypeConverter.cs
@@ -46,12 +46,12 @@ namespace Perspex.Markup.Xaml.Converters
}
// First look for non-attached property on the type and then look for an attached property.
- var property = PerspexObject.GetRegisteredProperties(type)
+ var property = PerspexPropertyRegistry.Instance.GetRegistered(type)
.FirstOrDefault(x => x.Name == propertyName);
if (property == null)
{
- property = PerspexObject.GetAttachedProperties(type)
+ property = PerspexPropertyRegistry.Instance.GetAttached(type)
.FirstOrDefault(x => x.Name == propertyName);
}
diff --git a/src/Perspex.Base/Perspex.Base.csproj b/src/Perspex.Base/Perspex.Base.csproj
index b7227d831b..5b3a086f59 100644
--- a/src/Perspex.Base/Perspex.Base.csproj
+++ b/src/Perspex.Base/Perspex.Base.csproj
@@ -50,6 +50,7 @@
+
diff --git a/src/Perspex.Base/PerspexObject.cs b/src/Perspex.Base/PerspexObject.cs
index 337fbf185d..a61e04a8ac 100644
--- a/src/Perspex.Base/PerspexObject.cs
+++ b/src/Perspex.Base/PerspexObject.cs
@@ -25,18 +25,6 @@ namespace Perspex
///
public class PerspexObject : IObservablePropertyBag, INotifyPropertyChanged
{
- ///
- /// The registered properties by type.
- ///
- private static readonly Dictionary> s_registered =
- new Dictionary>();
-
- ///
- /// The registered attached properties by owner type.
- ///
- private static readonly Dictionary> s_attached =
- new Dictionary>();
-
///
/// The parent object that inherited values are inherited from.
///
@@ -70,7 +58,7 @@ namespace Perspex
new PropertyEnricher("Id", GetHashCode()),
});
- foreach (var property in GetRegisteredProperties())
+ foreach (var property in PerspexPropertyRegistry.Instance.GetRegistered(this))
{
object value = property.IsDirect ?
property.Getter(this) :
@@ -129,7 +117,7 @@ namespace Perspex
_inheritanceParent.PropertyChanged -= ParentPropertyChanged;
}
- var inherited = (from property in GetRegisteredProperties(GetType())
+ var inherited = (from property in PerspexPropertyRegistry.Instance.GetRegistered(this)
where property.Inherits
select new
{
@@ -215,92 +203,6 @@ namespace Perspex
}
}
- ///
- /// Gets all s registered on a type.
- ///
- /// The type.
- /// A collection of definitions.
- public static IEnumerable GetRegisteredProperties(Type type)
- {
- Contract.Requires(type != null);
-
- TypeInfo i = type.GetTypeInfo();
-
- while (type != null)
- {
- List list;
-
- if (s_registered.TryGetValue(type, out list))
- {
- foreach (PerspexProperty p in list)
- {
- yield return p;
- }
- }
-
- type = type.GetTypeInfo().BaseType;
- }
- }
-
- ///
- /// 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.
- ///
- /// The type.
- /// The property.
- ///
- /// You won't usually want to call this method directly, instead use the
- /// method.
- ///
- public static void Register(Type type, PerspexProperty property)
- {
- Contract.Requires(type != null);
- Contract.Requires(property != null);
-
- List list;
-
- if (!s_registered.TryGetValue(type, out list))
- {
- list = new List();
- s_registered.Add(type, list);
- }
-
- if (!list.Contains(property))
- {
- 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);
- }
- }
- }
-
public bool CheckAccess() => Dispatcher.UIThread.CheckAccess();
public void VerifyAccess() => Dispatcher.UIThread.VerifyAccess();
@@ -409,7 +311,7 @@ namespace Perspex
object result = PerspexProperty.UnsetValue;
PriorityValue value;
- if (!IsRegistered(property))
+ if (!PerspexPropertyRegistry.Instance.IsRegistered(this, property))
{
ThrowNotRegistered(property);
}
@@ -448,17 +350,6 @@ namespace Perspex
}
}
- ///
- /// Gets all properties that are registered on this object.
- ///
- ///
- /// A collection of objects.
- ///
- public IEnumerable GetRegisteredProperties()
- {
- return GetRegisteredProperties(GetType());
- }
-
///
/// Checks whether a is set on this object.
///
@@ -478,16 +369,6 @@ namespace Perspex
return false;
}
- ///
- /// Checks whether a is registered on this class.
- ///
- /// The property.
- /// True if the property is registered, otherwise false.
- public bool IsRegistered(PerspexProperty property)
- {
- return FindRegistered(property) != null;
- }
-
///
/// Sets a value.
///
@@ -519,7 +400,7 @@ namespace Perspex
PriorityValue v;
var originalValue = value;
- if (!IsRegistered(property))
+ if (!PerspexPropertyRegistry.Instance.IsRegistered(this, property))
{
ThrowNotRegistered(property);
}
@@ -620,7 +501,7 @@ namespace Perspex
{
PriorityValue v;
- if (!IsRegistered(property))
+ if (!PerspexPropertyRegistry.Instance.IsRegistered(this, property))
{
ThrowNotRegistered(property);
}
@@ -751,6 +632,12 @@ namespace Perspex
}
}
+ ///
+ bool IPropertyBag.IsRegistered(PerspexProperty property)
+ {
+ return PerspexPropertyRegistry.Instance.IsRegistered(this, property);
+ }
+
///
/// Gets all priority values set on the object.
///
@@ -936,41 +823,6 @@ namespace Perspex
}
}
- ///
- /// Given a returns a registered perspex property that is
- /// equal.
- ///
- /// The property.
- /// The registered property or null if not found.
- ///
- /// Calling AddOwner on a direct PerspexProperty creates new new PerspexProperty with
- /// an overridden getter and setter. This property is a different object but is equal
- /// according to .
- ///
- public PerspexProperty FindRegistered(PerspexProperty property)
- {
- Type type = GetType();
-
- while (type != null)
- {
- List list;
-
- if (s_registered.TryGetValue(type, out list))
- {
- var index = list.IndexOf(property);
-
- if (index != -1)
- {
- return list[index];
- }
- }
-
- type = type.GetTypeInfo().BaseType;
- }
-
- return null;
- }
-
///
/// Given a returns a registered perspex property that is
/// equal or throws if not found.
@@ -979,7 +831,7 @@ namespace Perspex
/// The registered property.
public PerspexProperty GetRegistered(PerspexProperty property)
{
- var result = FindRegistered(property);
+ var result = PerspexPropertyRegistry.Instance.FindRegistered(this, property);
if (result == null)
{
diff --git a/src/Perspex.Base/PerspexProperty.cs b/src/Perspex.Base/PerspexProperty.cs
index e44284b570..62a3d5cfe2 100644
--- a/src/Perspex.Base/PerspexProperty.cs
+++ b/src/Perspex.Base/PerspexProperty.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using Perspex.Utilities;
@@ -367,7 +368,7 @@ namespace Perspex
notifying,
false);
- PerspexObject.Register(typeof(TOwner), result);
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
}
@@ -395,7 +396,7 @@ namespace Perspex
Cast(getter),
Cast(setter));
- PerspexObject.Register(typeof(TOwner), result);
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
}
@@ -431,7 +432,7 @@ namespace Perspex
null,
true);
- PerspexObject.Register(typeof(THost), result);
+ PerspexPropertyRegistry.Instance.Register(typeof(THost), result);
return result;
}
@@ -468,7 +469,7 @@ namespace Perspex
null,
true);
- PerspexObject.Register(typeof(THost), result);
+ PerspexPropertyRegistry.Instance.Register(typeof(THost), result);
return result;
}
diff --git a/src/Perspex.Base/PerspexPropertyRegistry.cs b/src/Perspex.Base/PerspexPropertyRegistry.cs
new file mode 100644
index 0000000000..d5df44a66f
--- /dev/null
+++ b/src/Perspex.Base/PerspexPropertyRegistry.cs
@@ -0,0 +1,244 @@
+// 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;
+
+namespace Perspex
+{
+ ///
+ /// Tracks registered instances.
+ ///
+ public class PerspexPropertyRegistry
+ {
+ ///
+ /// The registered properties by type.
+ ///
+ private readonly Dictionary> _registered =
+ new Dictionary>();
+
+ ///
+ /// The registered attached properties by owner type.
+ ///
+ private readonly Dictionary> _attached =
+ new Dictionary>();
+
+ ///
+ /// Gets the instance
+ ///
+ public static PerspexPropertyRegistry Instance { get; }
+ = new PerspexPropertyRegistry();
+
+ ///
+ /// Gets all attached s registered by an owner.
+ ///
+ /// The owner type.
+ /// A collection of definitions.
+ public IEnumerable GetAttached(Type ownerType)
+ {
+ List list;
+
+ if (_attached.TryGetValue(ownerType, out list))
+ {
+ return list;
+ }
+
+ return Enumerable.Empty();
+ }
+
+ ///
+ /// Gets all s registered on a type.
+ ///
+ /// The type.
+ /// A collection of definitions.
+ public IEnumerable GetRegistered(Type type)
+ {
+ Contract.Requires(type != null);
+
+ var i = type.GetTypeInfo();
+
+ while (type != null)
+ {
+ List list;
+
+ if (_registered.TryGetValue(type, out list))
+ {
+ foreach (PerspexProperty p in list)
+ {
+ yield return p;
+ }
+ }
+
+ type = type.GetTypeInfo().BaseType;
+ }
+ }
+
+ ///
+ /// Gets all s registered on a object.
+ ///
+ /// The object.
+ /// A collection of definitions.
+ public IEnumerable GetRegistered(PerspexObject o)
+ {
+ Contract.Requires(o != null);
+
+ return GetRegistered(o.GetType());
+ }
+
+ ///
+ /// Finds registered on a type.
+ ///
+ /// The type.
+ /// The property.
+ /// The registered property or null if not found.
+ ///
+ /// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
+ /// different object but is equal according to .
+ ///
+ public PerspexProperty FindRegistered(Type type, PerspexProperty property)
+ {
+ while (type != null)
+ {
+ List list;
+
+ if (_registered.TryGetValue(type, out list))
+ {
+ var index = list.IndexOf(property);
+
+ if (index != -1)
+ {
+ return list[index];
+ }
+ }
+
+ type = type.GetTypeInfo().BaseType;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Finds registered on an object.
+ ///
+ /// The object.
+ /// The property.
+ /// The registered property or null if not found.
+ ///
+ /// Calling AddOwner on a PerspexProperty creates a new PerspexProperty that is a
+ /// different object but is equal according to .
+ ///
+ public PerspexProperty FindRegistered(object o, PerspexProperty property)
+ {
+ return FindRegistered(o.GetType(), property);
+ }
+
+ ///
+ /// Finds a registered property on a type by name.
+ ///
+ /// The type.
+ ///
+ /// The property name. If an attached property it should be in the form
+ /// "OwnerType.PropertyName".
+ ///
+ ///
+ /// The registered property or null if no matching property found.
+ ///
+ public PerspexProperty FindRegistered(Type type, string name)
+ {
+ Contract.Requires(type != null);
+ Contract.Requires(name != null);
+
+ var parts = name.Split('.');
+
+ if (parts.Length < 1 || parts.Length > 2)
+ {
+ throw new ArgumentException("Invalid property name.");
+ }
+
+ if (parts.Length == 1)
+ {
+ var result = GetRegistered(type)
+ .FirstOrDefault(x => !x.IsAttached && x.Name == parts[0]);
+
+ if (result != null)
+ {
+ return result;
+ }
+
+ // A type can .AddOwner an attached property.
+ return GetRegistered(type)
+ .FirstOrDefault(x => x.Name == parts[0]);
+ }
+ else
+ {
+ return GetRegistered(type)
+ .FirstOrDefault(x => x.IsAttached && x.OwnerType.Name == parts[0] && x.Name == parts[1]);
+ }
+ }
+ ///
+ /// Checks whether a is registered on a type.
+ ///
+ /// The type.
+ /// The property.
+ /// True if the property is registered, otherwise false.
+ public bool IsRegistered(Type type, PerspexProperty property)
+ {
+ return FindRegistered(type, property) != null;
+ }
+
+ ///
+ /// Checks whether a is registered on a object.
+ ///
+ /// The object.
+ /// The property.
+ /// True if the property is registered, otherwise false.
+ public bool IsRegistered(object o, PerspexProperty property)
+ {
+ return IsRegistered(o.GetType(), property);
+ }
+
+ ///
+ /// Registers a on a type.
+ ///
+ /// The type.
+ /// The property.
+ ///
+ /// You won't usually want to call this method directly, instead use the
+ /// method.
+ ///
+ public void Register(Type type, PerspexProperty property)
+ {
+ Contract.Requires(type != null);
+ Contract.Requires(property != null);
+
+ List list;
+
+ if (!_registered.TryGetValue(type, out list))
+ {
+ list = new List();
+ _registered.Add(type, list);
+ }
+
+ if (!list.Contains(property))
+ {
+ list.Add(property);
+ }
+
+ if (property.IsAttached)
+ {
+ if (!_attached.TryGetValue(property.OwnerType, out list))
+ {
+ list = new List();
+ _attached.Add(property.OwnerType, list);
+ }
+
+ if (!list.Contains(property))
+ {
+ list.Add(property);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Perspex.Base/PerspexProperty`1.cs b/src/Perspex.Base/PerspexProperty`1.cs
index 73ac84e102..9a39b95c0c 100644
--- a/src/Perspex.Base/PerspexProperty`1.cs
+++ b/src/Perspex.Base/PerspexProperty`1.cs
@@ -105,7 +105,7 @@ namespace Perspex
"You must provide a new getter and setter when calling AddOwner on a direct PerspexProperty.");
}
- PerspexObject.Register(typeof(TOwner), this);
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), this);
return this;
}
@@ -119,8 +119,12 @@ namespace Perspex
Action setter = null)
where TOwner : PerspexObject
{
- var result = new PerspexProperty(this, CastReturn(getter), CastParam1(setter));
- PerspexObject.Register(typeof(TOwner), result);
+ var result = new PerspexProperty(
+ this,
+ CastReturn(getter),
+ CastParam1(setter));
+
+ PerspexPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
}
diff --git a/src/Perspex.Diagnostics/Debug.cs b/src/Perspex.Diagnostics/Debug.cs
index d5362c3257..1e07363a71 100644
--- a/src/Perspex.Diagnostics/Debug.cs
+++ b/src/Perspex.Diagnostics/Debug.cs
@@ -37,7 +37,7 @@ namespace Perspex.Diagnostics
builder.Append(" ");
builder.AppendLine(control.Classes.ToString());
- foreach (var property in control.GetRegisteredProperties())
+ foreach (var property in PerspexPropertyRegistry.Instance.GetRegistered(control))
{
var value = control.GetDiagnostic(property);
diff --git a/src/Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs
index 8a45ec8244..73abcf4a04 100644
--- a/src/Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs
+++ b/src/Perspex.Diagnostics/ViewModels/ControlDetailsViewModel.cs
@@ -14,7 +14,7 @@ namespace Perspex.Diagnostics.ViewModels
{
if (control != null)
{
- Properties = control.GetRegisteredProperties()
+ Properties = PerspexPropertyRegistry.Instance.GetRegistered(control)
.Select(x => new PropertyDetails(control, x))
.OrderBy(x => x.IsAttached)
.ThenBy(x => x.Name);
diff --git a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
index e343d3e512..398d073db5 100644
--- a/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
+++ b/tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj
@@ -75,6 +75,7 @@
+
diff --git a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
index 7fa816cf82..4233806b65 100644
--- a/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
+++ b/tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs
@@ -18,30 +18,6 @@ namespace Perspex.Base.UnitTests
p = AttachedOwner.AttachedProperty;
}
- [Fact]
- public void GetRegisteredProperties_Returns_Registered_Properties()
- {
- string[] names = PerspexObject.GetRegisteredProperties(typeof(Class1)).Select(x => x.Name).ToArray();
-
- Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names);
- }
-
- [Fact]
- public void GetRegisteredProperties_Returns_Registered_Properties_For_Base_Types()
- {
- string[] names = PerspexObject.GetRegisteredProperties(typeof(Class2)).Select(x => x.Name).ToArray();
-
- 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);
- }
-
[Fact]
public void IsSet_Returns_False_For_Unset_Property()
{
diff --git a/tests/Perspex.Base.UnitTests/PerspexPropertyRegistryTests.cs b/tests/Perspex.Base.UnitTests/PerspexPropertyRegistryTests.cs
new file mode 100644
index 0000000000..c0a9eb1f4f
--- /dev/null
+++ b/tests/Perspex.Base.UnitTests/PerspexPropertyRegistryTests.cs
@@ -0,0 +1,79 @@
+// 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.Linq;
+using System.Reactive.Linq;
+using Xunit;
+
+namespace Perspex.Base.UnitTests
+{
+ public class PerspexPropertyRegistryTests
+ {
+ public PerspexPropertyRegistryTests()
+ {
+ // Ensure properties are registered.
+ PerspexProperty p;
+ p = Class1.FooProperty;
+ p = Class2.BarProperty;
+ p = AttachedOwner.AttachedProperty;
+ }
+
+ [Fact]
+ public void GetRegistered_Returns_Registered_Properties()
+ {
+ string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class1))
+ .Select(x => x.Name)
+ .ToArray();
+
+ Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names);
+ }
+
+ [Fact]
+ public void GetRegistered_Returns_Registered_Properties_For_Base_Types()
+ {
+ string[] names = PerspexPropertyRegistry.Instance.GetRegistered(typeof(Class2))
+ .Select(x => x.Name)
+ .ToArray();
+
+ Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names);
+ }
+
+ [Fact]
+ public void GetAttached_Returns_Registered_Properties_For_Base_Types()
+ {
+ string[] names = PerspexPropertyRegistry.Instance.GetAttached(typeof(AttachedOwner)).Select(x => x.Name).ToArray();
+
+ Assert.Equal(new[] { "Attached" }, names);
+ }
+
+ private class Class1 : PerspexObject
+ {
+ public static readonly PerspexProperty FooProperty =
+ PerspexProperty.Register("Foo");
+
+ public static readonly PerspexProperty BazProperty =
+ PerspexProperty.Register("Baz");
+
+ public static readonly PerspexProperty QuxProperty =
+ PerspexProperty.Register("Qux");
+ }
+
+ private class Class2 : Class1
+ {
+ public static readonly PerspexProperty BarProperty =
+ PerspexProperty.Register("Bar");
+
+ public static readonly PerspexProperty FlobProperty =
+ PerspexProperty.Register("Flob");
+
+ public static readonly PerspexProperty FredProperty =
+ PerspexProperty.Register("Fred");
+ }
+
+ private class AttachedOwner
+ {
+ public static readonly PerspexProperty AttachedProperty =
+ PerspexProperty.RegisterAttached("Attached");
+ }
+ }
+}