diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index ac52c148be..74eaee5e07 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -53,14 +53,18 @@ namespace Avalonia.Build.Tasks
var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(clrPropertiesDef);
-
+ var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
+ TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
+ asm.MainModule.Types.Add(indexerAccessorClosure);
+
var xamlLanguage = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
typeSystem.TargetAssembly,
xamlLanguage,
XamlIlXmlnsMappings.Resolve(typeSystem, xamlLanguage),
AvaloniaXamlIlLanguage.CustomValueConverter,
- new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)));
+ new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
+ new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)));
var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext",
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 3cc5ce74cb..b2c4c33ed5 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -17,9 +17,10 @@
+
-
+
@@ -69,6 +70,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs
new file mode 100644
index 0000000000..718c695c45
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/ArrayElementPlugin.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Data;
+using Avalonia.Data.Core.Plugins;
+
+namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
+{
+ class ArrayElementPlugin : IPropertyAccessorPlugin
+ {
+ private readonly int[] _indices;
+ private readonly Type _elementType;
+
+ public ArrayElementPlugin(int[] indices, Type elementType)
+ {
+ _indices = indices;
+ _elementType = elementType;
+ }
+
+ public bool Match(object obj, string propertyName)
+ {
+ throw new InvalidOperationException("The ArrayElementPlugin does not support dynamic matching");
+ }
+
+ public IPropertyAccessor Start(WeakReference reference, string propertyName)
+ {
+ return new Accessor(reference, _indices, _elementType);
+ }
+
+ class Accessor : PropertyAccessorBase
+ {
+ private readonly int[] _indices;
+ private readonly WeakReference _reference;
+
+ public Accessor(WeakReference reference, int[] indices, Type elementType)
+ {
+ _reference = reference;
+ _indices = indices;
+ PropertyType = elementType;
+ }
+
+ public override Type PropertyType { get; }
+
+ public override object Value => _reference.Target is Array arr ? arr.GetValue(_indices) : null;
+
+ public override bool SetValue(object value, BindingPriority priority)
+ {
+ if (_reference.Target is Array arr)
+ {
+ arr.SetValue(value, _indices);
+ return true;
+ }
+ return false;
+ }
+
+ protected override void SubscribeCore()
+ {
+ PublishValue(Value);
+ }
+
+ protected override void UnsubscribeCore()
+ {
+ }
+ }
+ }
+
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
index ce3154f349..b45fde7798 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
@@ -32,7 +32,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
node = new LogicalNotNode();
break;
case PropertyElement prop:
- node = new PropertyAccessorNode(prop.Property.Name, enableValidation, new PropertyInfoAccessorPlugin(prop.Property));
+ node = new PropertyAccessorNode(prop.Property.Name, enableValidation, new PropertyInfoAccessorPlugin(prop.Property, prop.AccessorFactory));
+ break;
+ case ArrayElementPathElement arr:
+ node = new PropertyAccessorNode(CommonPropertyNames.IndexerName, enableValidation, new ArrayElementPlugin(arr.Indices, arr.ElementType));
break;
case AncestorPathElement ancestor:
node = new FindAncestorNode(ancestor.AncestorType, ancestor.Level);
@@ -69,9 +72,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
- public CompiledBindingPathBuilder Property(INotifyingPropertyInfo info)
+ public CompiledBindingPathBuilder Property(IPropertyInfo info, Func accessorFactory)
{
- _elements.Add(new PropertyElement(info));
+ _elements.Add(new PropertyElement(info, accessorFactory));
return this;
}
@@ -105,6 +108,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
+ public CompiledBindingPathBuilder ArrayElement(int[] indices, Type elementType)
+ {
+ _elements.Add(new ArrayElementPathElement(indices, elementType));
+ return this;
+ }
+
public CompiledBindingPath Build() => new CompiledBindingPath(_elements);
}
@@ -121,12 +130,15 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
internal class PropertyElement : ICompiledBindingPathElement
{
- public PropertyElement(INotifyingPropertyInfo property)
+ public PropertyElement(IPropertyInfo property, Func accessorFactory)
{
Property = property;
+ AccessorFactory = accessorFactory;
}
- public INotifyingPropertyInfo Property { get; }
+ public IPropertyInfo Property { get; }
+
+ public Func AccessorFactory { get; }
}
internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement
@@ -176,4 +188,16 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
public INameScope NameScope { get; }
public string Name { get; }
}
+
+ internal class ArrayElementPathElement : ICompiledBindingPathElement
+ {
+ public ArrayElementPathElement(int[] indices, Type elementType)
+ {
+ Indices = indices;
+ ElementType = elementType;
+ }
+
+ public int[] Indices { get; }
+ public Type ElementType { get; }
+ }
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/NotifyingPropertyInfoHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/NotifyingPropertyInfoHelpers.cs
deleted file mode 100644
index 0af201ade8..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/NotifyingPropertyInfoHelpers.cs
+++ /dev/null
@@ -1,255 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Text;
-using Avalonia.Data.Core;
-using Avalonia.Reactive;
-using Avalonia.Utilities;
-
-namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
-{
- public static class NotifyingPropertyInfoHelpers
- {
- public static INotifyingPropertyInfo CreateINPCPropertyInfo(IPropertyInfo basePropertyInfo)
- => new INPCPropertyInfo(basePropertyInfo);
-
- public static INotifyingPropertyInfo CreateAvaloniaPropertyInfo(AvaloniaProperty property)
- => new AvaloniaPropertyInfo(property);
-
- public static INotifyingPropertyInfo CreateIndexerPropertyInfo(IPropertyInfo basePropertyInfo, int argument)
- => new IndexerInfo(basePropertyInfo, argument);
- }
-
- public interface INotifyingPropertyInfo : IPropertyInfo
- {
- void OnPropertyChanged(object target, EventHandler handler);
- void RemoveListener(object target, EventHandler handler);
- }
-
- internal abstract class NotifyingPropertyInfoBase : INotifyingPropertyInfo
- {
- private readonly IPropertyInfo _base;
- protected readonly ConditionalWeakTable