diff --git a/src/Avalonia.Base/AttachedProperty.cs b/src/Avalonia.Base/AttachedProperty.cs
index 9d4d40bfef..fdb04b6dfc 100644
--- a/src/Avalonia.Base/AttachedProperty.cs
+++ b/src/Avalonia.Base/AttachedProperty.cs
@@ -9,7 +9,7 @@ namespace Avalonia
/// An attached avalonia property.
///
/// The type of the property's value.
- public class AttachedProperty : StyledPropertyBase
+ public class AttachedProperty : StyledProperty
{
///
/// Initializes a new instance of the class.
@@ -35,11 +35,10 @@ namespace Avalonia
///
/// The owner type.
/// The property.
- public StyledProperty AddOwner() where TOwner : IAvaloniaObject
+ public new AttachedProperty AddOwner() where TOwner : IAvaloniaObject
{
- var result = new StyledProperty(this, typeof(TOwner));
- AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
- return result;
+ AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this);
+ return this;
}
}
}
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index a46d567d28..4ab813333d 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -12,7 +12,6 @@ using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.Threading;
using Avalonia.Utilities;
-using System.Reactive.Concurrency;
namespace Avalonia
{
@@ -218,11 +217,6 @@ namespace Avalonia
}
else
{
- if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
- {
- ThrowNotRegistered(property);
- }
-
return GetValueInternal(property);
}
}
@@ -377,11 +371,6 @@ namespace Avalonia
{
PriorityValue v;
- if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
- {
- ThrowNotRegistered(property);
- }
-
if (!_values.TryGetValue(property, out v))
{
v = CreatePriorityValue(property);
@@ -804,11 +793,6 @@ namespace Avalonia
var originalValue = value;
- if (!AvaloniaPropertyRegistry.Instance.IsRegistered(this, property))
- {
- ThrowNotRegistered(property);
- }
-
if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
@@ -836,18 +820,32 @@ namespace Avalonia
}
///
- /// Given a returns a registered avalonia property that is
- /// equal or throws if not found.
+ /// Given a direct property, returns a registered avalonia property that is equivalent or
+ /// throws if not found.
///
/// The property.
/// The registered property.
- public AvaloniaProperty GetRegistered(AvaloniaProperty property)
+ private AvaloniaProperty GetRegistered(AvaloniaProperty property)
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(this, property);
+ var direct = property as IDirectPropertyAccessor;
+
+ if (direct == null)
+ {
+ throw new AvaloniaInternalException(
+ "AvaloniaObject.GetRegistered should only be called for direct properties");
+ }
+
+ if (property.OwnerType.IsAssignableFrom(GetType()))
+ {
+ return property;
+ }
+
+ var result = AvaloniaPropertyRegistry.Instance.GetRegistered(this)
+ .FirstOrDefault(x => x == property);
if (result == null)
{
- ThrowNotRegistered(property);
+ throw new ArgumentException($"Property '{property.Name} not registered on '{this.GetType()}");
}
return result;
@@ -898,15 +896,5 @@ namespace Avalonia
value,
priority);
}
-
- ///
- /// Throws an exception indicating that the specified property is not registered on this
- /// object.
- ///
- /// The property
- private void ThrowNotRegistered(AvaloniaProperty p)
- {
- throw new ArgumentException($"Property '{p.Name} not registered on '{this.GetType()}");
- }
}
}
diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs
index fb78e3b2a0..f7dabd3a43 100644
--- a/src/Avalonia.Base/AvaloniaProperty.cs
+++ b/src/Avalonia.Base/AvaloniaProperty.cs
@@ -311,7 +311,9 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode);
var result = new AttachedProperty(name, typeof(TOwner), metadata, inherits);
- AvaloniaPropertyRegistry.Instance.Register(typeof(THost), result);
+ var registry = AvaloniaPropertyRegistry.Instance;
+ registry.Register(typeof(TOwner), result);
+ registry.RegisterAttached(typeof(THost), result);
return result;
}
@@ -344,7 +346,9 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode);
var result = new AttachedProperty(name, ownerType, metadata, inherits);
- AvaloniaPropertyRegistry.Instance.Register(typeof(THost), result);
+ var registry = AvaloniaPropertyRegistry.Instance;
+ registry.Register(ownerType, result);
+ registry.RegisterAttached(typeof(THost), result);
return result;
}
diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
index ec1643427b..c0a4ace6ed 100644
--- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
+++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
using System.Runtime.CompilerServices;
namespace Avalonia
@@ -14,23 +13,14 @@ namespace Avalonia
///
public class AvaloniaPropertyRegistry
{
- ///
- /// The registered properties by type.
- ///
private readonly Dictionary> _registered =
new Dictionary>();
-
- ///
- /// The registered properties by type cached values to increase performance.
- ///
- private readonly Dictionary> _registeredCache =
- new Dictionary>();
-
- ///
- /// The registered attached properties by owner type.
- ///
private readonly Dictionary> _attached =
new Dictionary>();
+ private readonly Dictionary> _registeredCache =
+ new Dictionary>();
+ private readonly Dictionary> _attachedCache =
+ new Dictionary>();
///
/// Gets the instance
@@ -39,51 +29,68 @@ namespace Avalonia
= new AvaloniaPropertyRegistry();
///
- /// Gets all attached s registered by an owner.
+ /// Gets all non-attached s registered on a type.
///
- /// The owner type.
+ /// The type.
/// A collection of definitions.
- public IEnumerable GetAttached(Type ownerType)
+ public IEnumerable GetRegistered(Type type)
{
- Dictionary inner;
+ Contract.Requires(type != null);
+
+ if (_registeredCache.TryGetValue(type, out var result))
+ {
+ return result;
+ }
- // Ensure the type's static ctor has been run.
- RuntimeHelpers.RunClassConstructor(ownerType.TypeHandle);
+ var t = type;
+ result = new List();
- if (_attached.TryGetValue(ownerType, out inner))
+ while (t != null)
{
- return inner.Values;
+ // Ensure the type's static ctor has been run.
+ RuntimeHelpers.RunClassConstructor(t.TypeHandle);
+
+ if (_registered.TryGetValue(t, out var registered))
+ {
+ result.AddRange(registered.Values);
+ }
+
+ t = t.BaseType;
}
- return Enumerable.Empty();
+ _registeredCache.Add(type, result);
+ return result;
}
///
- /// Gets all s registered on a type.
+ /// Gets all attached s registered on a type.
///
/// The type.
/// A collection of definitions.
- public IEnumerable GetRegistered(Type type)
+ public IEnumerable GetRegisteredAttached(Type type)
{
Contract.Requires(type != null);
- while (type != null)
+ if (_attachedCache.TryGetValue(type, out var result))
{
- // Ensure the type's static ctor has been run.
- RuntimeHelpers.RunClassConstructor(type.TypeHandle);
+ return result;
+ }
- Dictionary inner;
+ var t = type;
+ result = new List();
- if (_registered.TryGetValue(type, out inner))
+ while (t != null)
+ {
+ if (_attached.TryGetValue(t, out var attached))
{
- foreach (var p in inner)
- {
- yield return p.Value;
- }
+ result.AddRange(attached.Values);
}
- type = type.GetTypeInfo().BaseType;
+ t = t.BaseType;
}
+
+ _attachedCache.Add(type, result);
+ return result;
}
///
@@ -99,142 +106,92 @@ namespace Avalonia
}
///
- /// Finds a registered on a type.
+ /// Finds a registered non-attached property on a type by name.
///
/// The type.
- /// The property.
- /// The registered property or null if not found.
- ///
- /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a
- /// different object but is equal according to .
- ///
- public AvaloniaProperty FindRegistered(Type type, AvaloniaProperty property)
+ /// The property name.
+ ///
+ /// The registered property or null if no matching property found.
+ ///
+ ///
+ /// The property name contains a '.'.
+ ///
+ public AvaloniaProperty FindRegistered(Type type, string name)
{
- Type currentType = type;
- Dictionary cache;
- AvaloniaProperty result;
+ Contract.Requires(type != null);
+ Contract.Requires(name != null);
- if (_registeredCache.TryGetValue(type, out cache))
+ if (name.Contains('.'))
{
- if (cache.TryGetValue(property.Id, out result))
- {
- return result;
- }
+ throw new InvalidOperationException("Attached properties not supported.");
}
- while (currentType != null)
- {
- Dictionary inner;
-
- if (_registered.TryGetValue(currentType, out inner))
- {
- if (inner.TryGetValue(property.Id, out result))
- {
- if (cache == null)
- {
- _registeredCache[type] = cache = new Dictionary();
- }
-
- cache[property.Id] = result;
-
- return result;
- }
- }
-
- currentType = currentType.GetTypeInfo().BaseType;
- }
-
- return null;
+ return GetRegistered(type).FirstOrDefault(x => x.Name == name);
}
///
- /// Finds registered on an object.
+ /// Finds a registered non-attached property on a type by name.
///
/// The object.
- /// The property.
- /// The registered property or null if not found.
- ///
- /// Calling AddOwner on a AvaloniaProperty creates a new AvaloniaProperty that is a
- /// different object but is equal according to .
- ///
- public AvaloniaProperty FindRegistered(object o, AvaloniaProperty property)
+ /// The property name.
+ ///
+ /// The registered property or null if no matching property found.
+ ///
+ ///
+ /// The property name contains a '.'.
+ ///
+ public AvaloniaProperty FindRegistered(AvaloniaObject o, string name)
{
- return FindRegistered(o.GetType(), property);
+ Contract.Requires(o != null);
+ Contract.Requires(name != null);
+
+ return FindRegistered(o.GetType(), name);
}
///
- /// Finds a registered property on a type by name.
+ /// Finds a registered attached property on a type by name.
///
/// The type.
- ///
- /// The property name. If an attached property it should be in the form
- /// "OwnerType.PropertyName".
- ///
+ /// The owner type.
+ /// The property name.
///
/// The registered property or null if no matching property found.
///
- public AvaloniaProperty FindRegistered(Type type, string name)
+ ///
+ /// The property name contains a '.'.
+ ///
+ public AvaloniaProperty FindRegisteredAttached(Type type, Type ownerType, string name)
{
Contract.Requires(type != null);
+ Contract.Requires(ownerType != null);
Contract.Requires(name != null);
- var parts = name.Split('.');
- var types = GetImplementedTypes(type).ToList();
-
- if (parts.Length < 1 || parts.Length > 2)
+ if (name.Contains('.'))
{
- throw new ArgumentException("Invalid property name.");
+ throw new InvalidOperationException("Attached properties not supported.");
}
- string propertyName;
- var results = GetRegistered(type);
-
- if (parts.Length == 1)
- {
- propertyName = parts[0];
- results = results.Where(x => !x.IsAttached || types.Contains(x.OwnerType.Name));
- }
- else
- {
- if (!types.Contains(parts[0]))
- {
- results = results.Where(x => x.OwnerType.Name == parts[0]);
- }
-
- propertyName = parts[1];
- }
-
- return results.FirstOrDefault(x => x.Name == propertyName);
+ return GetRegisteredAttached(type).FirstOrDefault(x => x.Name == name);
}
///
- /// Finds a registered property on an object by name.
+ /// Finds a registered non-attached property on a type by name.
///
/// The object.
- ///
- /// The property name. If an attached property it should be in the form
- /// "OwnerType.PropertyName".
- ///
+ /// The owner type.
+ /// The property name.
///
/// The registered property or null if no matching property found.
///
- public AvaloniaProperty FindRegistered(AvaloniaObject o, string name)
+ ///
+ /// The property name contains a '.'.
+ ///
+ public AvaloniaProperty FindRegisteredAttached(AvaloniaObject o, Type ownerType, string name)
{
- return FindRegistered(o.GetType(), name);
- }
+ Contract.Requires(o != null);
+ Contract.Requires(name != null);
- ///
- /// Returns a type and all its base types.
- ///
- /// The type.
- /// The type and all its base types.
- private IEnumerable GetImplementedTypes(Type type)
- {
- while (type != null)
- {
- yield return type.Name;
- type = type.GetTypeInfo().BaseType;
- }
+ return FindRegisteredAttached(o.GetType(), ownerType, name);
}
///
@@ -245,7 +202,11 @@ namespace Avalonia
/// True if the property is registered, otherwise false.
public bool IsRegistered(Type type, AvaloniaProperty property)
{
- return FindRegistered(type, property) != null;
+ Contract.Requires(type != null);
+ Contract.Requires(property != null);
+
+ return Instance.GetRegistered(type).Any(x => x == property) ||
+ Instance.GetRegisteredAttached(type).Any(x => x == property);
}
///
@@ -256,6 +217,9 @@ namespace Avalonia
/// True if the property is registered, otherwise false.
public bool IsRegistered(object o, AvaloniaProperty property)
{
+ Contract.Requires(o != null);
+ Contract.Requires(property != null);
+
return IsRegistered(o.GetType(), property);
}
@@ -274,34 +238,53 @@ namespace Avalonia
Contract.Requires(type != null);
Contract.Requires(property != null);
- Dictionary inner;
-
- if (!_registered.TryGetValue(type, out inner))
+ if (!_registered.TryGetValue(type, out var inner))
{
inner = new Dictionary();
+ inner.Add(property.Id, property);
_registered.Add(type, inner);
}
-
- if (!inner.ContainsKey(property.Id))
+ else if (!inner.ContainsKey(property.Id))
{
inner.Add(property.Id, property);
}
+
+ _registeredCache.Clear();
+ }
- if (property.IsAttached)
+ ///
+ /// Registers an attached on a type.
+ ///
+ /// The type.
+ /// The property.
+ ///
+ /// You won't usually want to call this method directly, instead use the
+ ///
+ /// method.
+ ///
+ public void RegisterAttached(Type type, AvaloniaProperty property)
+ {
+ Contract.Requires(type != null);
+ Contract.Requires(property != null);
+
+ if (!property.IsAttached)
{
- if (!_attached.TryGetValue(property.OwnerType, out inner))
- {
- inner = new Dictionary();
- _attached.Add(property.OwnerType, inner);
- }
+ throw new InvalidOperationException(
+ "Cannot register a non-attached property as attached.");
+ }
- if (!inner.ContainsKey(property.Id))
- {
- inner.Add(property.Id, property);
- }
+ if (!_attached.TryGetValue(type, out var inner))
+ {
+ inner = new Dictionary();
+ inner.Add(property.Id, property);
+ _attached.Add(type, inner);
+ }
+ else
+ {
+ inner.Add(property.Id, property);
}
- _registeredCache.Clear();
+ _attachedCache.Clear();
}
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Base/DirectProperty.cs b/src/Avalonia.Base/DirectProperty.cs
index 8352528285..1ce73c20ba 100644
--- a/src/Avalonia.Base/DirectProperty.cs
+++ b/src/Avalonia.Base/DirectProperty.cs
@@ -75,6 +75,9 @@ namespace Avalonia
///
public Action Setter { get; }
+ ///
+ Type IDirectPropertyAccessor.Owner => typeof(TOwner);
+
///
/// Registers the direct property on another type.
///
diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs
index 62aeef73c7..4f46652693 100644
--- a/src/Avalonia.Base/IDirectPropertyAccessor.cs
+++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs
@@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
+using System;
+
namespace Avalonia
{
///
@@ -14,6 +16,11 @@ namespace Avalonia
///
bool IsReadOnly { get; }
+ ///
+ /// Gets the class that registered the property.
+ ///
+ Type Owner { get; }
+
///
/// Gets the value of the property on the instance.
///
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
index a34ccaa413..bc3caff3b9 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
@@ -53,10 +53,7 @@ namespace Avalonia.Markup.Xaml.Converters
}
}
- // First look for non-attached property on the type and then look for an attached property.
- var property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, s) ??
- AvaloniaPropertyRegistry.Instance.GetAttached(type)
- .FirstOrDefault(x => x.Name == propertyName);
+ AvaloniaProperty property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, propertyName);
if (property == null)
{
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
index bdb21abd77..fda5da902a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
@@ -200,8 +200,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
var type = (getter ?? setter).DeclaringType;
- var prop = AvaloniaPropertyRegistry.Instance.GetAttached(type)
- .FirstOrDefault(v => v.Name == attachablePropertyName);
+ var prop = AvaloniaPropertyRegistry.Instance.FindRegistered(type, attachablePropertyName);
if (prop != null)
{
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
index 7de96ea220..59dbba7084 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
@@ -19,16 +19,36 @@ namespace Avalonia.Markup.Xaml.PortableXaml
public class AvaloniaXamlType : XamlType
{
+ static readonly AvaloniaPropertyTypeConverter propertyTypeConverter = new AvaloniaPropertyTypeConverter();
+
public AvaloniaXamlType(Type underlyingType, XamlSchemaContext schemaContext) :
base(underlyingType, schemaContext)
{
}
+ protected override XamlMember LookupAttachableMember(string name)
+ {
+ var m = base.LookupAttachableMember(name);
+
+ if (m == null)
+ {
+ // Might be an AddOwnered attached property.
+ var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(UnderlyingType, name);
+
+ if (avProp?.IsAttached == true)
+ {
+ return new AvaloniaPropertyXamlMember(avProp, this);
+ }
+ }
+
+ return m;
+ }
+
protected override XamlMember LookupMember(string name, bool skipReadOnlyCheck)
{
var m = base.LookupMember(name, skipReadOnlyCheck);
- if (m == null)
+ if (m == null && !name.Contains("."))
{
//so far Portable.xaml haven't found the member/property
//but what if we have AvaloniaProperty
diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
index 90eabc69fb..ac64459dd7 100644
--- a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
+++ b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Linq;
using System.Reactive.Linq;
using Avalonia.Data;
@@ -15,9 +16,9 @@ namespace Avalonia.Markup.Data.Plugins
///
public bool Match(object obj, string propertyName)
{
- if (obj is AvaloniaObject a)
+ if (obj is AvaloniaObject o)
{
- return AvaloniaPropertyRegistry.Instance.FindRegistered(a, propertyName) != null;
+ return LookupProperty(o, propertyName) != null;
}
return false;
@@ -39,7 +40,7 @@ namespace Avalonia.Markup.Data.Plugins
var instance = reference.Target;
var o = (AvaloniaObject)instance;
- var p = AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName);
+ var p = LookupProperty(o, propertyName);
if (p != null)
{
@@ -57,6 +58,54 @@ namespace Avalonia.Markup.Data.Plugins
}
}
+ private static AvaloniaProperty LookupProperty(AvaloniaObject o, string propertyName)
+ {
+ if (!propertyName.Contains("."))
+ {
+ return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName);
+ }
+ else
+ {
+ var split = propertyName.Split('.');
+
+ if (split.Length == 2)
+ {
+ // HACK: We need a way to resolve types here using something like IXamlTypeResolver.
+ // We don't currently have that so we have to make our best guess.
+ var type = split[0];
+ var name = split[1];
+ var registry = AvaloniaPropertyRegistry.Instance;
+ var registered = registry.GetRegisteredAttached(o.GetType())
+ .Concat(registry.GetRegistered(o.GetType()));
+
+ foreach (var p in registered)
+ {
+ if (p.Name == name && IsOfType(p.OwnerType, type))
+ {
+ return p;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static bool IsOfType(Type type, string typeName)
+ {
+ while (type != null)
+ {
+ if (type.Name == typeName)
+ {
+ return true;
+ }
+
+ type = type.BaseType;
+ }
+
+ return false;
+ }
+
private class Accessor : PropertyAccessorBase
{
private readonly WeakReference _reference;
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs
new file mode 100644
index 0000000000..4e033be3fb
--- /dev/null
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_AddOwner.cs
@@ -0,0 +1,52 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests
+{
+ public class AvaloniaObjectTests_AddOwner
+ {
+ [Fact]
+ public void AddOwnered_Property_Retains_Default_Value()
+ {
+ var target = new Class2();
+
+ Assert.Equal("foodefault", target.GetValue(Class2.FooProperty));
+ }
+
+ [Fact]
+ public void AddOwnered_Property_Does_Not_Retain_Validation()
+ {
+ var target = new Class2();
+
+ target.SetValue(Class2.FooProperty, "throw");
+ }
+
+ private class Class1 : AvaloniaObject
+ {
+ public static readonly StyledProperty FooProperty =
+ AvaloniaProperty.Register(
+ "Foo",
+ "foodefault",
+ validate: ValidateFoo);
+
+ private static string ValidateFoo(AvaloniaObject arg1, string arg2)
+ {
+ if (arg2 == "throw")
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ return arg2;
+ }
+ }
+
+ private class Class2 : AvaloniaObject
+ {
+ public static readonly StyledProperty FooProperty =
+ Class1.FooProperty.AddOwner();
+ }
+ }
+}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
new file mode 100644
index 0000000000..acaabc73df
--- /dev/null
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Attached.cs
@@ -0,0 +1,52 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Xunit;
+
+namespace Avalonia.Base.UnitTests
+{
+ public class AvaloniaObjectTests_Attached
+ {
+ [Fact]
+ public void AddOwnered_Property_Retains_Default_Value()
+ {
+ var target = new Class2();
+
+ Assert.Equal("foodefault", target.GetValue(Class2.FooProperty));
+ }
+
+ [Fact]
+ public void AddOwnered_Property_Retains_Validation()
+ {
+ var target = new Class2();
+
+ Assert.Throws(() => target.SetValue(Class2.FooProperty, "throw"));
+ }
+
+ private class Class1 : AvaloniaObject
+ {
+ public static readonly AttachedProperty FooProperty =
+ AvaloniaProperty.RegisterAttached(
+ "Foo",
+ "foodefault",
+ validate: ValidateFoo);
+
+ private static string ValidateFoo(AvaloniaObject arg1, string arg2)
+ {
+ if (arg2 == "throw")
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ return arg2;
+ }
+ }
+
+ private class Class2 : AvaloniaObject
+ {
+ public static readonly AttachedProperty FooProperty =
+ Class1.FooProperty.AddOwner();
+ }
+ }
+}
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
index da0b0252a3..c030657034 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
@@ -4,12 +4,15 @@
using System.Linq;
using System.Reactive.Linq;
using Xunit;
+using Xunit.Abstractions;
namespace Avalonia.Base.UnitTests
{
public class AvaloniaPropertyRegistryTests
{
- public AvaloniaPropertyRegistryTests()
+ ITestOutputHelper s;
+
+ public AvaloniaPropertyRegistryTests(ITestOutputHelper s)
{
// Ensure properties are registered.
AvaloniaProperty p;
@@ -25,7 +28,7 @@ namespace Avalonia.Base.UnitTests
.Select(x => x.Name)
.ToArray();
- Assert.Equal(new[] { "Foo", "Baz", "Qux", "Attached" }, names);
+ Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names);
}
[Fact]
@@ -35,61 +38,41 @@ namespace Avalonia.Base.UnitTests
.Select(x => x.Name)
.ToArray();
- Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux", "Attached" }, names);
+ Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names);
}
[Fact]
- public void GetAttached_Returns_Registered_Properties_For_Base_Types()
+ public void GetRegisteredAttached_Returns_Registered_Properties()
{
- string[] names = AvaloniaPropertyRegistry.Instance.GetAttached(typeof(AttachedOwner)).Select(x => x.Name).ToArray();
+ string[] names = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(typeof(Class1))
+ .Select(x => x.Name)
+ .ToArray();
Assert.Equal(new[] { "Attached" }, names);
}
[Fact]
- public void FindRegistered_Finds_Untyped_Property()
+ public void GetRegisteredAttached_Returns_Registered_Properties_For_Base_Types()
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Foo");
+ string[] names = AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(typeof(Class2))
+ .Select(x => x.Name)
+ .ToArray();
- Assert.Equal(Class1.FooProperty, result);
+ Assert.Equal(new[] { "Attached" }, names);
}
[Fact]
- public void FindRegistered_Finds_Typed_Property()
+ public void FindRegistered_Finds_Property()
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Class1.Foo");
+ var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Foo");
Assert.Equal(Class1.FooProperty, result);
}
[Fact]
- public void FindRegistered_Finds_Typed_Inherited_Property()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class1.Foo");
-
- Assert.Equal(Class2.FooProperty, result);
- }
-
- [Fact]
- public void FindRegistered_Finds_Inherited_Property_With_Derived_Type_Name()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Class2.Foo");
-
- Assert.Equal(Class2.FooProperty, result);
- }
-
- [Fact]
- public void FindRegistered_Finds_Attached_Property()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "AttachedOwner.Attached");
-
- Assert.Equal(AttachedOwner.AttachedProperty, result);
- }
-
- [Fact]
- public void FindRegistered_Doesnt_Finds_Unqualified_Attached_Property()
+ public void FindRegistered_Doesnt_Find_Nonregistered_Property()
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Attached");
+ var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Bar");
Assert.Null(result);
}
@@ -99,55 +82,34 @@ namespace Avalonia.Base.UnitTests
{
var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(AttachedOwner), "Attached");
- Assert.True(AttachedOwner.AttachedProperty == result);
+ Assert.Same(AttachedOwner.AttachedProperty, result);
}
[Fact]
- public void FindRegistered_Finds_AddOwnered_Untyped_Attached_Property()
+ public void FindRegistered_Finds_AddOwnered_Attached_Property()
{
var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Attached");
- Assert.True(AttachedOwner.AttachedProperty == result);
+ Assert.Same(AttachedOwner.AttachedProperty, result);
}
[Fact]
- public void FindRegistered_Finds_AddOwnered_Typed_Attached_Property()
+ public void FindRegistered_Doesnt_Find_Non_AddOwnered_Attached_Property()
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class3.Attached");
-
- Assert.True(AttachedOwner.AttachedProperty == result);
- }
-
- [Fact]
- public void FindRegistered_Finds_AddOwnered_AttachedTyped_Attached_Property()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "AttachedOwner.Attached");
-
- Assert.True(AttachedOwner.AttachedProperty == result);
- }
-
- [Fact]
- public void FindRegistered_Finds_AddOwnered_BaseTyped_Attached_Property()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class3), "Class1.Attached");
-
- Assert.True(AttachedOwner.AttachedProperty == result);
- }
-
- [Fact]
- public void FindRegistered_Doesnt_Find_Nonregistered_Property()
- {
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class1), "Bar");
+ var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class2), "Attached");
Assert.Null(result);
}
[Fact]
- public void FindRegistered_Doesnt_Find_Nonregistered_Attached_Property()
+ public void FindRegisteredAttached_Finds_Property()
{
- var result = AvaloniaPropertyRegistry.Instance.FindRegistered(typeof(Class4), "AttachedOwner.Attached");
+ var result = AvaloniaPropertyRegistry.Instance.FindRegisteredAttached(
+ typeof(Class1),
+ typeof(AttachedOwner),
+ "Attached");
- Assert.Null(result);
+ Assert.Equal(AttachedOwner.AttachedProperty, result);
}
private class Class1 : AvaloniaObject
@@ -176,18 +138,18 @@ namespace Avalonia.Base.UnitTests
private class Class3 : Class1
{
- public static readonly StyledProperty AttachedProperty =
+ public static readonly AttachedProperty AttachedProperty =
AttachedOwner.AttachedProperty.AddOwner();
}
- public class Class4 : AvaloniaObject
- {
- }
-
private class AttachedOwner : Class1
{
public static readonly AttachedProperty AttachedProperty =
AvaloniaProperty.RegisterAttached("Attached");
}
+
+ private class AttachedOwner2 : AttachedOwner
+ {
+ }
}
}