Browse Source

Implement the runtime side of the "method->Delegate" binding conversion for compiled bindings.

pull/7246/head
Jeremy Koritzinsky 4 years ago
parent
commit
e7afc15ca7
No known key found for this signature in database GPG Key ID: 25A7D1C8126B7A16
  1. 26
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  2. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  3. 23
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  4. 74
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs

26
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -22,19 +22,9 @@ namespace Avalonia.Data.Core.Plugins
var method = GetFirstMethodWithName(instance.GetType(), methodName);
if (method != null)
if (method is not null)
{
var parameters = method.GetParameters();
if (parameters.Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{
var exception = new ArgumentException(
"Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.",
nameof(methodName));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
return new Accessor(reference, method, parameters);
return new Accessor(reference, method);
}
else
{
@ -82,7 +72,7 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase
{
public Accessor(WeakReference<object?> reference, MethodInfo method, ParameterInfo[] parameters)
public Accessor(WeakReference<object?> reference, MethodInfo method)
{
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
@ -90,10 +80,13 @@ namespace Avalonia.Data.Core.Plugins
var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void);
var parameters = method.GetParameters();
var signatureTypeCount = (hasReturn ? 1 : 0) + parameters.Length;
var paramTypes = new Type[signatureTypeCount];
for (var i = 0; i < parameters.Length; i++)
{
ParameterInfo parameter = parameters[i];
@ -105,13 +98,10 @@ namespace Avalonia.Data.Core.Plugins
{
paramTypes[paramTypes.Length - 1] = returnType;
PropertyType = Expression.GetFuncType(paramTypes);
}
else
{
PropertyType = Expression.GetActionType(paramTypes);
}
PropertyType = Expression.GetDelegateType(paramTypes);
if (method.IsStatic)
{
Value = method.CreateDelegate(PropertyType);

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -20,6 +20,7 @@
<Compile Include="MarkupExtensions\CompiledBindings\ArrayElementPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\CompiledBindingPath.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\FindVisualAncestorNode.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\MethodAccessorPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\ObservableStreamPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorFactory.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorPlugin.cs" />

23
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
@ -36,6 +37,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
case PropertyElement prop:
node = new PropertyAccessorNode(prop.Property.Name, enableValidation, new PropertyInfoAccessorPlugin(prop.Property, prop.AccessorFactory));
break;
case MethodAsDelegateElement methodAsDelegate:
node = new PropertyAccessorNode(methodAsDelegate.Method.Name, enableValidation, new MethodAccessorPlugin(methodAsDelegate.Method, methodAsDelegate.DelegateType));
break;
case ArrayElementPathElement arr:
node = new PropertyAccessorNode(CommonPropertyNames.IndexerName, enableValidation, new ArrayElementPlugin(arr.Indices, arr.ElementType));
break;
@ -92,6 +96,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
public CompiledBindingPathBuilder Method(RuntimeMethodHandle handle, Type delegateType)
{
_elements.Add(new MethodAsDelegateElement(handle, delegateType));
return this;
}
public CompiledBindingPathBuilder StreamTask<T>()
{
_elements.Add(new TaskStreamPathElement<T>());
@ -178,6 +188,19 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
=> _isFirstElement ? Property.Name : $".{Property.Name}";
}
internal class MethodAsDelegateElement : ICompiledBindingPathElement
{
public MethodAsDelegateElement(RuntimeMethodHandle method, Type delegateType)
{
Method = (MethodInfo)MethodBase.GetMethodFromHandle(method);
DelegateType = delegateType;
}
public MethodInfo Method { get; }
public Type DelegateType { get; }
}
internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement
{
IStreamPlugin CreatePlugin();

74
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Avalonia.Data;
using Avalonia.Data.Core.Plugins;
#nullable enable
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
internal class MethodAccessorPlugin : IPropertyAccessorPlugin
{
private MethodInfo _method;
private readonly Type _delegateType;
public MethodAccessorPlugin(MethodInfo method, Type delegateType)
{
_method = method;
_delegateType = delegateType;
}
public bool Match(object obj, string propertyName)
{
throw new InvalidOperationException("The MethodAccessorPlugin does not support dynamic matching");
}
public IPropertyAccessor Start(WeakReference<object?> reference, string propertyName)
{
Debug.Assert(_method.Name == propertyName);
return new Accessor(reference, _method, _delegateType);
}
private sealed class Accessor : PropertyAccessorBase
{
public Accessor(WeakReference<object?> reference, MethodInfo method, Type delegateType)
{
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
PropertyType = delegateType;
if (method.IsStatic)
{
Value = method.CreateDelegate(PropertyType);
}
else if (reference.TryGetTarget(out var target))
{
Value = method.CreateDelegate(PropertyType, target);
}
}
public override Type? PropertyType { get; }
public override object? Value { get; }
public override bool SetValue(object? value, BindingPriority priority) => false;
protected override void SubscribeCore()
{
try
{
PublishValue(Value);
}
catch { }
}
protected override void UnsubscribeCore()
{
}
}
}
}
Loading…
Cancel
Save