From 4df24bc59365c5c292909bd9788345e0ca1bbfaf Mon Sep 17 00:00:00 2001 From: Maxwell Katz Date: Sun, 9 Mar 2025 00:13:30 +0900 Subject: [PATCH] Support complex types in datatype (#18379) * Fix XamlTypeExtensionNode not being handled on the x:DataType transformer * Add testt with complex DataType * Make vm:MainWindowViewModel+TestItem nested type generic on BindingDemo --- samples/BindingDemo/BindingDemo.csproj | 1 + samples/BindingDemo/MainWindow.xaml | 13 ++-- samples/BindingDemo/MainWindow.xaml.cs | 2 +- samples/BindingDemo/TestItemView.xaml | 4 +- .../ViewModels/MainWindowViewModel.cs | 22 +++---- ...valoniaXamlIlDataContextTypeTransformer.cs | 6 +- .../CompiledBindingExtensionTests.cs | 66 ++++++++++++++++++- 7 files changed, 90 insertions(+), 24 deletions(-) diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index faeb643d8a..33c6aa1440 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -2,6 +2,7 @@ Exe $(AvsCurrentTargetFramework) + diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml index ed23f68d91..9d68c8da8a 100644 --- a/samples/BindingDemo/MainWindow.xaml +++ b/samples/BindingDemo/MainWindow.xaml @@ -3,6 +3,7 @@ x:Class="BindingDemo.MainWindow" xmlns:vm="using:BindingDemo.ViewModels" xmlns:local="using:BindingDemo" + xmlns:system="clr-namespace:System;assembly=System.Runtime" Title="AvaloniaUI Bindings Test" Width="800" Height="600" @@ -29,7 +30,7 @@ - + @@ -51,9 +52,9 @@ + Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/> + Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/> @@ -67,8 +68,8 @@ - - + + @@ -81,7 +82,7 @@ - + diff --git a/samples/BindingDemo/MainWindow.xaml.cs b/samples/BindingDemo/MainWindow.xaml.cs index 57466fe581..36d4feb108 100644 --- a/samples/BindingDemo/MainWindow.xaml.cs +++ b/samples/BindingDemo/MainWindow.xaml.cs @@ -9,7 +9,7 @@ namespace BindingDemo { public MainWindow() { - Resources["SharedItem"] = new MainWindowViewModel.TestItem() { StringValue = "shared" }; + Resources["SharedItem"] = new MainWindowViewModel.TestItem() { Value = "shared" }; this.InitializeComponent(); this.DataContext = new MainWindowViewModel(); this.AttachDevTools(); diff --git a/samples/BindingDemo/TestItemView.xaml b/samples/BindingDemo/TestItemView.xaml index 6eb59ffc83..fcf3a80ceb 100644 --- a/samples/BindingDemo/TestItemView.xaml +++ b/samples/BindingDemo/TestItemView.xaml @@ -2,9 +2,9 @@ xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:viewModels="using:BindingDemo.ViewModels" x:Class="BindingDemo.TestItemView" - x:DataType="viewModels:MainWindowViewModel+TestItem"> + x:DataType="{x:Type viewModels:MainWindowViewModel+TestItem, x:TypeArguments=x:String}"> - + diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs index f5f4f3531f..eb1a007695 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -23,14 +23,14 @@ namespace BindingDemo.ViewModels public MainWindowViewModel() { - Items = new ObservableCollection( - Enumerable.Range(0, 20).Select(x => new TestItem + Items = new ObservableCollection>( + Enumerable.Range(0, 20).Select(x => new TestItem { - StringValue = "Item " + x, + Value = "Item " + x, Detail = "Item " + x + " details", })); - Selection = new SelectionModel { SingleSelect = false }; + Selection = new SelectionModel> { SingleSelect = false }; ShuffleItems = MiniCommand.Create(() => { @@ -58,8 +58,8 @@ namespace BindingDemo.ViewModels .Select(x => DateTimeOffset.Now); } - public ObservableCollection Items { get; } - public SelectionModel Selection { get; } + public ObservableCollection> Items { get; } + public SelectionModel> Selection { get; } public MiniCommand ShuffleItems { get; } public string BooleanString @@ -117,15 +117,15 @@ namespace BindingDemo.ViewModels } // Nested class, jsut so we can test it in XAML - public class TestItem : ViewModelBase + public class TestItem : ViewModelBase { - private string _stringValue = "String Value"; + private T _value; private string _detail; - public string StringValue + public T Value { - get { return _stringValue; } - set { this.RaiseAndSetIfChanged(ref this._stringValue, value); } + get { return _value; } + set { this.RaiseAndSetIfChanged(ref this._value, value); } } public string Detail diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 6b4f32bce0..8632ea6ee0 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -44,7 +44,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { on.Children.RemoveAt(i); i--; - if (directive.Values[0] is XamlAstTextNode text) + if (directive.Values[0] is XamlTypeExtensionNode typeNode) + { + directiveDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, typeNode.Value.GetClrType()); + } + else if (directive.Values[0] is XamlAstTextNode text) { directiveDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, TypeReferenceResolver.ResolveType(context, text.Text, isMarkupExtension: false, text, strict: true).Type); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 727ebeab40..61b3024f65 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -244,7 +244,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions window.DataContext = dataContext; - Assert.Equal(dataContext.TaskProperty.Result, textBlock.Text); + Assert.Equal("foobar", textBlock.Text); } } @@ -1320,6 +1320,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportsParentInPathWithTypeAndLevelFilter() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = (Window)AvaloniaRuntimeXamlLoader.Load(@" + + + + + + +"); + var textBlock = window.GetControl("textBlock"); + + Assert.Equal("p1", textBlock.Text); + } + } + [Fact] public void SupportConverterWithParameter() { @@ -1936,7 +1958,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions [Theory] [InlineData(null, "Not called")] [InlineData("A", "Do A")] - public void Binding_Method_With_Parameter_To_Command_CanExecute(object commandParameter, string result) + public void Binding_Method_With_Parameter_To_Command_CanExecute(object? commandParameter, string result) { using (UnitTestApplication.Start(TestServices.StyledWindow)) { @@ -2213,6 +2235,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Resolves_Nested_Generic_DataTypes() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = (Window)AvaloniaRuntimeXamlLoader.Load(@" + + + + +"); + var textBlock = window.GetControl("textBlock"); + + var dataContext = new TestDataContext + { + NestedGenericString = new TestDataContext.NestedGeneric + { + Value = "10" + } + }; + + window.DataContext = dataContext.NestedGenericString; + + Assert.Equal(dataContext.NestedGenericString.Value, textBlock.Text); + } + } + static void Throws(string type, Action cb) { try @@ -2303,7 +2356,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public INonIntegerIndexerDerived NonIntegerIndexerInterfaceProperty => NonIntegerIndexerProperty; - string IHasExplicitProperty.ExplicitProperty => "Hello"; + public NestedGeneric? NestedGenericString { get; init; } + + string IHasExplicitProperty.ExplicitProperty => "Hello"; public string ExplicitProperty => "Bye"; @@ -2328,6 +2383,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } } + + public class NestedGeneric + { + public T Value { get; set; } + } } public class ListItemCollectionView : List