From 7639ebebae746782ac2b8f2472e551ee5105a2fb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 22 Dec 2021 16:59:51 -0600 Subject: [PATCH 001/197] Add feature switch to enable switching compiled bindings on by default. --- packages/Avalonia/AvaloniaBuildTasks.targets | 6 ++++++ .../CompileAvaloniaXamlTask.cs | 4 +++- .../XamlCompilerTaskExecutor.cs | 13 ++++++------ .../AvaloniaXamlIlRuntimeCompiler.cs | 20 ++++++++++++------- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index d43a5c1624..d64c2c5afc 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -5,6 +5,7 @@ low <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false <_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false + false @@ -20,6 +21,10 @@ + + + + !string.IsNullOrWhiteSpace(l)).ToArray(), - ProjectDirectory, OutputPath, VerifyIl, outputImportance, + ProjectDirectory, OutputPath, VerifyIl, DefaultCompileBindings, outputImportance, (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, EnableComInteropPatching, SkipXamlCompilation, DebuggerLaunch); if (!res.Success) @@ -72,6 +72,8 @@ namespace Avalonia.Build.Tasks public string OutputPath { get; set; } public bool VerifyIl { get; set; } + + public bool DefaultCompileBindings { get; set; } public bool EnableComInteropPatching { get; set; } public bool SkipXamlCompilation { get; set; } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 593d79471e..52b58c2318 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -39,15 +39,15 @@ namespace Avalonia.Build.Tasks public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, + string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation) { - return Compile(engine, input, references, projectDirectory, output, verifyIl, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false); + return Compile(engine, input, references, projectDirectory, output, verifyIl, defaultCompileBindings, logImportance, strongNameKey, patchCom, skipXamlCompilation, debuggerLaunch:false); } internal static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) + string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) { var typeSystem = new CecilTypeSystem(references .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) @@ -57,7 +57,7 @@ namespace Avalonia.Build.Tasks if (!skipXamlCompilation) { - var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, logImportance, debuggerLaunch); + var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings, logImportance, debuggerLaunch); if (compileRes == null && !patchCom) return new CompileResult(true); if (compileRes == false) @@ -78,7 +78,8 @@ namespace Avalonia.Build.Tasks } static bool? CompileCore(IBuildEngine engine, CecilTypeSystem typeSystem, - string projectDirectory, bool verifyIl, + string projectDirectory, bool verifyIl, + bool defaultCompileBindings, MessageImportance logImportance , bool debuggerLaunch = false) { @@ -143,7 +144,7 @@ namespace Avalonia.Build.Tasks var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem, xamlLanguage, emitConfig); - var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl }; + var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl, DefaultCompileBindings = defaultCompileBindings }; var editorBrowsableAttribute = typeSystem .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute")) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs index ece90762cb..be293331df 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs @@ -24,6 +24,8 @@ namespace Avalonia.Markup.Xaml.XamlIl { static class AvaloniaXamlIlRuntimeCompiler { + private const string UseCompileBindingsByDefaultConfigSwitch = "Avalonia.UseCompiledBindingsByDefault"; + #if !RUNTIME_XAML_CECIL private static SreTypeSystem _sreTypeSystem; private static Type _ignoresAccessChecksFromAttribute; @@ -178,13 +180,14 @@ namespace Avalonia.Markup.Xaml.XamlIl var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N")); var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N")); + bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; + var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm, _sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter, new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)), new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType))), _sreEmitMappings, - _sreContextType) { EnableIlVerification = true }; - + _sreContextType) { EnableIlVerification = true, DefaultCompileBindings = compileBindingsByDefault }; IXamlType overrideType = null; if (rootInstance != null) @@ -200,8 +203,8 @@ namespace Avalonia.Markup.Xaml.XamlIl return LoadOrPopulate(created, rootInstance); } #endif - - static object LoadOrPopulate(Type created, object rootInstance) + + static object LoadOrPopulate(Type created, object rootInstance) { var isp = Expression.Parameter(typeof(IServiceProvider)); @@ -299,8 +302,6 @@ namespace Avalonia.Markup.Xaml.XamlIl { overrideType = _cecilTypeSystem.GetType(rootInstance.GetType().FullName); } - - var safeUri = uri.ToString() .Replace(":", "_") @@ -324,13 +325,18 @@ namespace Avalonia.Markup.Xaml.XamlIl asm.MainModule.Types.Add(contextDef); var tb = _cecilTypeSystem.CreateTypeBuilder(def); + + bool compileBindingsByDefault = AppContext.TryGetSwitch(UseCompileBindingsByDefaultConfigSwitch, out var compileBindingsSwitchValue) && compileBindingsSwitchValue; var compiler = new AvaloniaXamlIlCompiler(new XamlIlTransformerConfiguration(_cecilTypeSystem, localAssembly == null ? null : _cecilTypeSystem.FindAssembly(localAssembly.GetName().Name), _cecilMappings, XamlIlXmlnsMappings.Resolve(_cecilTypeSystem, _cecilMappings), AvaloniaXamlIlLanguage.CustomValueConverter), _cecilEmitMappings, - _cecilTypeSystem.CreateTypeBuilder(contextDef)); + _cecilTypeSystem.CreateTypeBuilder(contextDef)) + { + DefaultCompileBindings = compileBindingsByDefault + }; compiler.ParseAndCompile(xaml, uri.ToString(), tb, overrideType); var asmPath = Path.Combine(_cecilEmitDir, safeUri + ".dll"); using(var f = File.Create(asmPath)) From 6d3ce0a8a5c8c8bf38a0fed9c9b730bcd8c66e7c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 15 Mar 2022 19:42:18 +0000 Subject: [PATCH 002/197] Add service for runtime platform information. --- .../Avalonia.Markup.Xaml.csproj | 2 + .../XamlIl/Runtime/RuntimePlatformInfo.cs | 46 +++++++++++++++++++ .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 3 ++ 3 files changed, 51 insertions(+) create mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 548aae31a8..961542fc14 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -28,6 +28,7 @@ + @@ -50,6 +51,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs new file mode 100644 index 0000000000..2440989d71 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs @@ -0,0 +1,46 @@ +using System; +using System.Runtime.InteropServices; + + +namespace Avalonia.Markup.Xaml.XamlIl.Runtime; + +public class RuntimePlatformInfo +{ + private static OSPlatform IOS { get; } = OSPlatform.Create("IOS"); + + private static OSPlatform Android { get; } = OSPlatform.Create("ANDROID"); + + private static OSPlatform Browser { get; } = OSPlatform.Create("BROWSER"); + + public static RuntimePlatformInfo Instance { get; } = new(); + + private RuntimePlatformInfo() + { + } + + public bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + public bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + public bool IsIOS => RuntimeInformation.IsOSPlatform(IOS); + + public bool IsAndroid => RuntimeInformation.IsOSPlatform(Android); + + public bool IsBrowser => RuntimeInformation.IsOSPlatform(Browser); + + public bool IsDesktop(Size primaryScreenSize) => primaryScreenSize.Width > 1280; + + public bool IsLaptopOrDesktop(Size primaryScreenSize) => + primaryScreenSize.Width > 1024 && primaryScreenSize.Width <= 1280; + + public bool IsTablet(Size primaryScreenSize) => + primaryScreenSize.Width > 768 && primaryScreenSize.Width <= 1024; + + public bool IsMobileLandscape(Size primaryScreenSize) => + primaryScreenSize.Width > 480 && primaryScreenSize.Width <= 768; + + public bool IsMobile(Size primaryScreenSize) => + primaryScreenSize.Width <= 480; +} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index c48f386ffd..7e5dd79602 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -182,6 +182,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime return _nameScope; if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) return this; + if (serviceType == typeof(RuntimePlatformInfo)) + return RuntimePlatformInfo.Instance; + return null; } From 9479baaa2b0f92922d638c70b6b13076c4d7a97f Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 1 Jun 2022 01:12:20 -0400 Subject: [PATCH 003/197] Add object-based OnPlatformExtension impl --- .../Avalonia.Markup.Xaml.csproj | 1 - .../MarkupExtensions/OnPlatformExtension.cs | 97 +++++++++++++++++++ .../XamlIl/Runtime/RuntimePlatformInfo.cs | 46 --------- .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 2 - 4 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 3664070ca0..f4c5d70f7f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -51,7 +51,6 @@ - diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs new file mode 100644 index 0000000000..a9e8e0c994 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs @@ -0,0 +1,97 @@ +#nullable enable +using System; +using System.Globalization; +using System.Reflection; +using Avalonia.Data.Converters; +using Avalonia.Metadata; +using Avalonia.Platform; +using Avalonia.Styling; + +namespace Avalonia.Markup.Xaml.MarkupExtensions; + +public class OnPlatformExtension +{ + private static readonly object s_unset = new object(); + + public OnPlatformExtension() + { + + } + + public OnPlatformExtension(object defaultValue) + { + Default = defaultValue; + } + + [Content] + public object? Default { get; set; } = s_unset; + public object? Windows { get; set; } = s_unset; + public object? macOS { get; set; } = s_unset; + public object? Linux { get; set; } = s_unset; + public object? Android { get; set; } = s_unset; + public object? iOS { get; set; } = s_unset; + public object? Browser { get; set; } = s_unset; + + public IValueConverter? Converter { get; set; } + + public object? ConverterParameter { get; set; } + + public object? ProvideValue(IServiceProvider serviceProvider) + { + if (Default == s_unset + && Windows == s_unset + && macOS == s_unset + && Linux == s_unset + && Android == s_unset + && iOS == s_unset + && Browser == s_unset) + { + throw new InvalidOperationException("OnPlatformExtension requires a value to be specified for at least one platform or Default."); + } + + var provideTarget = serviceProvider.GetService(); + + var targetType = provideTarget.TargetProperty switch + { + AvaloniaProperty ap => ap.PropertyType, + PropertyInfo pi => pi.PropertyType, + _ => null, + }; + + if (provideTarget.TargetObject is Setter setter) + { + targetType = setter.Property?.PropertyType ?? targetType; + } + + if (!TryGetValueForPlatform(out var value)) + { + return AvaloniaProperty.UnsetValue; + } + + if (targetType is null) + { + return value; + } + + var converter = Converter ?? DefaultValueConverter.Instance; + return converter.Convert(value, targetType, ConverterParameter, CultureInfo.CurrentUICulture); + } + + private bool TryGetValueForPlatform(out object? value) + { + var runtimeInfo = AvaloniaLocator.Current.GetRequiredService().GetRuntimeInfo(); + + value = runtimeInfo.OperatingSystem switch + { + OperatingSystemType.WinNT when Windows != s_unset => Windows, + OperatingSystemType.Linux when Linux != s_unset => Linux, + OperatingSystemType.OSX when macOS != s_unset => macOS, + OperatingSystemType.Android when Android != s_unset => Android, + OperatingSystemType.iOS when iOS != s_unset => iOS, + OperatingSystemType.Browser when Browser != s_unset => Browser, + _ => Default + }; + + return value != s_unset; + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs deleted file mode 100644 index 2440989d71..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/RuntimePlatformInfo.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Runtime.InteropServices; - - -namespace Avalonia.Markup.Xaml.XamlIl.Runtime; - -public class RuntimePlatformInfo -{ - private static OSPlatform IOS { get; } = OSPlatform.Create("IOS"); - - private static OSPlatform Android { get; } = OSPlatform.Create("ANDROID"); - - private static OSPlatform Browser { get; } = OSPlatform.Create("BROWSER"); - - public static RuntimePlatformInfo Instance { get; } = new(); - - private RuntimePlatformInfo() - { - } - - public bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - public bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - - public bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - - public bool IsIOS => RuntimeInformation.IsOSPlatform(IOS); - - public bool IsAndroid => RuntimeInformation.IsOSPlatform(Android); - - public bool IsBrowser => RuntimeInformation.IsOSPlatform(Browser); - - public bool IsDesktop(Size primaryScreenSize) => primaryScreenSize.Width > 1280; - - public bool IsLaptopOrDesktop(Size primaryScreenSize) => - primaryScreenSize.Width > 1024 && primaryScreenSize.Width <= 1280; - - public bool IsTablet(Size primaryScreenSize) => - primaryScreenSize.Width > 768 && primaryScreenSize.Width <= 1024; - - public bool IsMobileLandscape(Size primaryScreenSize) => - primaryScreenSize.Width > 480 && primaryScreenSize.Width <= 768; - - public bool IsMobile(Size primaryScreenSize) => - primaryScreenSize.Width <= 480; -} diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index 7e5dd79602..1d7fffe195 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -182,8 +182,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime return _nameScope; if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) return this; - if (serviceType == typeof(RuntimePlatformInfo)) - return RuntimePlatformInfo.Instance; return null; } From 30447ac033de41d1494866caa96323591548b3f3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 1 Jun 2022 01:18:35 -0400 Subject: [PATCH 004/197] Add tests for OnPlatform extensions --- .../OnPlatformExtensionTests.cs | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs new file mode 100644 index 0000000000..0583de691f --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs @@ -0,0 +1,263 @@ +using System; +using System.Globalization; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.PlatformSupport; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions; + +public class OnPlatformExtensionTests : XamlTestBase +{ + [Fact] + public void Should_Resolve_Default_Value() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.Unknown)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal("Hello World", textBlock.Text); + } + } + + [Fact] + public void Should_Resolve_Default_Value_From_Ctor() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.Unknown)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal("Hello World", textBlock.Text); + } + } + + [Theory] + [InlineData(OperatingSystemType.WinNT, "Im Windows")] + [InlineData(OperatingSystemType.OSX, "Im macOS")] + [InlineData(OperatingSystemType.Linux, "Im Linux")] + [InlineData(OperatingSystemType.Android, "Im Android")] + [InlineData(OperatingSystemType.iOS, "Im iOS")] + [InlineData(OperatingSystemType.Browser, "Im Browser")] + [InlineData(OperatingSystemType.Unknown, "Default value")] + public void Should_Resolve_Expected_Value_Per_Platform(OperatingSystemType currentPlatform, string expectedResult) + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(currentPlatform)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal(expectedResult, textBlock.Text); + } + } + + [Fact] + public void Should_Convert_Bcl_Type() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(50.1, border.Height); + } + } + + [Fact] + public void Should_Convert_Avalonia_Type() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(new Thickness(10, 8, 10, 8), border.Padding); + } + } + + [Fact] + public void Should_Allow_Nester_Markup_Extensions() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + + #ff506070 + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(Color.Parse("#ff506070"), ((ISolidColorBrush)border.Background!).Color); + } + } + + [Fact] + public void Should_Use_Converter_If_Provided() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + + + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(new Thickness(4), border.Padding); + } + } + + [Fact] + public void Should_Support_Xml_Syntax() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + + + + + + + + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(Color.Parse("#ff506070"), ((ISolidColorBrush)border.Background!).Color); + } + } + + [Fact] + public void Should_Support_Control_Inside_Xml_Syntax() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT)); + + var xaml = @" + + + +