Skip to content

Commit

Permalink
Merge pull request #6158 from MartinZikmund/dev/mazi/defaultvalueprov…
Browse files Browse the repository at this point in the history
…ider

fix: Allow multiple default value providers
  • Loading branch information
mergify[bot] authored Jun 16, 2021
2 parents 18f69f0 + d34e070 commit 646ae14
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.UI.Tests.BinderTests;
using Windows.UI.Xaml;

namespace Uno.UI.Tests.DependencyPropertyTests
{
[TestClass]
public partial class Given_DependencyProperty
{
[TestMethod]
public void When_DefaultValueProvider_Registered_Provides_Value()
{
bool GetDefaultValue(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.TestProperty)
{
value = 3;
return true;
}

value = null;
return false;
}

var test = new DefaultValueProviderSample();
test.RegisterDefaultValueProvider(GetDefaultValue);

var value = test.GetPrecedenceSpecificValue(DefaultValueProviderSample.TestProperty, DependencyPropertyValuePrecedences.DefaultValue);
Assert.AreEqual(3, value);
}

[TestMethod]
public void When_DefaultValueProvider_Registered_Other_Property_Not_Affected()
{
bool GetDefaultValue(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.TestProperty)
{
value = 3;
return true;
}

value = 42; // Set arbitrary to verify false has effect
return false;
}

var test = new DefaultValueProviderSample();
test.RegisterDefaultValueProvider(GetDefaultValue);

var value = test.GetPrecedenceSpecificValue(DefaultValueProviderSample.OtherProperty, DependencyPropertyValuePrecedences.DefaultValue);
Assert.AreEqual(0, value);
}

[TestMethod]
public void When_DefaultValueProvider_Multiple_Registered_Overwrite()
{
bool GetDefaultValue(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.TestProperty)
{
value = 3;
return true;
}

value = null;
return false;
}

bool GetDefaultValue2(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.TestProperty)
{
value = 17;
return true;
}

value = null;
return false;
}

var test = new DefaultValueProviderSample();
test.RegisterDefaultValueProvider(GetDefaultValue);
test.RegisterDefaultValueProvider(GetDefaultValue2);

var value = test.GetPrecedenceSpecificValue(DefaultValueProviderSample.TestProperty, DependencyPropertyValuePrecedences.DefaultValue);
Assert.AreEqual(17, value);
}

[TestMethod]
public void When_DefaultValueProvider_Multiple_Registered_Unaffected()
{
bool GetDefaultValue(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.TestProperty)
{
value = 3;
return true;
}

value = null;
return false;
}

bool GetDefaultValue2(DependencyProperty property, out object value)
{
if (property == DefaultValueProviderSample.OtherProperty)
{
value = 17;
return true;
}

value = 42;
return false;
}

var test = new DefaultValueProviderSample();
test.RegisterDefaultValueProvider(GetDefaultValue);
test.RegisterDefaultValueProvider(GetDefaultValue2);

var value = test.GetPrecedenceSpecificValue(DefaultValueProviderSample.TestProperty, DependencyPropertyValuePrecedences.DefaultValue);
Assert.AreEqual(3, value); // GetDefaultValue should apply
}
}

internal partial class DefaultValueProviderSample : MockDependencyObject
{
public DefaultValueProviderSample()
{

}

public int Test
{
get => (int)GetValue(TestProperty);
set => SetValue(TestProperty, value);
}

public static DependencyProperty TestProperty { get; } =
DependencyProperty.Register(nameof(Test), typeof(int), typeof(DefaultValueProviderSample), new PropertyMetadata(0));

public int Other
{
get => (int)GetValue(OtherProperty);
set => SetValue(OtherProperty, value);
}

public static readonly DependencyProperty OtherProperty =
DependencyProperty.Register(nameof(Other), typeof(int), typeof(DefaultValueProviderSample), new PropertyMetadata(0));
}
}
2 changes: 1 addition & 1 deletion src/Uno.UI/UI/Xaml/DependencyPropertyDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ private bool SetValueFast(object? value, DependencyPropertyValuePrecedences prec
return false;
}

internal void SetDefaultValue(object defaultValue)
internal void SetDefaultValue(object? defaultValue)
{
_defaultValue = defaultValue;
_flags |= Flags.DefaultValueSet;
Expand Down
46 changes: 40 additions & 6 deletions src/Uno.UI/UI/Xaml/DependencyPropertyDetailsCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ partial class DependencyPropertyDetailsCollection : IDisposable
private int _entriesLength;
private int _minId;
private int _maxId;
private DependencyObjectStore.DefaultValueProvider? _defaultValueProvider;
private List<DependencyObjectStore.DefaultValueProvider>? _defaultValueProviders = null;

private object? Owner => _hardOwnerReference ?? _ownerReference.Target;

Expand Down Expand Up @@ -141,9 +141,9 @@ public DependencyPropertyDetails FindPropertyDetails(DependencyProperty property
{
propertyEntry = new DependencyPropertyDetails(property, _ownerType);

if(_defaultValueProvider != null && _defaultValueProvider(property, out var v))
if (TryResolveDefaultValueFromProviders(property, out var value))
{
propertyEntry.SetDefaultValue(v);
propertyEntry.SetDefaultValue(value);
}
}

Expand Down Expand Up @@ -178,9 +178,9 @@ public DependencyPropertyDetails FindPropertyDetails(DependencyProperty property

ref var propertyEntry = ref Entries![property.UniqueId - _minId];
propertyEntry = new DependencyPropertyDetails(property, _ownerType);
if (_defaultValueProvider != null && _defaultValueProvider(property, out var v))
if (TryResolveDefaultValueFromProviders(property, out var value))
{
propertyEntry.SetValue(v, DependencyPropertyValuePrecedences.DefaultValue);
propertyEntry.SetValue(value, DependencyPropertyValuePrecedences.DefaultValue);
}

return propertyEntry;
Expand All @@ -192,6 +192,24 @@ public DependencyPropertyDetails FindPropertyDetails(DependencyProperty property
}
}

private bool TryResolveDefaultValueFromProviders(DependencyProperty property, out object? value)
{
if (_defaultValueProviders != null)
{
for (int i = _defaultValueProviders.Count - 1; i >= 0; i--)
{
var provider = _defaultValueProviders[i];
if (provider.Invoke(property, out var resolvedValue))
{
value = resolvedValue;
return true;
}
}
}
value = null;
return false;
}

private void AssignEntries(DependencyPropertyDetails[] newEntries, int newSize)
{
ReturnEntriesToPool();
Expand All @@ -214,9 +232,25 @@ private void ReturnEntriesToPool()

internal IEnumerable<DependencyPropertyDetails> GetAllDetails() => Entries.Trim();

/// <summary>
/// Adds a default value provider.
/// </summary>
/// <param name="provider">Default value provider.</param>
/// <remarks>
/// Providers which are registered later have higher priority.
/// E.g. when both a derived and base class register their own default
/// value provider in the constructor for the same property, the derived
/// class value is used.
/// </remarks>
public void RegisterDefaultValueProvider(DependencyObjectStore.DefaultValueProvider provider)
{
_defaultValueProvider = provider;
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}

_defaultValueProviders ??= new List<DependencyObjectStore.DefaultValueProvider>(2);
_defaultValueProviders.Add(provider);
}

internal void TryEnableHardReferences()
Expand Down

0 comments on commit 646ae14

Please sign in to comment.