소스 검색

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
Maxwell Katz 7 달 전
부모
커밋
82329882ac

+ 1 - 0
samples/BindingDemo/BindingDemo.csproj

@@ -2,6 +2,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
+<!--    <AvaloniaXamlIlDebuggerLaunch>true</AvaloniaXamlIlDebuggerLaunch>-->
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />

+ 7 - 6
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 @@
           </StackPanel>
           <StackPanel Margin="18" Spacing="4" Width="200">
             <TextBlock FontSize="16" Text="Collection Bindings"/>
-            <TextBox Watermark="Items[1].StringValue" UseFloatingWatermark="True" Text="{Binding Path=Items[1].StringValue}"/>
+            <TextBox Watermark="Items[1].Value" UseFloatingWatermark="True" Text="{Binding Path=Items[1].Value}"/>
             <Button Command="{Binding ShuffleItems}">Shuffle</Button>
           </StackPanel>
           <StackPanel Margin="18" Spacing="4" Width="200">
@@ -51,9 +52,9 @@
             <TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True" 
                      Text="{Binding #first.Text, Mode=TwoWay}"/>
             <TextBox Watermark="Value of SharedItem.StringValue" UseFloatingWatermark="True"
-                     Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/>
+                     Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/>
             <TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
-                     Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/>
+                     Text="{Binding Value, Source={StaticResource SharedItem}, Mode=TwoWay, DataType={x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}}"/>
           </StackPanel>
           <StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
             <TextBlock FontSize="16" Text="Scheduler"/>
@@ -67,8 +68,8 @@
     <TabItem Header="ListBox">
       <StackPanel Orientation="Horizontal">
         <StackPanel.DataTemplates>
-          <DataTemplate DataType="vm:MainWindowViewModel+TestItem">
-            <TextBlock Text="{Binding StringValue}"/>
+          <DataTemplate x:DataType="{x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}">
+            <TextBlock Text="{Binding Value}"/>
           </DataTemplate>
         </StackPanel.DataTemplates>
         <StackPanel Margin="18" Spacing="4" Width="200">
@@ -81,7 +82,7 @@
         </StackPanel>
         <ContentControl Content="{ReflectionBinding Selection.SelectedItems[0]}">
           <ContentControl.DataTemplates>
-            <DataTemplate DataType="vm:MainWindowViewModel+TestItem">
+            <DataTemplate x:DataType="{x:Type vm:MainWindowViewModel+TestItem, x:TypeArguments=x:String}">
               <local:TestItemView></local:TestItemView>
             </DataTemplate>
           </ContentControl.DataTemplates>

+ 1 - 1
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<string>() { Value = "shared" };
             this.InitializeComponent();
             this.DataContext = new MainWindowViewModel();
             this.AttachDevTools();

+ 2 - 2
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}">
   <StackPanel>
-    <TextBlock Classes="h1" Text="{Binding StringValue}"/>
+    <TextBlock Classes="h1" Text="{Binding Value}"/>
     <TextBox Text="{Binding Detail}" AcceptsReturn="True"/>
   </StackPanel>
 </UserControl>

+ 11 - 11
samples/BindingDemo/ViewModels/MainWindowViewModel.cs

@@ -23,14 +23,14 @@ namespace BindingDemo.ViewModels
 
         public MainWindowViewModel()
         {
-            Items = new ObservableCollection<TestItem>(
-                Enumerable.Range(0, 20).Select(x => new TestItem
+            Items = new ObservableCollection<TestItem<string>>(
+                Enumerable.Range(0, 20).Select(x => new TestItem<string>
                 {
-                    StringValue = "Item " + x,
+                    Value = "Item " + x,
                     Detail = "Item " + x + " details",
                 }));
 
-            Selection = new SelectionModel<TestItem> { SingleSelect = false };
+            Selection = new SelectionModel<TestItem<string>> { SingleSelect = false };
 
             ShuffleItems = MiniCommand.Create(() =>
             {
@@ -58,8 +58,8 @@ namespace BindingDemo.ViewModels
                 .Select(x => DateTimeOffset.Now);
         }
 
-        public ObservableCollection<TestItem> Items { get; }
-        public SelectionModel<TestItem> Selection { get; }
+        public ObservableCollection<TestItem<string>> Items { get; }
+        public SelectionModel<TestItem<string>> 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<T> : 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

+ 5 - 1
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);

+ 38 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@@ -2369,6 +2369,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             }
         }
 
+        [Fact]
+        public void Resolves_Nested_Generic_DataTypes()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var window = (Window)AvaloniaRuntimeXamlLoader.Load(@"
+<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='{x:Type local:TestDataContext+NestedGeneric, x:TypeArguments=x:String}'
+        x:Name='MyWindow'>
+    <Panel>
+        <TextBlock Text='{CompiledBinding Value}' Name='textBlock' />
+    </Panel>
+</Window>");
+                var textBlock = window.GetControl<TextBlock>("textBlock");
+
+                var dataContext = new TestDataContext
+                {
+                    NestedGenericString = new TestDataContext.NestedGeneric<string>
+                    {
+                        Value = "10"
+                    }
+                };
+
+                window.DataContext = dataContext.NestedGenericString;
+
+                Assert.Equal(dataContext.NestedGenericString.Value, textBlock.Text);
+            }
+        }
+
         static void Throws(string type, Action cb)
         {
             try
@@ -2459,6 +2490,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
 
         public INonIntegerIndexerDerived NonIntegerIndexerInterfaceProperty => NonIntegerIndexerProperty;
 
+        public NestedGeneric<string>? NestedGenericString { get; init; }
+        
         string IHasExplicitProperty.ExplicitProperty => "Hello"; 
 
         public string ExplicitProperty => "Bye";
@@ -2484,6 +2517,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
                 }
             }
         }
+
+        public class NestedGeneric<T>
+        {
+            public T Value { get; set; }
+        }
     }
 
     public class ListItemCollectionView<T> : List<T>