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