diff --git a/.ncrunch/Avalonia.FreeDesktop.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.FreeDesktop.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.FreeDesktop.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.FreeDesktop.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.FreeDesktop.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.FreeDesktop.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject index 02eb0d211e..bc1af9a143 100644 --- a/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject @@ -1,5 +1,8 @@  + + ..\Avalonia.Themes.Fluent\Strings\InvariantResources.xaml + False \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject index 02eb0d211e..bc1af9a143 100644 --- a/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject @@ -1,5 +1,8 @@  + + ..\Avalonia.Themes.Fluent\Strings\InvariantResources.xaml + False \ No newline at end of file diff --git a/.ncrunch/Avalonia.X11.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.X11.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.X11.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.X11.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.X11.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.X11.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/src/Avalonia.Base/Data/Core/TargetTypeConverter.cs b/src/Avalonia.Base/Data/Core/TargetTypeConverter.cs index f633094860..7a4b0544bd 100644 --- a/src/Avalonia.Base/Data/Core/TargetTypeConverter.cs +++ b/src/Avalonia.Base/Data/Core/TargetTypeConverter.cs @@ -5,6 +5,7 @@ using Avalonia.Data.Converters; using System.Windows.Input; using Avalonia.Utilities; using static Avalonia.Utilities.TypeUtilities; +using System.ComponentModel; namespace Avalonia.Data.Core; @@ -65,6 +66,40 @@ internal abstract class TargetTypeConverter return true; } +#pragma warning disable IL2026 +#pragma warning disable IL2067 + // TODO: TypeConverters are not trimming friendly in some edge cases, we probably need + // to make compiled bindings emit conversion code at compile-time. + var toTypeConverter = TypeDescriptor.GetConverter(t); + var from = value.GetType(); + + if (toTypeConverter.CanConvertFrom(from)) + { + result = toTypeConverter.ConvertFrom(null, culture, value); + return true; + } + + var fromTypeConverter = TypeDescriptor.GetConverter(from); + + if (fromTypeConverter.CanConvertTo(t)) + { + result = fromTypeConverter.ConvertTo(null, culture, value, t); + return true; + } + + // TODO: This requires reflection: we probably need to make compiled bindings emit + // conversion code at compile-time. + if (FindTypeConversionOperatorMethod( + value.GetType(), + t, + OperatorType.Implicit | OperatorType.Explicit) is { } cast) + { + result = cast.Invoke(null, new[] { value }); + return true; + } +#pragma warning restore IL2067 +#pragma warning restore IL2026 + if (value is IConvertible convertible) { try @@ -79,17 +114,6 @@ internal abstract class TargetTypeConverter } } - // TODO: This requires reflection: we probably need to make compiled bindings emit - // conversion code at compile-time. - if (FindTypeConversionOperatorMethod( - value.GetType(), - t, - OperatorType.Implicit | OperatorType.Explicit) is { } cast) - { - result = cast.Invoke(null, new[] { value }); - return true; - } - result = null; return false; } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index afc4a36fea..f724bf5f1c 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -6,6 +6,8 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Data; using Avalonia.Data.Converters; +using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; @@ -94,6 +96,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data Assert.Equal("Foo,Bar", target.Text); } } + + [Fact] + public void Can_Bind_Brush_to_Hex_String() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)window.Content; + window.DataContext = new { HexString = "#ff0000" }; + + window.ApplyTemplate(); + + var brush = Assert.IsType(border.Background); + Assert.Equal(Colors.Red, brush.Color); + } + } } public class ConcatConverter : IMultiValueConverter diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 7c7b8bcb00..c2cec8cf79 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -24,6 +24,7 @@ using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings; using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Metadata; using Avalonia.UnitTests; using Xunit; @@ -2022,6 +2023,30 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Can_Bind_Brush_To_Hex_String() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = $@" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + var dataContext = new TestData { StringProperty = "#ff0000" }; + window.DataContext = dataContext; + + var brush = Assert.IsType(textBlock!.Background); + Assert.Equal(Colors.Red, brush.Color); + } + } + static void Throws(string type, Action cb) { try