diff --git a/docs/design/FeatureSwitches.md b/docs/design/FeatureSwitches.md index ed40bb2ebeb4..1b93927b3ccf 100644 --- a/docs/design/FeatureSwitches.md +++ b/docs/design/FeatureSwitches.md @@ -5,9 +5,14 @@ Certain features of MAUI can be enabled or disabled using feature switches. The | MSBuild Property Name | AppContext Setting | Description | |-|-|-| | MauiXamlRuntimeParsingSupport | Microsoft.Maui.RuntimeFeature.IsXamlRuntimeParsingSupported | When disabled, all XAML loading at runtime will throw an exception. This will affect usage of APIs such as the `LoadFromXaml` extension method. This feature can be safely turned off when all XAML resources are compiled using XamlC (see [XAML compilation](https://learn.microsoft.com/en-us/dotnet/maui/xaml/xamlc)). This feature is enabled by default for all configurations except for NativeAOT. | +| MauiEnableIVisualAssemblyScanning | Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled | When enabled, MAUI will scan assemblies for types implementing `IVisual` and for `[assembly: Visual(...)]` attributes and register these types. | ## MauiXamlRuntimeParsingSupport When this feature is disabled, the following APIs are affected: - [`LoadFromXaml` extension methods](https://learn.microsoft.com/en-us/dotnet/maui/xaml/runtime-load) will throw runtime exceptions. - [Disabling XAML compilation](https://learn.microsoft.com/en-us/dotnet/maui/xaml/xamlc#disable-xaml-compilation) using `[XamlCompilation(XamlCompilationOptions.Skip)]` on pages and controls or whole assemblies will cause runtime exceptions. + +## MauiEnableIVisualAssemblyScanning + +When this feature is not enabled, custom and third party `IVisual` types will not be automatically discovered and registerd. diff --git a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets index 5a95ae630a03..a044846be4bb 100644 --- a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets +++ b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets @@ -209,12 +209,17 @@ false + false + diff --git a/src/Controls/src/Core/Visuals/VisualTypeConverter.cs b/src/Controls/src/Core/Visuals/VisualTypeConverter.cs index 9bcf366c9959..0edac84ecec4 100644 --- a/src/Controls/src/Core/Visuals/VisualTypeConverter.cs +++ b/src/Controls/src/Core/Visuals/VisualTypeConverter.cs @@ -26,6 +26,24 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati void InitMappings() { var mappings = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (RuntimeFeature.IsIVisualAssemblyScanningEnabled) + { + ScanAllAssemblies(mappings); + } + else + { + Register(typeof(VisualMarker.MaterialVisual), mappings); + Register(typeof(VisualMarker.DefaultVisual), mappings); + Register(typeof(VisualMarker.MatchParentVisual), mappings); + } + + _visualTypeMappings = mappings; + } + + [RequiresUnreferencedCode("The IVisual types might be removed by trimming and automatic registration via assembly scanning may not work as expected.")] + void ScanAllAssemblies(Dictionary mappings) + { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); // Check for IVisual Types @@ -45,8 +63,6 @@ void InitMappings() if (Internals.Registrar.ExtraAssemblies != null) foreach (var assembly in Internals.Registrar.ExtraAssemblies) RegisterFromAttributes(assembly, mappings); - - _visualTypeMappings = mappings; } static void RegisterFromAttributes(Assembly assembly, Dictionary mappings) @@ -138,6 +154,13 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c if (_visualTypeMappings.TryGetValue(strValue, out IVisual returnValue)) return returnValue; + if (!RuntimeFeature.IsIVisualAssemblyScanningEnabled) + { + Application.Current?.FindMauiContext()?.CreateLogger()?.LogWarning( + "Unable to find visual {key}. Automatic discovery of IVisual types is disabled. You can enabled it by setting the $(MauiEnableIVisualAssemblyScanning)=true MSBuild property. " + + "Note: automatic registration of IVisual types through assembly scanning is not trimming-compatible and it can lead to slower app startup.", strValue); + } + return VisualMarker.Default; } diff --git a/src/Core/src/ILLink.Substitutions.xml b/src/Core/src/ILLink.Substitutions.xml index 29c57a6aee1e..29cd4af68019 100644 --- a/src/Core/src/ILLink.Substitutions.xml +++ b/src/Core/src/ILLink.Substitutions.xml @@ -3,6 +3,8 @@ + + diff --git a/src/Core/src/RuntimeFeature.cs b/src/Core/src/RuntimeFeature.cs index a5544cfd00cc..0bcbd6ac08db 100644 --- a/src/Core/src/RuntimeFeature.cs +++ b/src/Core/src/RuntimeFeature.cs @@ -15,10 +15,16 @@ namespace Microsoft.Maui internal static class RuntimeFeature { private const bool IsXamlRuntimeParsingSupportedByDefault = true; + private const bool IsIVisualAssemblyScanningEnabledByDefault = false; internal static bool IsXamlRuntimeParsingSupported => AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsXamlRuntimeParsingSupported", out bool isEnabled) ? isEnabled : IsXamlRuntimeParsingSupportedByDefault; + + internal static bool IsIVisualAssemblyScanningEnabled => + AppContext.TryGetSwitch("Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled", out bool isEnabled) + ? isEnabled + : IsIVisualAssemblyScanningEnabledByDefault; } } diff --git a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs index 0176952e17c5..e3eda1be9e8c 100644 --- a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs +++ b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs @@ -49,6 +49,7 @@ public void RunOniOS(string id, string config, string framework, string runtimeI buildProps.Add("PublishAotUsingRuntimePack=true"); // TODO: This parameter will become obsolete https://github.com/dotnet/runtime/issues/87060 buildProps.Add("_IsPublishing=true"); // using dotnet build with -p:_IsPublishing=true enables targeting simulators buildProps.Add($"RuntimeIdentifier={runtimeIdentifier}"); + buildProps.Add("IlcTreatWarningsAsErrors=false"); // TODO: Remove this once all warnings are fixed https://github.com/dotnet/maui/issues/19397 } Assert.IsTrue(DotnetInternal.Build(projectFile, config, framework: $"{framework}-ios", properties: buildProps), diff --git a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/BuildWarningsUtilities.cs b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/BuildWarningsUtilities.cs index ba38fc0e5b40..43e3aeb7a1fe 100644 --- a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/BuildWarningsUtilities.cs +++ b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/BuildWarningsUtilities.cs @@ -225,29 +225,6 @@ public static void AssertWarnings(this List actualWarnings, Lis } }, new WarningsPerFile - { - File = "src/Controls/src/Core/Visuals/VisualTypeConverter.cs", - WarningsPerCode = new List - { - new WarningsPerCode - { - Code = "IL2026", - Messages = new List - { - "Microsoft.Maui.Controls.VisualTypeConverter.Register(Assembly,Dictionary`2): Using member 'System.Reflection.Assembly.GetExportedTypes()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Types might be removed.", - } - }, - new WarningsPerCode - { - Code = "IL2062", - Messages = new List - { - "Microsoft.Maui.Controls.VisualTypeConverter.Register(Assembly,Dictionary`2): Value passed to parameter '' of method 'Microsoft.Maui.Controls.VisualTypeConverter.Register(Type,Dictionary`2)' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements.", - } - }, - } - }, - new WarningsPerFile { File = "src/Core/src/Hosting/Internal/MauiFactory.cs", WarningsPerCode = new List