diff --git a/src/Compatibility/Core/src/Android/AppCompat/FormsAppCompatActivity.cs b/src/Compatibility/Core/src/Android/AppCompat/FormsAppCompatActivity.cs index 030490cae7a5..860e452ac261 100644 --- a/src/Compatibility/Core/src/Android/AppCompat/FormsAppCompatActivity.cs +++ b/src/Compatibility/Core/src/Android/AppCompat/FormsAppCompatActivity.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Android.App; @@ -104,7 +105,7 @@ public void SetStatusBarColor(AColor color) Window.SetStatusBarColor(color); } - static void RegisterHandler(Type target, Type handler, Type filter) + static void RegisterHandler(Type target, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type handler, Type filter) { Profile.FrameBegin(); @@ -459,7 +460,7 @@ void OnStateChanged() } // This is currently being used by the previewer please do not change or remove this - void RegisterHandlerForDefaultRenderer(Type target, Type handler, Type filter) + void RegisterHandlerForDefaultRenderer(Type target, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type handler, Type filter) { RegisterHandler(target, handler, filter); } diff --git a/src/Compatibility/Core/src/Android/Extensions/NativeBindingExtensions.cs b/src/Compatibility/Core/src/Android/Extensions/NativeBindingExtensions.cs index 1cce4eff3e42..54dca7b49c2f 100644 --- a/src/Compatibility/Core/src/Android/Extensions/NativeBindingExtensions.cs +++ b/src/Compatibility/Core/src/Android/Extensions/NativeBindingExtensions.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Internals; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { public static class NativeBindingExtensions { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this global::Android.Views.View view, string propertyName, BindingBase binding, string updateSourceEventName = null) { PlatformBindingHelpers.SetBinding(view, propertyName, binding, updateSourceEventName); diff --git a/src/Compatibility/Core/src/Android/NativeBindingservice.cs b/src/Compatibility/Core/src/Android/NativeBindingservice.cs index 5b7b9fad8b10..5fa34009a928 100644 --- a/src/Compatibility/Core/src/Android/NativeBindingservice.cs +++ b/src/Compatibility/Core/src/Android/NativeBindingservice.cs @@ -9,7 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { class NativeBindingService : INativeBindingService { - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as AView; diff --git a/src/Compatibility/Core/src/Android/Renderers/ListViewAdapter.cs b/src/Compatibility/Core/src/Android/Renderers/ListViewAdapter.cs index 738b7da01ee7..844caa2f622d 100644 --- a/src/Compatibility/Core/src/Android/Renderers/ListViewAdapter.cs +++ b/src/Compatibility/Core/src/Android/Renderers/ListViewAdapter.cs @@ -736,7 +736,9 @@ Cell GetNewGroupHeaderCell(ITemplatedItemsList group) else { groupHeaderCell = new TextCell(); - groupHeaderCell.SetBinding(TextCell.TextProperty, nameof(group.Name)); + groupHeaderCell.SetBinding( + TextCell.TextProperty, + TypedBinding.ForSingleNestingLevel(nameof(group.Name), static (ITemplatedItemsList g) => g.Name)); groupHeaderCell.BindingContext = group; } diff --git a/src/Compatibility/Core/src/ExportRendererAttribute.cs b/src/Compatibility/Core/src/ExportRendererAttribute.cs index 212130be8af0..acb5f89d160d 100644 --- a/src/Compatibility/Core/src/ExportRendererAttribute.cs +++ b/src/Compatibility/Core/src/ExportRendererAttribute.cs @@ -1,15 +1,16 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui.Controls.Compatibility { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class ExportRendererAttribute : HandlerAttribute { - public ExportRendererAttribute(Type handler, Type target) : this(handler, target, null) + public ExportRendererAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : this(handler, target, null) { } - public ExportRendererAttribute(Type handler, Type target, Type[] supportedVisuals) : base(handler, target, supportedVisuals) + public ExportRendererAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target, Type[] supportedVisuals) : base(handler, target, supportedVisuals) { } } @@ -17,7 +18,7 @@ public ExportRendererAttribute(Type handler, Type target, Type[] supportedVisual [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class ExportCellAttribute : HandlerAttribute { - public ExportCellAttribute(Type handler, Type target) : base(handler, target) + public ExportCellAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : base(handler, target) { } } @@ -25,7 +26,7 @@ public ExportCellAttribute(Type handler, Type target) : base(handler, target) [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute { - public ExportImageSourceHandlerAttribute(Type handler, Type target) : base(handler, target) + public ExportImageSourceHandlerAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : base(handler, target) { } } diff --git a/src/Compatibility/Core/src/MauiHandlersCollectionExtensions.cs b/src/Compatibility/Core/src/MauiHandlersCollectionExtensions.cs index df004cad2fc7..507321e6c571 100644 --- a/src/Compatibility/Core/src/MauiHandlersCollectionExtensions.cs +++ b/src/Compatibility/Core/src/MauiHandlersCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Hosting; using Microsoft.Maui.Hosting; @@ -6,7 +7,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Hosting { public static class MauiHandlersCollectionExtensions { - public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, Type rendererType) + public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type rendererType) { Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(rendererType); Hosting.MauiAppBuilderExtensions.CheckForCompatibility(); @@ -21,7 +22,7 @@ public static IMauiHandlersCollection TryAddCompatibilityRenderer(this IMauiHand return handlersCollection; } - public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, Type rendererType) + public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection, Type controlType, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type rendererType) { Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(rendererType); Hosting.MauiAppBuilderExtensions.CheckForCompatibility(); @@ -36,7 +37,7 @@ public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandler return handlersCollection; } - public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection) + public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection) where TMauiType : IView { Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(typeof(TRenderer)); @@ -51,7 +52,7 @@ public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection) + public static IMauiHandlersCollection AddCompatibilityRenderer(this IMauiHandlersCollection handlersCollection) where TControlType : IView { Internals.Registrar.CheckIfRendererIsCompatibilityRenderer(typeof(TRenderer)); diff --git a/src/Compatibility/Core/src/Tizen/Extensions/NativeBindingExtensions.cs b/src/Compatibility/Core/src/Tizen/Extensions/NativeBindingExtensions.cs index 5cbb35f62b12..0286d6911cf6 100644 --- a/src/Compatibility/Core/src/Tizen/Extensions/NativeBindingExtensions.cs +++ b/src/Compatibility/Core/src/Tizen/Extensions/NativeBindingExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Internals; using NView = Tizen.NUI.BaseComponents.View; @@ -7,6 +8,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Tizen { public static class NativeBindingExtensions { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this NView view, string propertyName, BindingBase binding, string updateSourceEventName = null) { PlatformBindingHelpers.SetBinding(view, propertyName, binding, updateSourceEventName); diff --git a/src/Compatibility/Core/src/Tizen/NativeBindingService.cs b/src/Compatibility/Core/src/Tizen/NativeBindingService.cs index d575dbf30615..e377576f703e 100644 --- a/src/Compatibility/Core/src/Tizen/NativeBindingService.cs +++ b/src/Compatibility/Core/src/Tizen/NativeBindingService.cs @@ -7,7 +7,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Tizen { class NativeBindingService : INativeBindingService { - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { Hosting.MauiAppBuilderExtensions.CheckForCompatibility(); diff --git a/src/Compatibility/Core/src/Windows/NativeBindingExtensions.cs b/src/Compatibility/Core/src/Windows/NativeBindingExtensions.cs index 00de36ce7264..938df2a83e75 100644 --- a/src/Compatibility/Core/src/Windows/NativeBindingExtensions.cs +++ b/src/Compatibility/Core/src/Windows/NativeBindingExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using Microsoft.Maui.Controls.Internals; using Microsoft.UI.Xaml; using static System.String; @@ -9,6 +10,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP { public static class NativeBindingExtensions { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this FrameworkElement view, string propertyName, BindingBase bindingBase, string updateSourceEventName = null) { var binding = bindingBase as Binding; diff --git a/src/Compatibility/Core/src/Windows/NativeBindingService.cs b/src/Compatibility/Core/src/Windows/NativeBindingService.cs index 794e91db2e33..53cf9aa35b27 100644 --- a/src/Compatibility/Core/src/Windows/NativeBindingService.cs +++ b/src/Compatibility/Core/src/Windows/NativeBindingService.cs @@ -9,7 +9,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP { public class NativeBindingService : INativeBindingService { - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { var view = target as FrameworkElement; diff --git a/src/Compatibility/Core/src/iOS/NativeBindingService.cs b/src/Compatibility/Core/src/iOS/NativeBindingService.cs index 0fd914b78845..933bff77e3d4 100644 --- a/src/Compatibility/Core/src/iOS/NativeBindingService.cs +++ b/src/Compatibility/Core/src/iOS/NativeBindingService.cs @@ -11,7 +11,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS [Preserve(AllMembers = true)] class NativeBindingService : INativeBindingService { - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = TrimmerConstants.NativeBindingService)] + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public bool TrySetBinding(object target, string propertyName, BindingBase binding) { Hosting.MauiAppBuilderExtensions.CheckForCompatibility(); diff --git a/src/Controls/src/Core/AppThemeBinding.cs b/src/Controls/src/Core/AppThemeBinding.cs index 460bc7ea84a0..f6120590559b 100644 --- a/src/Controls/src/Core/AppThemeBinding.cs +++ b/src/Controls/src/Core/AppThemeBinding.cs @@ -107,7 +107,7 @@ void Set() target.SetDynamicResource(_targetProperty, dynamicResource.Key, specificity); else { - if (!BindingExpression.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) + if (!BindingExpressionHelper.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(this, null, target, _targetProperty, "AppThemeBinding", BindingExpression.CannotConvertTypeErrorMessage, value, _targetProperty.ReturnType); return; diff --git a/src/Controls/src/Core/BindableObjectExtensions.cs b/src/Controls/src/Core/BindableObjectExtensions.cs index 6e2ee6605aec..e0545128f0b8 100644 --- a/src/Controls/src/Core/BindableObjectExtensions.cs +++ b/src/Controls/src/Core/BindableObjectExtensions.cs @@ -1,6 +1,7 @@ #nullable disable using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Maui.Graphics; @@ -48,6 +49,7 @@ internal static void PropagateBindingContext(this BindableObject self, IEnume } /// + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this BindableObject self, BindableProperty targetProperty, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null, string stringFormat = null) { diff --git a/src/Controls/src/Core/Binding.cs b/src/Controls/src/Core/Binding.cs index 0459f0bed444..d3c0e1c2e833 100644 --- a/src/Controls/src/Core/Binding.cs +++ b/src/Controls/src/Core/Binding.cs @@ -1,14 +1,13 @@ #nullable disable using System; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Reflection; namespace Microsoft.Maui.Controls { /// + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public sealed class Binding : BindingBase { public const string SelfPath = "."; @@ -92,7 +91,7 @@ public object Source } /// - public static readonly object DoNothing = new object(); + public static readonly object DoNothing = MultiBinding.DoNothing; // the instance was moved to MultiBinding because the Binding class is annotated with [RequiresUnreferencedCode] /// [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Controls/src/Core/BindingExpression.cs b/src/Controls/src/Core/BindingExpression.cs index 3df39157b791..5a061f567f57 100644 --- a/src/Controls/src/Core/BindingExpression.cs +++ b/src/Controls/src/Core/BindingExpression.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; @@ -12,6 +13,7 @@ namespace Microsoft.Maui.Controls { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] internal sealed class BindingExpression { internal const string PropertyNotFoundErrorMessage = "'{0}' property not found on '{1}', target property: '{2}.{3}'"; @@ -154,7 +156,7 @@ void ApplyCore(object sourceObject, BindableObject target, BindableProperty prop else value = Binding.FallbackValue ?? property.GetDefaultValue(target); - if (!TryConvert(ref value, property, property.ReturnType, true)) + if (!BindingExpressionHelper.TryConvert(ref value, property, property.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, property.ReturnType); return; @@ -166,7 +168,7 @@ void ApplyCore(object sourceObject, BindableObject target, BindableProperty prop { object value = Binding.GetTargetValue(target.GetValue(property), part.SetterType); - if (!TryConvert(ref value, property, part.SetterType, false)) + if (!BindingExpressionHelper.TryConvert(ref value, property, part.SetterType, false)) { BindingDiagnostics.SendBindingFailure(Binding, current, target, property, "Binding", CannotConvertTypeErrorMessage, value, part.SetterType); return; @@ -428,54 +430,6 @@ void SetupPart(TypeInfo sourceType, BindingExpressionPart part) } } - static readonly Type[] DecimalTypes = { typeof(float), typeof(decimal), typeof(double) }; - - internal static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget) - { - if (value == null) - return !convertTo.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(convertTo) != null; - try - { - if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value))) - return true; - } - catch (InvalidOperationException) - { //that's what TypeConverters ususally throw - return false; - } - - object original = value; - try - { - convertTo = Nullable.GetUnderlyingType(convertTo) ?? convertTo; - - var stringValue = value as string ?? string.Empty; - // see: https://bugzilla.xamarin.com/show_bug.cgi?id=32871 - // do not canonicalize "*.[.]"; "1." should not update bound BindableProperty - if (stringValue.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal) && DecimalTypes.Contains(convertTo)) - { - value = original; - return false; - } - - // do not canonicalize "-0"; user will likely enter a period after "-0" - if (stringValue == "-0" && DecimalTypes.Contains(convertTo)) - { - value = original; - return false; - } - - value = Convert.ChangeType(value, convertTo, CultureInfo.CurrentCulture); - - return true; - } - catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is InvalidOperationException || ex is OverflowException) - { - value = original; - return false; - } - } - // SubscribeToAncestryChanges, ClearAncestryChangeSubscriptions, FindAncestryIndex, and // OnElementParentSet are used with RelativeSource ancestor-type bindings, to detect when // there has been an ancestry change requiring re-applying the binding, and to minimize diff --git a/src/Controls/src/Core/BindingExpressionHelper.cs b/src/Controls/src/Core/BindingExpressionHelper.cs new file mode 100644 index 000000000000..03b510b6877e --- /dev/null +++ b/src/Controls/src/Core/BindingExpressionHelper.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Maui.Controls +{ + internal static class BindingExpressionHelper + { + static readonly Type[] DecimalTypes = { typeof(float), typeof(decimal), typeof(double) }; + + internal static bool TryConvert(ref object value, BindableProperty targetProperty, Type convertTo, bool toTarget) + { + if (value == null) + return !convertTo.GetTypeInfo().IsValueType || Nullable.GetUnderlyingType(convertTo) != null; + try + { + if ((toTarget && targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value))) + return true; + } + catch (InvalidOperationException) + { //that's what TypeConverters ususally throw + return false; + } + + object original = value; + try + { + convertTo = Nullable.GetUnderlyingType(convertTo) ?? convertTo; + + var stringValue = value as string ?? string.Empty; + // see: https://bugzilla.xamarin.com/show_bug.cgi?id=32871 + // do not canonicalize "*.[.]"; "1." should not update bound BindableProperty + if (stringValue.EndsWith(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, StringComparison.Ordinal) && DecimalTypes.Contains(convertTo)) + { + value = original; + return false; + } + + // do not canonicalize "-0"; user will likely enter a period after "-0" + if (stringValue == "-0" && DecimalTypes.Contains(convertTo)) + { + value = original; + return false; + } + + value = Convert.ChangeType(value, convertTo, CultureInfo.CurrentCulture); + + return true; + } + catch (Exception ex) when (ex is InvalidCastException || ex is FormatException || ex is InvalidOperationException || ex is OverflowException) + { + value = original; + return false; + } + } + } +} diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewAdapter.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewAdapter.cs index 960718e34fef..92647b2b075b 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewAdapter.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewAdapter.cs @@ -763,7 +763,9 @@ Cell GetNewGroupHeaderCell(ITemplatedItemsList group) else { groupHeaderCell = new TextCell(); - groupHeaderCell.SetBinding(TextCell.TextProperty, nameof(group.Name)); + groupHeaderCell.SetBinding( + TextCell.TextProperty, + TypedBinding.ForSingleNestingLevel(nameof(group.Name), static (ITemplatedItemsList g) => g.Name)); groupHeaderCell.BindingContext = group; } diff --git a/src/Controls/src/Core/Compatibility/iOS/Extensions/UIViewExtensions.cs b/src/Controls/src/Core/Compatibility/iOS/Extensions/UIViewExtensions.cs index 1cd6dce834ee..e00daa84a4bf 100644 --- a/src/Controls/src/Core/Compatibility/iOS/Extensions/UIViewExtensions.cs +++ b/src/Controls/src/Core/Compatibility/iOS/Extensions/UIViewExtensions.cs @@ -5,6 +5,8 @@ using static System.String; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; +using System.Diagnostics.CodeAnalysis; + #if __MOBILE__ using ObjCRuntime; using UIKit; @@ -48,6 +50,7 @@ public static SizeRequest GetSizeRequest(this UIView self, double widthConstrain return new SizeRequest(request, minimum); } + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this UIView view, string propertyName, BindingBase bindingBase, string updateSourceEventName = null) { diff --git a/src/Controls/src/Core/Controls.Core.csproj b/src/Controls/src/Core/Controls.Core.csproj index d6ec168e682b..760c0f19c6ed 100644 --- a/src/Controls/src/Core/Controls.Core.csproj +++ b/src/Controls/src/Core/Controls.Core.csproj @@ -21,6 +21,14 @@ .NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating native mobile and desktop apps with C# and XAML. This package only contains the core C# and XAML objects used by .NET MAUI. Please install the Microsoft.Maui.Controls package to start using .NET MAUI. + + true + true + true + + $(NoWarn);IL2059 + + diff --git a/src/Controls/src/Core/HandlerAttribute.cs b/src/Controls/src/Core/HandlerAttribute.cs index 671ac6ddbb14..3c1794d95717 100644 --- a/src/Controls/src/Core/HandlerAttribute.cs +++ b/src/Controls/src/Core/HandlerAttribute.cs @@ -1,6 +1,7 @@ #nullable disable using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui.Controls { @@ -8,11 +9,11 @@ namespace Microsoft.Maui.Controls [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public abstract class HandlerAttribute : Attribute { - protected HandlerAttribute(Type handler, Type target) : this(handler, target, null) + protected HandlerAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target) : this(handler, target, null) { } - protected HandlerAttribute(Type handler, Type target, Type[] supportedVisuals) + protected HandlerAttribute(Type handler, [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type target, Type[] supportedVisuals) { SupportedVisuals = supportedVisuals ?? new[] { typeof(VisualMarker.DefaultVisual) }; TargetType = target; @@ -26,6 +27,7 @@ protected HandlerAttribute(Type handler, Type target, Type[] supportedVisuals) internal Type[] SupportedVisuals { get; private set; } internal Type HandlerType { get; private set; } + [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] internal Type TargetType { get; private set; } /// diff --git a/src/Controls/src/Core/INativeBindingService.cs b/src/Controls/src/Core/INativeBindingService.cs index 721c3149fc29..26f7acc3eaba 100644 --- a/src/Controls/src/Core/INativeBindingService.cs +++ b/src/Controls/src/Core/INativeBindingService.cs @@ -1,9 +1,12 @@ #nullable disable +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.Maui.Controls.Xaml.Internals { public interface INativeBindingService { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] bool TrySetBinding(object target, string propertyName, BindingBase binding); bool TrySetBinding(object target, BindableProperty property, BindingBase binding); bool TrySetValue(object target, BindableProperty property, object value); diff --git a/src/Controls/src/Core/MultiBinding.cs b/src/Controls/src/Core/MultiBinding.cs index eaa9baef2d44..36c6966c215a 100644 --- a/src/Controls/src/Core/MultiBinding.cs +++ b/src/Controls/src/Core/MultiBinding.cs @@ -76,6 +76,8 @@ internal override BindingBase Clone() return clone; } + internal static readonly object DoNothing = new object(); // this object instance must be the same as Binding.DoNothing + internal override void Apply(bool fromTarget) { if (_applying) @@ -95,10 +97,10 @@ internal override void Apply(bool fromTarget) if (!fromTarget) { var value = GetSourceValue(GetValueArray(), _targetProperty.ReturnType); - if (value != Binding.DoNothing) + if (value != DoNothing) { _applying = true; - if (!BindingExpression.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) + if (!BindingExpressionHelper.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(this, null, _targetObject, _targetProperty, "MultiBinding", BindingExpression.CannotConvertTypeErrorMessage, value, _targetProperty.ReturnType); return; @@ -118,7 +120,7 @@ internal override void Apply(bool fromTarget) return; for (var i = 0; i < Math.Min(_bpProxies.Length, values.Length); i++) { - if (ReferenceEquals(values[i], Binding.DoNothing) || ReferenceEquals(values[i], BindableProperty.UnsetValue)) + if (ReferenceEquals(values[i], DoNothing) || ReferenceEquals(values[i], BindableProperty.UnsetValue)) continue; _proxyObject.SetValue(_bpProxies[i], values[i]); } @@ -168,10 +170,10 @@ internal override void Apply(object context, BindableObject targetObject, Bindab return; var value = GetSourceValue(GetValueArray(), _targetProperty.ReturnType); - if (value != Binding.DoNothing) + if (value != DoNothing) { _applying = true; - if (!BindingExpression.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) + if (!BindingExpressionHelper.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(this, context, _targetObject, _targetProperty, "MultiBinding", BindingExpression.CannotConvertTypeErrorMessage, value, _targetProperty.ReturnType); return; diff --git a/src/Controls/src/Core/MultiPage.cs b/src/Controls/src/Core/MultiPage.cs index ee42ac77dfaf..6b8d6f27dbc5 100644 --- a/src/Controls/src/Core/MultiPage.cs +++ b/src/Controls/src/Core/MultiPage.cs @@ -13,7 +13,7 @@ namespace Microsoft.Maui.Controls { [ContentProperty("Children")] - public abstract class MultiPage<[DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers)] T> : Page, IViewContainer, IPageContainer, IItemsView, IMultiPageController where T : Page + public abstract class MultiPage<[DynamicallyAccessedMembers(BindableProperty.DeclaringTypeMembers | BindableProperty.ReturnTypeMembers)] T> : Page, IViewContainer, IPageContainer, IItemsView, IMultiPageController where T : Page { /// Bindable property for . public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(MultiPage<>), null); diff --git a/src/Controls/src/Core/PlatformBindingHelpers.cs b/src/Controls/src/Core/PlatformBindingHelpers.cs index a9f04355e5e6..d3444129ff15 100644 --- a/src/Controls/src/Core/PlatformBindingHelpers.cs +++ b/src/Controls/src/Core/PlatformBindingHelpers.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -14,6 +15,7 @@ namespace Microsoft.Maui.Controls.Internals /// internal static class PlatformBindingHelpers { + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(TPlatformView target, string targetProperty, BindingBase bindingBase, string updateSourceEventName = null) where TPlatformView : class { var binding = bindingBase as Binding; @@ -22,11 +24,12 @@ public static void SetBinding(TPlatformView target, string target updateSourceEventName = binding.UpdateSourceEventName; INotifyPropertyChanged eventWrapper = null; if (!IsNullOrEmpty(updateSourceEventName)) - eventWrapper = new EventWrapper(target, targetProperty, updateSourceEventName); + eventWrapper = new EventWrapper(target, targetProperty, updateSourceEventName); SetBinding(target, targetProperty, bindingBase, eventWrapper); } + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(TPlatformView target, string targetProperty, BindingBase bindingBase, INotifyPropertyChanged propertyChanged) where TPlatformView : class { if (target == null) @@ -41,13 +44,13 @@ public static void SetBinding(TPlatformView target, string target var targetPropertyInfo = target.GetType().GetProperty(targetProperty); var propertyType = targetPropertyInfo?.PropertyType; var defaultValue = targetPropertyInfo?.GetMethod.Invoke(target, Array.Empty()); - bindableProperty = CreateBindableProperty(targetProperty, propertyType, defaultValue); + bindableProperty = CreateBindableProperty(targetProperty, propertyType, defaultValue); if (binding != null && binding.Mode != BindingMode.OneWay && propertyChanged != null) propertyChanged.PropertyChanged += (sender, e) => { if (e.PropertyName != targetProperty) return; - SetValueFromNative(sender as TPlatformView, targetProperty, bindableProperty); + SetValueFromNative(sender as TPlatformView, targetProperty, bindableProperty); //we need to keep the listener around he same time we have the proxy proxy.NativeINPCListener = propertyChanged; }; @@ -56,46 +59,46 @@ public static void SetBinding(TPlatformView target, string target SetValueFromNative(target, targetProperty, bindableProperty); proxy.SetBinding(bindableProperty, bindingBase); - } - static BindableProperty CreateBindableProperty(string targetProperty, Type propertyType = null, object defaultValue = null) where TPlatformView : class - { - propertyType = propertyType ?? typeof(object); - defaultValue = defaultValue ?? (propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null); - return BindableProperty.Create( - targetProperty, - propertyType, - typeof(BindableObjectProxy), - defaultValue: defaultValue, - defaultBindingMode: BindingMode.Default, - propertyChanged: (bindable, oldValue, newValue) => - { - TPlatformView platformView; - if ((bindable as BindableObjectProxy).TargetReference.TryGetTarget(out platformView)) - SetPlatformValue(platformView, targetProperty, newValue); - } - ); - } + static BindableProperty CreateBindableProperty(string targetProperty, Type propertyType = null, object defaultValue = null) + { + propertyType = propertyType ?? typeof(object); + defaultValue = defaultValue ?? (propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null); + return BindableProperty.Create( + targetProperty, + propertyType, + typeof(BindableObjectProxy), + defaultValue: defaultValue, + defaultBindingMode: BindingMode.Default, + propertyChanged: (bindable, oldValue, newValue) => + { + TPlatformView platformView; + if ((bindable as BindableObjectProxy).TargetReference.TryGetTarget(out platformView)) + SetPlatformValue(platformView, targetProperty, newValue); + } + ); + } - static void SetPlatformValue(TPlatformView target, string targetProperty, object newValue) where TPlatformView : class - { - var mi = target.GetType().GetProperty(targetProperty)?.SetMethod; - if (mi == null) - throw new InvalidOperationException(Format("Native Binding on {0}.{1} failed due to missing or inaccessible property", target.GetType(), targetProperty)); - mi.Invoke(target, new[] { newValue }); - } + static void SetPlatformValue(TPlatformView target, string targetProperty, object newValue) + { + var mi = target.GetType().GetProperty(targetProperty)?.SetMethod; + if (mi == null) + throw new InvalidOperationException(Format("Native Binding on {0}.{1} failed due to missing or inaccessible property", target.GetType(), targetProperty)); + mi.Invoke(target, new[] { newValue }); + } - static void SetValueFromNative(TPlatformView target, string targetProperty, BindableProperty bindableProperty) where TPlatformView : class - { - BindableObjectProxy proxy; - if (!BindableObjectProxy.BindableObjectProxies.TryGetValue(target, out proxy)) - return; - SetValueFromRenderer(proxy, bindableProperty, target.GetType().GetProperty(targetProperty)?.GetMethod.Invoke(target, Array.Empty())); - } + static void SetValueFromNative(TPlatformView target, string targetProperty, BindableProperty bindableProperty) + { + BindableObjectProxy proxy; + if (!BindableObjectProxy.BindableObjectProxies.TryGetValue(target, out proxy)) + return; + SetValueFromRenderer(proxy, bindableProperty, target.GetType().GetProperty(targetProperty)?.GetMethod.Invoke(target, Array.Empty())); + } - static void SetValueFromRenderer(BindableObject bindable, BindableProperty property, object value) - { - bindable.SetValue(property, value); + static void SetValueFromRenderer(BindableObject bindable, BindableProperty property, object value) + { + bindable.SetValue(property, value); + } } public static void SetBinding(TPlatformView target, BindableProperty targetProperty, BindingBase binding) where TPlatformView : class @@ -149,20 +152,19 @@ public static void TransferBindablePropertiesToWrapper : INotifyPropertyChanged { string TargetProperty { get; set; } - static readonly MethodInfo s_handlerinfo = typeof(EventWrapper).GetRuntimeMethods().Single(mi => mi.Name == "OnPropertyChanged" && mi.IsPublic == false); - public EventWrapper(object target, string targetProperty, string updateSourceEventName) + public EventWrapper(TTarget target, string targetProperty, string updateSourceEventName) { TargetProperty = targetProperty; Delegate handlerDelegate = null; EventInfo updateSourceEvent = null; try { - updateSourceEvent = target.GetType().GetRuntimeEvent(updateSourceEventName); - handlerDelegate = s_handlerinfo.CreateDelegate(updateSourceEvent.EventHandlerType, this); + updateSourceEvent = typeof(TTarget).GetRuntimeEvent(updateSourceEventName); + handlerDelegate = ((EventHandler)OnPropertyChanged).Method.CreateDelegate(updateSourceEvent.EventHandlerType, this); } catch (Exception) { diff --git a/src/Controls/src/Core/Registrar.cs b/src/Controls/src/Core/Registrar.cs index 891d14bb0703..858c99c1a2cd 100644 --- a/src/Controls/src/Core/Registrar.cs +++ b/src/Controls/src/Core/Registrar.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Microsoft.Extensions.DependencyInjection; @@ -21,37 +22,58 @@ public enum InitializationFlags : long namespace Microsoft.Maui.Controls.Internals { + internal struct HandlerType + { + internal const DynamicallyAccessedMemberTypes TargetMembers = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + + [DynamicallyAccessedMembers(TargetMembers)] + public readonly Type Target; + public readonly short Priority; + + public HandlerType( + [DynamicallyAccessedMembers(TargetMembers)] Type target, + short priority) + { + Target = target; + Priority = priority; + } + } + [EditorBrowsable(EditorBrowsableState.Never)] public class Registrar where TRegistrable : class { - readonly Dictionary> _handlers = new Dictionary>(); + readonly Dictionary> _handlers = new Dictionary>(); static Type _defaultVisualType = typeof(VisualMarker.DefaultVisual); //static Type _materialVisualType = typeof(VisualMarker.MaterialVisual); static Type[] _defaultVisualRenderers = new[] { _defaultVisualType }; - public void Register(Type tview, Type trender, Type[] supportedVisuals, short priority) + public void Register( + Type tview, + [DynamicallyAccessedMembers(HandlerType.TargetMembers)] Type trender, + Type[] supportedVisuals, + short priority) { supportedVisuals = supportedVisuals ?? _defaultVisualRenderers; //avoid caching null renderers if (trender == null) return; - if (!_handlers.TryGetValue(tview, out Dictionary visualRenderers)) + if (!_handlers.TryGetValue(tview, out Dictionary visualRenderers)) { - visualRenderers = new Dictionary(); + visualRenderers = new Dictionary(); _handlers[tview] = visualRenderers; } for (int i = 0; i < supportedVisuals.Length; i++) { - if (visualRenderers.TryGetValue(supportedVisuals[i], out (Type target, short priority) existingTargetValue)) + if (visualRenderers.TryGetValue(supportedVisuals[i], out HandlerType existingTargetValue)) { - if (existingTargetValue.priority <= priority) - visualRenderers[supportedVisuals[i]] = (trender, priority); + if (existingTargetValue.Priority <= priority) + visualRenderers[supportedVisuals[i]] = new(trender, priority); } else - visualRenderers[supportedVisuals[i]] = (trender, priority); + visualRenderers[supportedVisuals[i]] = new(trender, priority); } // This registers a factory into the Handler version of the registrar. @@ -71,9 +93,11 @@ public void Register(Type tview, Type trender, Type[] supportedVisuals, short pr // }); } - public void Register(Type tview, Type trender, Type[] supportedVisual) => Register(tview, trender, supportedVisual, 0); + public void Register(Type tview, [DynamicallyAccessedMembers(HandlerType.TargetMembers)] Type trender, Type[] supportedVisual) + => Register(tview, trender, supportedVisual, 0); - public void Register(Type tview, Type trender) => Register(tview, trender, _defaultVisualRenderers); + public void Register(Type tview, [DynamicallyAccessedMembers(HandlerType.TargetMembers)] Type trender) + => Register(tview, trender, _defaultVisualRenderers); internal TRegistrable GetHandler(Type type) => GetHandler(type, _defaultVisualType); @@ -143,27 +167,28 @@ public TOut GetHandlerForObject(object obj, params object[] args) where TO public Type GetHandlerType(Type viewType) => GetHandlerType(viewType, _defaultVisualType); + [return: DynamicallyAccessedMembers(HandlerType.TargetMembers)] public Type GetHandlerType(Type viewType, Type visualType) { visualType = visualType ?? _defaultVisualType; // 1. Do we have this specific type registered already? - if (_handlers.TryGetValue(viewType, out Dictionary visualRenderers)) - if (visualRenderers.TryGetValue(visualType, out (Type target, short priority) specificTypeRenderer)) - return specificTypeRenderer.target; + if (_handlers.TryGetValue(viewType, out Dictionary visualRenderers)) + if (visualRenderers.TryGetValue(visualType, out HandlerType specificTypeRenderer)) + return specificTypeRenderer.Target; //else if (visualType == _materialVisualType) // VisualMarker.MaterialCheck(); if (visualType != _defaultVisualType && visualRenderers != null) - if (visualRenderers.TryGetValue(_defaultVisualType, out (Type target, short priority) specificTypeRenderer)) - return specificTypeRenderer.target; + if (visualRenderers.TryGetValue(_defaultVisualType, out HandlerType specificTypeRenderer)) + return specificTypeRenderer.Target; // 2. Do we have a RenderWith for this type or its base types? Register them now. RegisterRenderWithTypes(viewType, visualType); // 3. Do we have a custom renderer for a base type or did we just register an appropriate renderer from RenderWith? - if (LookupHandlerType(viewType, visualType, out (Type target, short priority) baseTypeRenderer)) - return baseTypeRenderer.target; + if (LookupHandlerType(viewType, visualType, out HandlerType baseTypeRenderer)) + return baseTypeRenderer.Target; else return null; } @@ -179,12 +204,12 @@ public Type GetHandlerTypeForObject(object obj) return GetHandlerType(type); } - bool LookupHandlerType(Type viewType, Type visualType, out (Type target, short priority) handlerType) + bool LookupHandlerType(Type viewType, Type visualType, out HandlerType handlerType) { visualType = visualType ?? _defaultVisualType; while (viewType != null && viewType != typeof(Element)) { - if (_handlers.TryGetValue(viewType, out Dictionary visualRenderers)) + if (_handlers.TryGetValue(viewType, out Dictionary visualRenderers)) if (visualRenderers.TryGetValue(visualType, out handlerType)) return true; @@ -195,7 +220,7 @@ bool LookupHandlerType(Type viewType, Type visualType, out (Type target, short p viewType = viewType.BaseType; } - handlerType = (null, 0); + handlerType = new(null, 0); return false; } @@ -211,7 +236,7 @@ void RegisterRenderWithTypes(Type viewType, Type visualType) // Only go through this process if we have not registered something for this type; // we don't want RenderWith renderers to override ExportRenderers that are already registered. // Plus, there's no need to do this again if we already have a renderer registered. - if (!_handlers.TryGetValue(viewType, out Dictionary visualRenderers) || + if (!_handlers.TryGetValue(viewType, out Dictionary visualRenderers) || !(visualRenderers.ContainsKey(visualType) || visualRenderers.ContainsKey(_defaultVisualType))) { diff --git a/src/Controls/src/Core/RenderWithAttribute.cs b/src/Controls/src/Core/RenderWithAttribute.cs index 56028e5849e9..40974bae652a 100644 --- a/src/Controls/src/Core/RenderWithAttribute.cs +++ b/src/Controls/src/Core/RenderWithAttribute.cs @@ -1,5 +1,6 @@ #nullable disable using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui.Controls { @@ -7,14 +8,15 @@ namespace Microsoft.Maui.Controls [AttributeUsage(AttributeTargets.Class)] public sealed class RenderWithAttribute : Attribute { - /// - public RenderWithAttribute(Type type) : this(type, new[] { typeof(VisualMarker.DefaultVisual) }) + public RenderWithAttribute([DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type type) : this(type, new[] { typeof(VisualMarker.DefaultVisual) }) { } /// - public RenderWithAttribute(Type type, Type[] supportedVisuals) + public RenderWithAttribute( + [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] Type type, + Type[] supportedVisuals) { Type = type; SupportedVisuals = supportedVisuals ?? new[] { typeof(VisualMarker.DefaultVisual) }; @@ -22,7 +24,9 @@ public RenderWithAttribute(Type type, Type[] supportedVisuals) /// public Type[] SupportedVisuals { get; } + /// + [DynamicallyAccessedMembers(Internals.HandlerType.TargetMembers)] public Type Type { get; } } } \ No newline at end of file diff --git a/src/Controls/src/Core/StyleSheets/StylePropertyAttribute.cs b/src/Controls/src/Core/StyleSheets/StylePropertyAttribute.cs index bb0355cabd84..3711d31a00c9 100644 --- a/src/Controls/src/Core/StyleSheets/StylePropertyAttribute.cs +++ b/src/Controls/src/Core/StyleSheets/StylePropertyAttribute.cs @@ -1,5 +1,6 @@ #nullable disable using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui.Controls.StyleSheets { @@ -8,7 +9,9 @@ sealed class StylePropertyAttribute : Attribute { public string CssPropertyName { get; } public string BindablePropertyName { get; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public Type TargetType { get; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public Type PropertyOwnerType { get; set; } public BindableProperty BindableProperty { get; set; } public bool Inherited { get; set; } = false; diff --git a/src/Controls/src/Core/TemplateBinding.cs b/src/Controls/src/Core/TemplateBinding.cs index 99c6bc51dc86..28641fb3f73e 100644 --- a/src/Controls/src/Core/TemplateBinding.cs +++ b/src/Controls/src/Core/TemplateBinding.cs @@ -1,11 +1,13 @@ #nullable disable using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Microsoft.Maui.Controls { /// [Obsolete("Use Binding.Source=RelativeBindingSource.TemplatedParent")] + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public sealed class TemplateBinding : BindingBase { internal const string SelfPath = "."; diff --git a/src/Controls/src/Core/TemplateExtensions.cs b/src/Controls/src/Core/TemplateExtensions.cs index 01f45f532642..3a4c6b46ddd4 100644 --- a/src/Controls/src/Core/TemplateExtensions.cs +++ b/src/Controls/src/Core/TemplateExtensions.cs @@ -1,5 +1,6 @@ #nullable disable using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui.Controls { @@ -7,6 +8,7 @@ namespace Microsoft.Maui.Controls public static class TemplateExtensions { /// + [RequiresUnreferencedCode(TrimmerConstants.StringPathBindingWarning, Url = TrimmerConstants.ExpressionBasedBindingsDocsUrl)] public static void SetBinding(this DataTemplate self, BindableProperty targetProperty, string path) { if (self == null) diff --git a/src/Controls/src/Core/TrimmerConstants.cs b/src/Controls/src/Core/TrimmerConstants.cs index 6d39f3865aae..f87c98624403 100644 --- a/src/Controls/src/Core/TrimmerConstants.cs +++ b/src/Controls/src/Core/TrimmerConstants.cs @@ -5,7 +5,9 @@ class TrimmerConstants // https://github.com/dotnet/runtime/blob/f130138b337b57342e94dabf499b818531effed5/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs#L31-L32 internal const string SerializerTrimmerWarning = "Data Contract Serialization and Deserialization might require types that cannot be statically analyzed. Make sure all of the required types are preserved."; - internal const string NativeBindingService = "This method properly handles missing properties, and there is not a way to preserve them from this method."; + internal const string StringPathBindingWarning = "Using bindings with string paths is not trim safe. Use expression-based binding instead."; + + internal const string? ExpressionBasedBindingsDocsUrl = null; // TODO: we don't have this page yet internal const string XamlRuntimeParsingNotSupportedWarning = "Loading XAML at runtime might require types and members that cannot be statically analyzed. Make sure all of the required types and members are preserved."; diff --git a/src/Controls/src/Core/TypedBinding.cs b/src/Controls/src/Core/TypedBinding.cs index 6c9c5ecb17da..648a2c6b0626 100644 --- a/src/Controls/src/Core/TypedBinding.cs +++ b/src/Controls/src/Core/TypedBinding.cs @@ -306,7 +306,7 @@ internal void ApplyCore(object sourceObject, BindableObject target, BindableProp { } } - if (!BindingExpression.TryConvert(ref value, property, property.ReturnType, true)) + if (!BindingExpressionHelper.TryConvert(ref value, property, property.ReturnType, true)) { BindingDiagnostics.SendBindingFailure(this, sourceObject, target, property, "Binding", BindingExpression.CannotConvertTypeErrorMessage, value, property.ReturnType); return; @@ -319,7 +319,7 @@ internal void ApplyCore(object sourceObject, BindableObject target, BindableProp if (needsSetter && _setter != null && isTSource) { var value = GetTargetValue(target.GetValue(property), typeof(TProperty)); - if (!BindingExpression.TryConvert(ref value, property, typeof(TProperty), false)) + if (!BindingExpressionHelper.TryConvert(ref value, property, typeof(TProperty), false)) { BindingDiagnostics.SendBindingFailure(this, sourceObject, target, property, "Binding", BindingExpression.CannotConvertTypeErrorMessage, value, typeof(TProperty)); return; diff --git a/src/Controls/src/Core/VisualElement/VisualElement_StyleSheet.cs b/src/Controls/src/Core/VisualElement/VisualElement_StyleSheet.cs index 2f376722e2cd..97121851de20 100644 --- a/src/Controls/src/Core/VisualElement/VisualElement_StyleSheet.cs +++ b/src/Controls/src/Core/VisualElement/VisualElement_StyleSheet.cs @@ -1,5 +1,6 @@ #nullable disable using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Microsoft.Maui.Controls.Internals; @@ -8,6 +9,7 @@ namespace Microsoft.Maui.Controls { /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public partial class VisualElement : IStylable { BindableProperty IStylable.GetProperty(string key, bool inheriting) diff --git a/src/Controls/src/Core/Visuals/VisualTypeConverter.cs b/src/Controls/src/Core/Visuals/VisualTypeConverter.cs index 0edac84ecec4..73ba1916d4ee 100644 --- a/src/Controls/src/Core/Visuals/VisualTypeConverter.cs +++ b/src/Controls/src/Core/Visuals/VisualTypeConverter.cs @@ -48,11 +48,11 @@ void ScanAllAssemblies(Dictionary mappings) // Check for IVisual Types foreach (var assembly in assemblies) - Register(assembly, mappings); + RegisterAllIVisualTypesInAssembly(assembly, mappings); if (Internals.Registrar.ExtraAssemblies != null) foreach (var assembly in Internals.Registrar.ExtraAssemblies) - Register(assembly, mappings); + RegisterAllIVisualTypesInAssembly(assembly, mappings); // Check for visual assembly attributes after scanning for IVisual Types @@ -63,45 +63,45 @@ void ScanAllAssemblies(Dictionary mappings) if (Internals.Registrar.ExtraAssemblies != null) foreach (var assembly in Internals.Registrar.ExtraAssemblies) RegisterFromAttributes(assembly, mappings); - } - - static void RegisterFromAttributes(Assembly assembly, Dictionary mappings) - { - object[] attributes = assembly.GetCustomAttributesSafe(typeof(VisualAttribute)); - if (attributes != null) + static void RegisterAllIVisualTypesInAssembly(Assembly assembly, Dictionary mappings) { - foreach (VisualAttribute attribute in attributes) + if (assembly.IsDynamic) + return; + + try + { + foreach (var type in assembly.GetExportedTypes()) + if (typeof(IVisual).IsAssignableFrom(type) && type != typeof(IVisual)) + Register(type, mappings); + } + catch (NotSupportedException) { - var visual = CreateVisual(attribute.Visual); - if (visual != null) - mappings[attribute.Key] = visual; + Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Cannot scan assembly {assembly} for Visual types.", assembly.FullName); + } + catch (FileNotFoundException) + { + Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Unable to load a dependent assembly for {assembly}. It cannot be scanned for Visual types.", assembly.FullName); + } + catch (ReflectionTypeLoadException) + { + Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Unable to load a dependent assembly for {assembly}. Types cannot be loaded.", assembly.FullName); } } - } - - static void Register(Assembly assembly, Dictionary mappings) - { - if (assembly.IsDynamic) - return; - try - { - foreach (var type in assembly.GetExportedTypes()) - if (typeof(IVisual).IsAssignableFrom(type) && type != typeof(IVisual)) - Register(type, mappings); - } - catch (NotSupportedException) - { - Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Cannot scan assembly {assembly} for Visual types.", assembly.FullName); - } - catch (FileNotFoundException) - { - Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Unable to load a dependent assembly for {assembly}. It cannot be scanned for Visual types.", assembly.FullName); - } - catch (ReflectionTypeLoadException) + static void RegisterFromAttributes(Assembly assembly, Dictionary mappings) { - Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning("Unable to load a dependent assembly for {assembly}. Types cannot be loaded.", assembly.FullName); + object[] attributes = assembly.GetCustomAttributesSafe(typeof(VisualAttribute)); + + if (attributes != null) + { + foreach (VisualAttribute attribute in attributes) + { + var visual = CreateVisual(attribute.Visual); + if (visual != null) + mappings[attribute.Key] = visual; + } + } } } diff --git a/src/Controls/tests/Core.UnitTests/BindingExpressionTests.cs b/src/Controls/tests/Core.UnitTests/BindingExpressionTests.cs index e0d74726dd50..46eca59fbc36 100644 --- a/src/Controls/tests/Core.UnitTests/BindingExpressionTests.cs +++ b/src/Controls/tests/Core.UnitTests/BindingExpressionTests.cs @@ -139,7 +139,7 @@ public static IEnumerable TryConvertWithNumbersAndCulturesCasesData() public void TryConvertWithNumbersAndCultures(object inputString, CultureInfo culture, object expected) { CultureInfo.CurrentCulture = culture; - BindingExpression.TryConvert(ref inputString, Entry.TextProperty, expected.GetType(), false); + BindingExpressionHelper.TryConvert(ref inputString, Entry.TextProperty, expected.GetType(), false); Assert.Equal(expected, inputString); } diff --git a/src/Core/src/Core.csproj b/src/Core/src/Core.csproj index e5f3c9ed32a8..6e7ac4b200c5 100644 --- a/src/Core/src/Core.csproj +++ b/src/Core/src/Core.csproj @@ -66,12 +66,6 @@ - - - ILLink.Substitutions.xml - - - <_CopyItems Include="nuget\buildTransitive\**" Exclude="nuget\**\*.in.*" /> diff --git a/src/Core/src/ILLink.Substitutions.xml b/src/Core/src/ILLink.Substitutions.xml deleted file mode 100644 index 61802dbac054..000000000000 --- a/src/Core/src/ILLink.Substitutions.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/Core/src/RuntimeFeature.cs b/src/Core/src/RuntimeFeature.cs index 3e47cd1ac88a..653fc47a5d39 100644 --- a/src/Core/src/RuntimeFeature.cs +++ b/src/Core/src/RuntimeFeature.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Maui { @@ -8,7 +9,6 @@ namespace Microsoft.Maui /// for examples of how to add new feature switches. /// /// - /// Property names must be kept in sync with ILLink.Substitutions.xml for proper value substitutions. /// Mapping of MSBuild properties to feature switches and the default values of feature switches /// is defined in Microsoft.Maui.Sdk.Before.targets. /// @@ -20,6 +20,11 @@ internal static class RuntimeFeature private const bool IsQueryPropertyAttributeSupportedByDefault = true; private const bool IsImplicitCastOperatorsUsageViaReflectionSupportedByDefault = true; +#pragma warning disable IL4000 // Return value does not match FeatureGuardAttribute 'System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute'. +#if !NETSTANDARD + [FeatureSwitchDefinition("Microsoft.Maui.RuntimeFeature.IsXamlRuntimeParsingSupported")] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#endif internal static bool IsXamlRuntimeParsingSupported => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsXamlRuntimeParsingSupported", out bool isSupported) ? isSupported @@ -30,24 +35,41 @@ internal static bool IsXamlRuntimeParsingSupported "the MauiXamlRuntimeParsingSupport MSBuild property to true. Note: this feature is not trimming-safe and it might not " + "behave as expected when the application is trimmed."; +#if !NETSTANDARD + [FeatureSwitchDefinition("Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled")] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#endif internal static bool IsIVisualAssemblyScanningEnabled => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled", out bool isEnabled) ? isEnabled : IsIVisualAssemblyScanningEnabledByDefault; +#if !NETSTANDARD + [FeatureSwitchDefinition("Microsoft.Maui.RuntimeFeature.IsShellSearchResultsRendererDisplayMemberNameSupported")] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#endif internal static bool IsShellSearchResultsRendererDisplayMemberNameSupported => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsShellSearchResultsRendererDisplayMemberNameSupported", out bool isSupported) ? isSupported : IsShellSearchResultsRendererDisplayMemberNameSupportedByDefault; +#if !NETSTANDARD + [FeatureSwitchDefinition("Microsoft.Maui.RuntimeFeature.IsQueryPropertyAttributeSupported")] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#endif internal static bool IsQueryPropertyAttributeSupported => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsQueryPropertyAttributeSupported", out bool isSupported) ? isSupported : IsQueryPropertyAttributeSupportedByDefault; +#if !NETSTANDARD + [FeatureSwitchDefinition("Microsoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported")] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#endif internal static bool IsImplicitCastOperatorsUsageViaReflectionSupported => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported", out bool isSupported) ? isSupported : IsImplicitCastOperatorsUsageViaReflectionSupportedByDefault; +#pragma warning restore IL4000 } }