Skip to content

Commit

Permalink
Make complied bindings use TypeConverters again. (#14767)
Browse files Browse the repository at this point in the history
* Update ncrunch config.

* Add tests for converting strings to brushes.

* Make complied bindings use TypeConverters.

Certain conversions rely on type converters, which were disabled in compiled bindings since #13970 due to warnings that type converters are not trimming friendly.

Ideally we'd be generating the type conversion logic in the XAML compiler, but in reality the problem with type converters and trimming is limited to type converters with generics, which is an edge case.

For the moment re-enable the usage of type converters in compiled bindings until we implement generating the conversion code in the XAML compiler.
  • Loading branch information
grokys authored Feb 29, 2024
1 parent 8adb67a commit 20ca1bf
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .ncrunch/Avalonia.FreeDesktop.net6.0.v3.ncrunchproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>
3 changes: 3 additions & 0 deletions .ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<ProjectConfiguration>
<Settings>
<AdditionalFilesToIncludeForProject>
<Value>..\Avalonia.Themes.Fluent\Strings\InvariantResources.xaml</Value>
</AdditionalFilesToIncludeForProject>
<InstrumentOutputAssembly>False</InstrumentOutputAssembly>
</Settings>
</ProjectConfiguration>
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<ProjectConfiguration>
<Settings>
<AdditionalFilesToIncludeForProject>
<Value>..\Avalonia.Themes.Fluent\Strings\InvariantResources.xaml</Value>
</AdditionalFilesToIncludeForProject>
<InstrumentOutputAssembly>False</InstrumentOutputAssembly>
</Settings>
</ProjectConfiguration>
5 changes: 5 additions & 0 deletions .ncrunch/Avalonia.X11.net6.0.v3.ncrunchproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>
5 changes: 5 additions & 0 deletions .ncrunch/Avalonia.X11.netstandard2.0.v3.ncrunchproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>
46 changes: 35 additions & 11 deletions src/Avalonia.Base/Data/Core/TargetTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Windows.Input;
using Avalonia.Utilities;
using static Avalonia.Utilities.TypeUtilities;
using System.ComponentModel;

namespace Avalonia.Data.Core;

Expand Down Expand Up @@ -65,6 +66,40 @@ public override bool TryConvert(object? value, Type type, CultureInfo culture, o
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
Expand All @@ -79,17 +114,6 @@ public override bool TryConvert(object? value, Type type, CultureInfo culture, o
}
}

// 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;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
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;
Expand Down Expand Up @@ -94,6 +96,28 @@ public void MultiBinding_TemplatedParent_Works()
Assert.Equal("Foo,Bar", target.Text);
}
}

[Fact]
public void Can_Bind_Brush_to_Hex_String()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Data;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Border Background='{Binding HexString}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var border = (Border)window.Content;
window.DataContext = new { HexString = "#ff0000" };

window.ApplyTemplate();

var brush = Assert.IsType<ImmutableSolidColorBrush>(border.Background);
Assert.Equal(Colors.Red, brush.Color);
}
}
}

public class ConcatConverter : IMultiValueConverter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand Down Expand Up @@ -2022,6 +2023,30 @@ public void Can_Use_Implicit_Conversions()
}
}

[Fact]
public void Can_Bind_Brush_To_Hex_String()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = $@"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'
x:DataType='local:TestData'
x:CompileBindings='True'>
<TextBlock Name='textBlock' Background='{{Binding StringProperty}}'/>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");

var dataContext = new TestData { StringProperty = "#ff0000" };
window.DataContext = dataContext;

var brush = Assert.IsType<ImmutableSolidColorBrush>(textBlock!.Background);
Assert.Equal(Colors.Red, brush.Color);
}
}

static void Throws(string type, Action cb)
{
try
Expand Down

0 comments on commit 20ca1bf

Please sign in to comment.