From e22cfa842431565d77f446063324ee1d5c459833 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 14 Sep 2024 15:37:30 -0700 Subject: [PATCH] XamlX update: IProvideValueTarget fixes and nested-types support (#17021) * Update XamlX * Include nested types PR as well * Fix Designer.HostApp compilation on older targets --- samples/BindingDemo/MainWindow.xaml | 13 ++-- samples/BindingDemo/MainWindow.xaml.cs | 1 + samples/BindingDemo/TestItemView.xaml | 2 +- .../ViewModels/MainWindowViewModel.cs | 19 +++++ samples/BindingDemo/ViewModels/TestItem.cs | 16 ----- ...aXamlIlOptionMarkupExtensionTransformer.cs | 8 ++- ...mlIlTransformInstanceAttachedProperties.cs | 5 ++ .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- .../Avalonia.Designer.HostApp.csproj | 1 + .../Compiler/RoslynTypeSystem.cs | 9 +++ .../Xaml/ProvideValueTargetTests.cs | 71 +++++++++++++++++++ 11 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ProvideValueTargetTests.cs diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml index f14ad59cd5..ed23f68d91 100644 --- a/samples/BindingDemo/MainWindow.xaml +++ b/samples/BindingDemo/MainWindow.xaml @@ -12,9 +12,6 @@ - - - @@ -54,9 +51,9 @@ + Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/> + Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/> @@ -70,7 +67,7 @@ - + @@ -84,7 +81,7 @@ - + @@ -126,7 +123,7 @@ - + diff --git a/samples/BindingDemo/MainWindow.xaml.cs b/samples/BindingDemo/MainWindow.xaml.cs index eaa57e1f5f..57466fe581 100644 --- a/samples/BindingDemo/MainWindow.xaml.cs +++ b/samples/BindingDemo/MainWindow.xaml.cs @@ -9,6 +9,7 @@ namespace BindingDemo { public MainWindow() { + Resources["SharedItem"] = new MainWindowViewModel.TestItem() { StringValue = "shared" }; this.InitializeComponent(); this.DataContext = new MainWindowViewModel(); this.AttachDevTools(); diff --git a/samples/BindingDemo/TestItemView.xaml b/samples/BindingDemo/TestItemView.xaml index 6edade34b2..6eb59ffc83 100644 --- a/samples/BindingDemo/TestItemView.xaml +++ b/samples/BindingDemo/TestItemView.xaml @@ -2,7 +2,7 @@ xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:viewModels="using:BindingDemo.ViewModels" x:Class="BindingDemo.TestItemView" - x:DataType="viewModels:TestItem"> + x:DataType="viewModels:MainWindowViewModel+TestItem"> diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs index 18a7a01a69..f5f4f3531f 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -115,5 +115,24 @@ namespace BindingDemo.ViewModels { return BooleanFlag; } + + // Nested class, jsut so we can test it in XAML + public class TestItem : ViewModelBase + { + private string _stringValue = "String Value"; + private string _detail; + + public string StringValue + { + get { return _stringValue; } + set { this.RaiseAndSetIfChanged(ref this._stringValue, value); } + } + + public string Detail + { + get { return _detail; } + set { this.RaiseAndSetIfChanged(ref this._detail, value); } + } + } } } diff --git a/samples/BindingDemo/ViewModels/TestItem.cs b/samples/BindingDemo/ViewModels/TestItem.cs index 2f49a3c99f..2e11f63e95 100644 --- a/samples/BindingDemo/ViewModels/TestItem.cs +++ b/samples/BindingDemo/ViewModels/TestItem.cs @@ -2,21 +2,5 @@ using MiniMvvm; namespace BindingDemo.ViewModels { - public class TestItem : ViewModelBase - { - private string _stringValue = "String Value"; - private string _detail; - public string StringValue - { - get { return _stringValue; } - set { this.RaiseAndSetIfChanged(ref this._stringValue, value); } - } - - public string Detail - { - get { return _detail; } - set { this.RaiseAndSetIfChanged(ref this._detail, value); } - } - } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs index b6e2744125..104859d438 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOptionMarkupExtensionTransformer.cs @@ -334,13 +334,17 @@ internal class AvaloniaXamlIlOptionMarkupExtensionTransformer : IXamlAstTransfor public bool IsPrivate => false; public bool IsFamily => false; public bool IsStatic => false; + public bool ContainsGenericParameters => false; + public bool IsGenericMethod => false; + public bool IsGenericMethodDefinition => false; public IXamlType ReturnType => ExtensionNodeContainer.GetReturnType(); public IReadOnlyList Parameters { get; } public IXamlType DeclaringType { get; } public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) => throw new NotImplementedException(); - public IReadOnlyList CustomAttributes => Array.Empty(); - + public IReadOnlyList CustomAttributes => []; public IXamlParameterInfo GetParameterInfo(int index) => new AnonymousParameterInfo(Parameters[index], index); + public IReadOnlyList GenericParameters => []; + public IReadOnlyList GenericArguments => []; public void EmitCall(XamlEmitContext context, IXamlILEmitter codeGen) { diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index 34e8e0974b..5668fa2b73 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -168,6 +168,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public bool IsPrivate => false; public bool IsFamily => false; public bool IsStatic => true; + public bool ContainsGenericParameters => false; + public bool IsGenericMethod => false; + public bool IsGenericMethodDefinition => false; public string Name { get; protected set; } public IXamlType DeclaringType { get; } public IXamlMethod MakeGenericMethod(IReadOnlyList typeArguments) @@ -181,6 +184,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IReadOnlyList CustomAttributes => DeclaringType.CustomAttributes; public IXamlParameterInfo GetParameterInfo(int index) => new AnonymousParameterInfo(Parameters[index], index); + public IReadOnlyList GenericParameters => []; + public IReadOnlyList GenericArguments => []; public void EmitCall(IXamlILEmitter emitter) { diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 14fed0cb06..ab84721302 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 14fed0cb0666a9b3f71cb635cf61eb6010d4ff64 +Subproject commit ab84721302d6ed2b8b65315f3c54217693640348 diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index 4255813c5f..e4f78f304f 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -16,6 +16,7 @@ + diff --git a/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs b/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs index d2034d96ae..7e0ab123f4 100644 --- a/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs +++ b/src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs @@ -297,6 +297,15 @@ internal class RoslynMethod : IXamlMethod public bool IsFamily => _symbol.DeclaredAccessibility == Accessibility.Protected; public bool IsStatic => false; + public bool ContainsGenericParameters => _symbol.TypeParameters.Any(); + public bool IsGenericMethod => _symbol.IsGenericMethod; + public bool IsGenericMethodDefinition => _symbol.IsDefinition && _symbol.IsGenericMethod; + + public IReadOnlyList GenericParameters => throw new NotImplementedException(); + + public IReadOnlyList GenericArguments => _symbol.TypeArguments + .Select(ga => new RoslynType((INamedTypeSymbol)ga, _assembly)) + .ToArray(); public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ProvideValueTargetTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ProvideValueTargetTests.cs new file mode 100644 index 0000000000..6df31f55f2 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ProvideValueTargetTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Data; +using Avalonia.Data.Core; +using Avalonia.Markup.Xaml.MarkupExtensions; +using Avalonia.Media; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml; + +public class ProvideValueTargetTests : XamlTestBase +{ + [Fact] + public void ProvideValueTarget_Has_Correct_Targets_Set() + { + using var _ = UnitTestApplication.Start(TestServices.StyledWindow); + + var capturedTargets = new CapturedTargets(); + AvaloniaLocator.CurrentMutable.BindToSelf(capturedTargets); + + AvaloniaRuntimeXamlLoader.Load(@" + + + + +"); + + Assert.Collection(capturedTargets.Targets, + item => + { + Assert.IsType(item.TargetObject); + Assert.Equal(TextElement.ForegroundProperty, item.TargetProperty); + }, + item => + { + Assert.IsAssignableFrom(item.TargetObject); + var prop = Assert.IsType(item.TargetProperty); + Assert.Equal(nameof(Binding.Source), prop.Name); + }, + item => + { + Assert.IsType(item.TargetObject); + Assert.Equal(TextBlock.BackgroundProperty, item.TargetProperty); + }); + } +} + +public class CapturedTargets +{ + public List<(object TargetObject, object TargetProperty)> Targets { get; } = []; +} + +public class CapturingTargetsMarkupExtension +{ + public object ProvideValue(IServiceProvider serviceProvider) + { + var parentsProvider = serviceProvider.GetRequiredService(); + var capturedTargets = AvaloniaLocator.Current.GetRequiredService(); + capturedTargets.Targets.Add((parentsProvider.TargetObject, parentsProvider.TargetProperty)); + return Brushes.DarkViolet; + } +}