Browse Source

Merge pull request #11341 from AvaloniaUI/fixes/11212-attached-property-coercion

Register attached property metadata on TOwner instead of THost.
pull/11387/head
Max Katz 3 years ago
committed by GitHub
parent
commit
ff54192a05
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/Avalonia.Base/AttachedProperty.cs
  2. 12
      src/Avalonia.Base/AvaloniaProperty.cs
  3. 4
      src/Avalonia.Base/AvaloniaProperty`1.cs
  4. 2
      src/Avalonia.Base/DirectPropertyBase.cs
  5. 4
      src/Avalonia.Base/StyledProperty.cs
  6. 2
      tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs
  7. 17
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs
  8. 1
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
  9. 2
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs
  10. 2
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
  11. 4
      tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs
  12. 1
      tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs
  13. 2
      tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs

4
src/Avalonia.Base/AttachedProperty.cs

@ -13,16 +13,18 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The class that is registering the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">A value validation callback.</param>
public AttachedProperty(
string name,
Type ownerType,
Type hostType,
StyledPropertyMetadata<TValue> metadata,
bool inherits = false,
Func<TValue, bool>? validate = null)
: base(name, ownerType, metadata, inherits, validate)
: base(name, ownerType, hostType, metadata, inherits, validate)
{
IsAttached = true;
}

12
src/Avalonia.Base/AvaloniaProperty.cs

@ -39,12 +39,14 @@ namespace Avalonia
/// <param name="name">The name of the property.</param>
/// <param name="valueType">The type of the property's value.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="notifying">A <see cref="Notifying"/> callback.</param>
protected AvaloniaProperty(
string name,
Type valueType,
Type ownerType,
Type hostType,
AvaloniaPropertyMetadata metadata,
Action<AvaloniaObject, bool>? notifying = null)
{
@ -63,9 +65,9 @@ namespace Avalonia
Notifying = notifying;
Id = s_nextId++;
_metadata.Add(ownerType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
_metadata.Add(hostType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
_defaultMetadata = metadata.GenerateTypeSafeMetadata();
_singleMetadata = new(ownerType, metadata);
_singleMetadata = new(hostType, metadata);
}
/// <summary>
@ -255,6 +257,7 @@ namespace Avalonia
var result = new StyledProperty<TValue>(
name,
typeof(TOwner),
typeof(TOwner),
metadata,
inherits,
validate);
@ -301,6 +304,7 @@ namespace Avalonia
var result = new StyledProperty<TValue>(
name,
typeof(TOwner),
typeof(TOwner),
metadata,
inherits,
validate,
@ -338,7 +342,7 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode,
coerce: coerce);
var result = new AttachedProperty<TValue>(name, typeof(TOwner), metadata, inherits, validate);
var result = new AttachedProperty<TValue>(name, typeof(TOwner), typeof(THost), metadata, inherits, validate);
var registry = AvaloniaPropertyRegistry.Instance;
registry.Register(typeof(TOwner), result);
registry.RegisterAttached(typeof(THost), result);
@ -375,7 +379,7 @@ namespace Avalonia
defaultBindingMode: defaultBindingMode,
coerce: coerce);
var result = new AttachedProperty<TValue>(name, ownerType, metadata, inherits, validate);
var result = new AttachedProperty<TValue>(name, ownerType, typeof(THost), metadata, inherits, validate);
var registry = AvaloniaPropertyRegistry.Instance;
registry.Register(ownerType, result);
registry.RegisterAttached(typeof(THost), result);

4
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -19,14 +19,16 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
protected AvaloniaProperty(
string name,
Type ownerType,
Type hostType,
AvaloniaPropertyMetadata metadata,
Action<AvaloniaObject, bool>? notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying)
: base(name, typeof(TValue), ownerType, hostType, metadata, notifying)
{
_changed = new LightweightSubject<AvaloniaPropertyChangedEventArgs<TValue>>();
}

2
src/Avalonia.Base/DirectPropertyBase.cs

@ -24,7 +24,7 @@ namespace Avalonia
string name,
Type ownerType,
AvaloniaPropertyMetadata metadata)
: base(name, ownerType, metadata)
: base(name, ownerType, ownerType, metadata)
{
Owner = ownerType;
}

4
src/Avalonia.Base/StyledProperty.cs

@ -16,6 +16,7 @@ namespace Avalonia
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="hostType">The class that the property being is registered on.</param>
/// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">
@ -26,11 +27,12 @@ namespace Avalonia
public StyledProperty(
string name,
Type ownerType,
Type hostType,
StyledPropertyMetadata<TValue> metadata,
bool inherits = false,
Func<TValue, bool>? validate = null,
Action<AvaloniaObject, bool>? notifying = null)
: base(name, ownerType, metadata, notifying)
: base(name, ownerType, hostType, metadata, notifying)
{
Inherits = inherits;
ValidateValue = validate;

2
tests/Avalonia.Base.UnitTests/AttachedPropertyTests.cs

@ -1,3 +1,4 @@
using Avalonia.Controls;
using Xunit;
namespace Avalonia.Base.UnitTests
@ -10,6 +11,7 @@ namespace Avalonia.Base.UnitTests
var property = new AttachedProperty<string>(
"Foo",
typeof(Class1),
typeof(Control),
new StyledPropertyMetadata<string>());
Assert.True(property.IsAttached);

17
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs

@ -31,6 +31,16 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(100, target.GetValue(Class1.AttachedProperty));
}
[Fact]
public void Coerces_Set_Value_Attached_On_Class_Not_Derived_From_Owner()
{
var target = new Class2();
target.SetValue(Class1.AttachedProperty, 150);
Assert.Equal(100, target.GetValue(Class1.AttachedProperty));
}
[Fact]
public void Coerces_Bound_Value()
{
@ -301,7 +311,7 @@ namespace Avalonia.Base.UnitTests
coerce: CoerceFoo);
public static readonly AttachedProperty<int> AttachedProperty =
AvaloniaProperty.RegisterAttached<Class1, Class1, int>(
AvaloniaProperty.RegisterAttached<Class1, AvaloniaObject, int>(
"Attached",
defaultValue: 11,
coerce: CoerceFoo);
@ -332,8 +342,9 @@ namespace Avalonia.Base.UnitTests
public static int CoerceFoo(AvaloniaObject instance, int value)
{
var o = (Class1)instance;
return Math.Clamp(value, o.MinFoo, o.MaxFoo);
return instance is Class1 o ?
Math.Clamp(value, o.MinFoo, o.MaxFoo) :
Math.Clamp(value, 0, 100);
}
protected override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)

1
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs

@ -15,6 +15,7 @@ namespace Avalonia.Base.UnitTests
new StyledProperty<int>(
"BadDefault",
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<int>(101),
validate: Class1.ValidateFoo));
}

2
tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs

@ -22,7 +22,7 @@ namespace Avalonia.Base.UnitTests
{
var registry = new AvaloniaPropertyRegistry();
var metadata = new StyledPropertyMetadata<int>();
var property = new AttachedProperty<int>("test", typeof(object), metadata, true);
var property = new AttachedProperty<int>("test", typeof(object), typeof(object), metadata, true);
registry.Register(typeof(object), property);
registry.RegisterAttached(typeof(AvaloniaPropertyRegistryTests), property);
property.AddOwner<Class4>();

2
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -157,7 +157,7 @@ namespace Avalonia.Base.UnitTests
private class TestProperty<TValue> : AvaloniaProperty<TValue>
{
public TestProperty(string name, Type ownerType, TestMetadata metadata = null)
: base(name, ownerType, metadata ?? new TestMetadata())
: base(name, ownerType, ownerType, metadata ?? new TestMetadata())
{
}

4
tests/Avalonia.Base.UnitTests/StyledPropertyTests.cs

@ -9,7 +9,8 @@ namespace Avalonia.Base.UnitTests
{
var p1 = new StyledProperty<string>(
"p1",
typeof(Class1),
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<string>());
var p2 = p1.AddOwner<Class2>();
@ -24,6 +25,7 @@ namespace Avalonia.Base.UnitTests
var p1 = new StyledProperty<string>(
"p1",
typeof(Class1),
typeof(Class1),
new StyledPropertyMetadata<string>());
var p2 = p1.AddOwner<Class2>();

1
tests/Avalonia.Base.UnitTests/Utilities/AvaloniaPropertyDictionaryTests.cs

@ -18,6 +18,7 @@ namespace Avalonia.Base.UnitTests.Utilities
TestProperties[i] = new StyledProperty<string>(
$"Test{i}",
typeof(AvaloniaPropertyDictionaryTests),
typeof(AvaloniaPropertyDictionaryTests),
new StyledPropertyMetadata<string>());
}

2
tests/Avalonia.Benchmarks/Utilities/AvaloniaPropertyDictionaryBenchmarks.cs

@ -172,7 +172,7 @@ internal sealed class AvaloniaPropertyValueStoreOld<TValue>
internal class MockProperty : StyledProperty<int>
{
public MockProperty(string name) : base(name, typeof(object), new StyledPropertyMetadata<int>())
public MockProperty(string name) : base(name, typeof(object), typeof(object), new StyledPropertyMetadata<int>())
{
}
}

Loading…
Cancel
Save