Browse Source

XamlX update: IProvideValueTarget fixes and nested-types support (#17021)

* Update XamlX

* Include nested types PR as well

* Fix Designer.HostApp compilation on older targets
Max Katz 1 year ago
parent
commit
e22cfa8424

+ 5 - 8
samples/BindingDemo/MainWindow.xaml

@@ -12,9 +12,6 @@
       <Setter Property="FontSize" Value="18"/>
     </Style>
   </Window.Styles>
-  <Window.Resources>
-    <vm:TestItem x:Key="SharedItem" StringValue="shared" />
-  </Window.Resources>
   
   <TabControl>
     <TabItem Header="Basic">
@@ -54,9 +51,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}"/>
+                     Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/>
             <TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
-                     Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
+                     Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay, DataType=vm:MainWindowViewModel+TestItem}"/>
           </StackPanel>
           <StackPanel Margin="18" Spacing="4" Width="200" HorizontalAlignment="Left">
             <TextBlock FontSize="16" Text="Scheduler"/>
@@ -70,7 +67,7 @@
     <TabItem Header="ListBox">
       <StackPanel Orientation="Horizontal">
         <StackPanel.DataTemplates>
-          <DataTemplate DataType="vm:TestItem">
+          <DataTemplate DataType="vm:MainWindowViewModel+TestItem">
             <TextBlock Text="{Binding StringValue}"/>
           </DataTemplate>
         </StackPanel.DataTemplates>
@@ -84,7 +81,7 @@
         </StackPanel>
         <ContentControl Content="{ReflectionBinding Selection.SelectedItems[0]}">
           <ContentControl.DataTemplates>
-            <DataTemplate DataType="vm:TestItem">
+            <DataTemplate DataType="vm:MainWindowViewModel+TestItem">
               <local:TestItemView></local:TestItemView>
             </DataTemplate>
           </ContentControl.DataTemplates>
@@ -126,7 +123,7 @@
       </TabItem.Resources>
       <StackPanel>
         <!-- Tests for #10856  -->
-        <TextBlock Text="{local:GenericMarkupExtension, Value=Red, x:TypeArguments=Color}"/>
+        <TextBlock Text="{local:GenericMarkupExtension Value=Red, x:TypeArguments=Color}"/>
         <TextBlock HorizontalAlignment="Left"
                    Text="{Binding $self.Background, Converter={StaticResource BrushConverter}}">
           <TextBlock.Background>

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

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

+ 19 - 0
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); }
+            }
+        }
     }
 }

+ 0 - 16
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); }
-        }
-    }
 }

+ 6 - 2
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<IXamlType> Parameters { get; }
         public IXamlType DeclaringType { get; }
         public IXamlMethod MakeGenericMethod(IReadOnlyList<IXamlType> typeArguments) => throw new NotImplementedException();
-        public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => Array.Empty<IXamlCustomAttribute>();
-
+        public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => [];
         public IXamlParameterInfo GetParameterInfo(int index) => new AnonymousParameterInfo(Parameters[index], index);
+        public IReadOnlyList<IXamlType> GenericParameters => [];
+        public IReadOnlyList<IXamlType> GenericArguments => [];
 
         public void EmitCall(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
         {

+ 5 - 0
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<IXamlType> typeArguments) 
@@ -181,6 +184,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
 
                 public IReadOnlyList<IXamlCustomAttribute> CustomAttributes => DeclaringType.CustomAttributes;
                 public IXamlParameterInfo GetParameterInfo(int index) => new AnonymousParameterInfo(Parameters[index], index);
+                public IReadOnlyList<IXamlType> GenericParameters => [];
+                public IReadOnlyList<IXamlType> GenericArguments => [];
 
                 public void EmitCall(IXamlILEmitter emitter)
                 {

+ 1 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@@ -1 +1 @@
-Subproject commit 14fed0cb0666a9b3f71cb635cf61eb6010d4ff64
+Subproject commit ab84721302d6ed2b8b65315f3c54217693640348

+ 1 - 0
src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj

@@ -16,6 +16,7 @@
     <Compile Include="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\CompilerDynamicDependencies.cs" />
     <Compile Include="..\..\..\src\Avalonia.Base\Compatibility\NullableAttributes.cs" Link="Compatibility\NullableAttributes.cs" />
     <Compile Include="..\..\..\src\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="Compatibility\TrimmingAttributes.cs" />
+    <Compile Include="..\..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" />
     <Compile Include="..\..\..\src\Shared\IsExternalInit.cs" Link="Compatibility\IsExternalInit.cs" />
   </ItemGroup>
   <Import Project="..\..\..\src\Markup\Avalonia.Markup.Xaml.Loader\IncludeXamlIlSre.props" />

+ 9 - 0
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<IXamlType> GenericParameters => throw new NotImplementedException();
+
+    public IReadOnlyList<IXamlType> GenericArguments => _symbol.TypeArguments
+        .Select(ga => new RoslynType((INamedTypeSymbol)ga, _assembly))
+        .ToArray();
 
     public IXamlType ReturnType => new RoslynType((INamedTypeSymbol) _symbol.ReturnType, _assembly);
 

+ 71 - 0
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(@"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+        xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'
+        Foreground='{local:CapturingTargetsMarkup}'
+        x:CompileBindings='True'>
+
+  <TextBlock Tag='{Binding Source={local:CapturingTargetsMarkup}}'
+             Background='{local:CapturingTargetsMarkup}' />
+
+</Window>");
+
+        Assert.Collection(capturedTargets.Targets,
+            item =>
+            {
+                Assert.IsType<Window>(item.TargetObject);
+                Assert.Equal(TextElement.ForegroundProperty, item.TargetProperty);
+            },
+            item =>
+            {
+                Assert.IsAssignableFrom<CompiledBindingExtension>(item.TargetObject);
+                var prop = Assert.IsType<ClrPropertyInfo>(item.TargetProperty);
+                Assert.Equal(nameof(Binding.Source), prop.Name);
+            },
+            item =>
+            {
+                Assert.IsType<TextBlock>(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<IProvideValueTarget>();
+        var capturedTargets = AvaloniaLocator.Current.GetRequiredService<CapturedTargets>();
+        capturedTargets.Targets.Add((parentsProvider.TargetObject, parentsProvider.TargetProperty));
+        return Brushes.DarkViolet;
+    }
+}