diff --git a/samples/Directory.Packages.props b/samples/Directory.Packages.props index f9ffa7fe2..ba0413bd0 100644 --- a/samples/Directory.Packages.props +++ b/samples/Directory.Packages.props @@ -23,25 +23,25 @@ - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + diff --git a/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Helpers/ResponsiveExtensionsSamplePage.xaml b/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Helpers/ResponsiveExtensionsSamplePage.xaml index 7e8775f70..b14ac5ba2 100644 --- a/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Helpers/ResponsiveExtensionsSamplePage.xaml +++ b/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Helpers/ResponsiveExtensionsSamplePage.xaml @@ -15,6 +15,15 @@ + + + + + + + + + @@ -22,12 +31,7 @@ - - - - - - + + + + + + + + + + + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 584279618..a8e107bea 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -15,23 +15,23 @@ - - - + + + - - - - - - + + + + + + - + diff --git a/src/Uno.Toolkit.RuntimeTests/Tests/DependencyObjectExtensionTests.cs b/src/Uno.Toolkit.RuntimeTests/Tests/DependencyObjectExtensionTests.cs new file mode 100644 index 000000000..4bc72615c --- /dev/null +++ b/src/Uno.Toolkit.RuntimeTests/Tests/DependencyObjectExtensionTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Uno.Toolkit.UI; +using Uno.UI.RuntimeTests; + +#if IS_WINUI +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +#else +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#endif + +namespace Uno.Toolkit.RuntimeTests.Tests; + +[TestClass] +[RunsOnUIThread] +internal class DependencyObjectExtensionTests +{ + [TestMethod] + public void When_Type_FindDependencyProperty() + { + var dp = typeof(Grid).FindDependencyProperty(nameof(Grid.PaddingProperty)); + + Assert.AreEqual(Grid.PaddingProperty, dp); + } + + [TestMethod] + public void When_Type_FindDependencyProperty_Attached() + { + var dp = typeof(Grid).FindDependencyProperty(nameof(Grid.RowProperty)); + + Assert.AreEqual(Grid.RowProperty, dp); + } + + [TestMethod] + public void When_DO_FindDependencyProperty() + { + var dp = new Grid().FindDependencyProperty(nameof(Grid.PaddingProperty)); + + Assert.AreEqual(Grid.PaddingProperty, dp); + } + + [TestMethod] + public void When_DO_FindDependencyProperty_Attached() + { + var dp = new Grid().FindDependencyProperty(nameof(Grid.RowProperty)); + + Assert.AreEqual(Grid.RowProperty, dp); + } +} diff --git a/src/Uno.Toolkit.Skia.WinUI/Controls/Shadows/ShadowContainer.cs b/src/Uno.Toolkit.Skia.WinUI/Controls/Shadows/ShadowContainer.cs index abf901ba4..ebea428e6 100644 --- a/src/Uno.Toolkit.Skia.WinUI/Controls/Shadows/ShadowContainer.cs +++ b/src/Uno.Toolkit.Skia.WinUI/Controls/Shadows/ShadowContainer.cs @@ -376,7 +376,7 @@ private void InvalidateShadows(bool force = false) StackPanel => StackPanel.CornerRadiusProperty, Shape => null, // note: shapes have special handling, see: GetShadowShapeContext - DependencyObject @do => @do.FindDependencyPropertyUsingReflection("CornerRadiusProperty"), + DependencyObject @do => @do.FindDependencyProperty("CornerRadiusProperty"), _ => null, }; } @@ -392,7 +392,7 @@ private void InvalidateShadows(bool force = false) StackPanel stackpanel => stackpanel.CornerRadius, Shape => null, // note: shapes have special handling, see: GetShadowShapeContext - DependencyObject @do => @do.FindDependencyPropertyUsingReflection("CornerRadiusProperty") is { } dp + DependencyObject @do => @do.FindDependencyProperty("CornerRadiusProperty") is { } dp ? (CornerRadius)@do.GetValue(dp) : null, _ => null, diff --git a/src/Uno.Toolkit.UI/Extensions/DependencyObjectExtensions.cs b/src/Uno.Toolkit.UI/Extensions/DependencyObjectExtensions.cs index 522fd1ebd..b6ef2f0df 100644 --- a/src/Uno.Toolkit.UI/Extensions/DependencyObjectExtensions.cs +++ b/src/Uno.Toolkit.UI/Extensions/DependencyObjectExtensions.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Uno.Disposables; using Uno.Extensions; -using System.Reflection; using Microsoft.Extensions.Logging; using Uno.Logging; @@ -25,7 +25,7 @@ namespace Uno.Toolkit.UI { internal static class DependencyObjectExtensions { - private static Dictionary<(Type type, string property), DependencyProperty?> _dependencyPropertyReflectionCache = new Dictionary<(Type, string), DependencyProperty?>(2); + private static Dictionary<(Type Type, string Property), DependencyProperty?> _dependencyPropertyReflectionCache = new Dictionary<(Type, string), DependencyProperty?>(2); #if HAS_UNO /// @@ -182,65 +182,55 @@ internal static void SetParent(this DependencyObject dependencyObject, object? p } #endif - public static DependencyProperty? FindDependencyPropertyUsingReflection(this DependencyObject dependencyObject, string propertyName) - { - var type = dependencyObject.GetType(); - var propertyType = typeof(TProperty); - var key = (ownerType: type, propertyName); + public static DependencyProperty? FindDependencyProperty(this DependencyObject owner, string propertyName) => owner.GetType().FindDependencyProperty(propertyName); - if (_dependencyPropertyReflectionCache.TryGetValue(key, out var property)) - { - return property; - } + public static DependencyProperty? FindDependencyProperty(this DependencyObject owner, string propertyName) => owner.GetType().FindDependencyProperty(propertyName); - property = - type.GetProperty(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty ?? - type.GetField(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty; + public static DependencyProperty? FindDependencyProperty(this Type ownerOrDescendantType, string propertyName) + { + var propertyType = typeof(TProperty); + var property = FindDependencyProperty(ownerOrDescendantType, propertyName); #if HAS_UNO - if (property == null) - { - typeof(DependencyObjectExtensions).Log().LogWarning($"The '{type}.{propertyName}' dependency property does not exist."); - } - else if (property.GetType() != propertyType) + // note: for winui, it is no possible to obtain the property type from DependencyProperty by reflection. + // we can only check the sibling {propertyName} property or G/Set{propertyName} method (for attached dp) for the property type. + if (property != null && ( + property.GetType().GetProperty("Type", NonPublic | Instance)?.GetValue(property) is not Type type || + type != propertyType + )) { - typeof(DependencyObjectExtensions).Log().LogWarning($"The '{type}.{propertyName}' dependency property is not of the expected '{propertyType}' type."); + typeof(DependencyObjectExtensions).Log().LogWarning($"The '{ownerOrDescendantType.GetType().Name}.{propertyName}' dependency property is not of the expected '{propertyType.Name}' type."); property = null; } #endif - _dependencyPropertyReflectionCache[key] = property; - return property; } - public static DependencyProperty? FindDependencyPropertyUsingReflection(this DependencyObject dependencyObject, string propertyName) + public static DependencyProperty? FindDependencyProperty(this Type ownerOrDescendantType, string propertyName) { - var type = dependencyObject.GetType(); + var type = ownerOrDescendantType; var key = (ownerType: type, propertyName); - if (_dependencyPropertyReflectionCache.TryGetValue(key, out var property)) + // given that we are doing FlattenHierarchy lookup, it is fine that we are storing multiple pairs of (types-to-same-dp) + // since it is not worth the trouble to handle the type hierarchy... + if (!_dependencyPropertyReflectionCache.TryGetValue(key, out var property)) { - return property; + property = + type.GetProperty(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty ?? + type.GetField(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty; + _dependencyPropertyReflectionCache[key] = property; } - property = - type.GetProperty(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty ?? - type.GetField(propertyName, Public | Static | FlattenHierarchy)?.GetValue(null) as DependencyProperty; - -#if HAS_UNO if (property == null) { - typeof(DependencyObjectExtensions).Log().LogWarning($"The '{type}.{propertyName}' dependency property does not exist."); + typeof(DependencyObjectExtensions).Log().LogWarning($"The dependency property '{propertyName}' does not exist on '{type}' or its ancestors."); } -#endif - - _dependencyPropertyReflectionCache[key] = property; return property; } - public static bool TryGetValue(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, out DependencyObject? value) + private static bool TryGetValue(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, out DependencyObject? value) { value = default; diff --git a/src/Uno.Toolkit.UI/Helpers/PaddingHelper.cs b/src/Uno.Toolkit.UI/Helpers/PaddingHelper.cs index fbe76aab1..4cbab847d 100644 --- a/src/Uno.Toolkit.UI/Helpers/PaddingHelper.cs +++ b/src/Uno.Toolkit.UI/Helpers/PaddingHelper.cs @@ -23,7 +23,7 @@ public static Thickness GetPadding(UIElement uiElement) return padding; } - var property = uiElement.FindDependencyPropertyUsingReflection("PaddingProperty"); + var property = uiElement.FindDependencyProperty("PaddingProperty"); return property != null && uiElement.GetValue(property) is Thickness t ? t : default; } @@ -35,7 +35,7 @@ public static bool SetPadding(UIElement uiElement, Thickness padding) return true; } - var property = uiElement.FindDependencyPropertyUsingReflection("PaddingProperty"); + var property = uiElement.FindDependencyProperty("PaddingProperty"); if (property != null) { uiElement.SetValue(property, padding); @@ -53,7 +53,7 @@ public static bool TryUpdatePadding(this FrameworkElement frameworkElement, Thic return !padding.Equals(newPadding) && TrySetPadding(frameworkElement, newPadding); } - var property = frameworkElement.FindDependencyPropertyUsingReflection("PaddingProperty"); + var property = frameworkElement.FindDependencyProperty("PaddingProperty"); if (property is { } && frameworkElement.GetValue(property) is Thickness currentPadding) { if (!currentPadding.Equals(newPadding)) diff --git a/src/Uno.Toolkit.UI/Markup/ResponsiveExtension.cs b/src/Uno.Toolkit.UI/Markup/ResponsiveExtension.cs index 7fd09330a..c2228cee9 100644 --- a/src/Uno.Toolkit.UI/Markup/ResponsiveExtension.cs +++ b/src/Uno.Toolkit.UI/Markup/ResponsiveExtension.cs @@ -4,14 +4,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; using Windows.Foundation; using Uno.Extensions; using Uno.Logging; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - - #if IS_WINUI using Microsoft.UI.Xaml; @@ -85,21 +82,16 @@ private void BindToEvents(IXamlServiceProvider serviceProvider) if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget pvt && pvt.TargetObject is FrameworkElement target && pvt.TargetProperty is ProvideValueTargetProperty pvtp && - target.FindDependencyPropertyUsingReflection($"{pvtp.Name}Property") is DependencyProperty dp) + pvtp.DeclaringType.FindDependencyProperty($"{pvtp.Name}Property") is DependencyProperty dp) { TargetWeakRef = new WeakReference(target); _targetProperty = dp; - _propertyType = -#if HAS_UNO // workaround for uno#14719: uno doesn't inject the proper pvtp.Type - typeof(DependencyProperty).GetProperty("Type", Instance | NonPublic)?.GetValue(dp) as Type; -#else - pvtp.Type; -#endif + _propertyType = pvtp.Type; + // here, we need to bind to two events: // 1. Window.SizeChanged for obvious reason // 2. Control.Loaded because the initial value(result of ProvideValue) is resolved without the inherited .resources // which may define a different DefaultResponsiveLayout resource somewhere along the visual tree, so we need to rectify that. - ResponsiveHelper.GetForCurrentView().Register(this); target.Loaded += OnTargetLoaded; @@ -158,7 +150,7 @@ private void UpdateBindingIfNeeded(ResponsiveHelper? helper = null, bool forceAp var helper = ResponsiveHelper.GetForCurrentView(); var resolved = helper.ResolveLayout(GetAppliedLayout(), GetAvailableLayoutOptions()); var value = GetValueFor(resolved.Result); - + CurrentValue = value; CurrentLayout = resolved.Result; LastResolved = resolved; diff --git a/src/Uno.Toolkit.UITest/Controls/ShadowContainer/Given_ShadowContainer.cs b/src/Uno.Toolkit.UITest/Controls/ShadowContainer/Given_ShadowContainer.cs index 638af9e44..34cb655f2 100644 --- a/src/Uno.Toolkit.UITest/Controls/ShadowContainer/Given_ShadowContainer.cs +++ b/src/Uno.Toolkit.UITest/Controls/ShadowContainer/Given_ShadowContainer.cs @@ -22,7 +22,6 @@ public class Given_ShadowContainer : TestBase [Test] [TestCase(10, 10, false)] - [TestCase(10, 10, false)] [TestCase(10, 10, true)] [TestCase(-10, -10, false)] [TestCase(-10, -10, true)] @@ -30,6 +29,8 @@ public class Given_ShadowContainer : TestBase [TestCase(10, -10, true)] [TestCase(-10, 10, false)] [TestCase(10, -10, false)] + //TODO: Re-enable when Android no longer crashes: https://github.com/unoplatform/uno.toolkit.ui/issues/982 + [ActivePlatforms(Platform.Browser, Platform.iOS)] public void When_Shadows(int xOffset, int yOffset, bool inner) { @@ -64,7 +65,6 @@ public void When_Shadows(int xOffset, int yOffset, bool inner) [Test] [TestCase(10, 10, false)] - [TestCase(10, 10, false)] [TestCase(10, 10, true)] [TestCase(-10, -10, false)] [TestCase(-10, -10, true)] @@ -72,6 +72,8 @@ public void When_Shadows(int xOffset, int yOffset, bool inner) [TestCase(10, -10, true)] [TestCase(-10, 10, false)] [TestCase(10, -10, false)] + //TODO: Re-enable when Android no longer crashes: https://github.com/unoplatform/uno.toolkit.ui/issues/982 + [ActivePlatforms(Platform.Browser, Platform.iOS)] public void When_RectangleShadows(int xOffset, int yOffset, bool inner) { @@ -106,6 +108,8 @@ public void When_RectangleShadows(int xOffset, int yOffset, bool inner) [Test] [TestCase(10, 10, false)] [TestCase(-10, -10, false)] + //TODO: Re-enable when Android no longer crashes: https://github.com/unoplatform/uno.toolkit.ui/issues/982 + [ActivePlatforms(Platform.Browser, Platform.iOS)] public void When_AsymmetricShadowsCorner(int xOffset, int yOffset, bool inner) { var shadowContainer = App.WaitForElementWithMessage("shadowContainer");