committed by
GitHub
8 changed files with 386 additions and 47 deletions
@ -1,41 +0,0 @@ |
|||||
using System; |
|
||||
using System.Globalization; |
|
||||
using System.Reflection; |
|
||||
using System.Windows.Input; |
|
||||
using Avalonia.Utilities; |
|
||||
|
|
||||
namespace Avalonia.Data.Converters |
|
||||
{ |
|
||||
class AlwaysEnabledDelegateCommand : ICommand |
|
||||
{ |
|
||||
private readonly Delegate action; |
|
||||
|
|
||||
private ParameterInfo parameterInfo; |
|
||||
|
|
||||
public AlwaysEnabledDelegateCommand(Delegate action) |
|
||||
{ |
|
||||
this.action = action; |
|
||||
var parameters = action.Method.GetParameters(); |
|
||||
parameterInfo = parameters.Length == 0 ? null : parameters[0]; |
|
||||
} |
|
||||
|
|
||||
#pragma warning disable 0067
|
|
||||
public event EventHandler CanExecuteChanged; |
|
||||
#pragma warning restore 0067
|
|
||||
|
|
||||
public bool CanExecute(object parameter) => true; |
|
||||
|
|
||||
public void Execute(object parameter) |
|
||||
{ |
|
||||
if (parameterInfo == null) |
|
||||
{ |
|
||||
action.DynamicInvoke(); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
TypeUtilities.TryConvert(parameterInfo.ParameterType, parameter, CultureInfo.CurrentCulture, out object convertedParameter); |
|
||||
action.DynamicInvoke(convertedParameter); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,230 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Reflection; |
||||
|
using System.Windows.Input; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Data.Converters |
||||
|
{ |
||||
|
class MethodToCommandConverter : ICommand |
||||
|
{ |
||||
|
readonly static Func<object, bool> AlwaysEnabled = (_) => true; |
||||
|
readonly static MethodInfo tryConvert = typeof(TypeUtilities) |
||||
|
.GetMethod(nameof(TypeUtilities.TryConvert), BindingFlags.Public | BindingFlags.Static); |
||||
|
readonly static PropertyInfo currentCulture = typeof(CultureInfo) |
||||
|
.GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static); |
||||
|
readonly Func<object, bool> canExecute; |
||||
|
readonly Action<object> execute; |
||||
|
readonly WeakPropertyChangedProxy weakPropertyChanged; |
||||
|
readonly PropertyChangedEventHandler propertyChangedEventHandler; |
||||
|
readonly string[] dependencyProperties; |
||||
|
|
||||
|
public MethodToCommandConverter(Delegate action) |
||||
|
{ |
||||
|
var target = action.Target; |
||||
|
var canExecuteMethodName = "Can" + action.Method.Name; |
||||
|
var parameters = action.Method.GetParameters(); |
||||
|
var parameterInfo = parameters.Length == 0 ? null : parameters[0].ParameterType; |
||||
|
|
||||
|
if (parameterInfo == null) |
||||
|
{ |
||||
|
execute = CreateExecute(target, action.Method); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
execute = CreateExecute(target, action.Method, parameterInfo); |
||||
|
} |
||||
|
|
||||
|
var canExecuteMethod = action.Method.DeclaringType.GetRuntimeMethods() |
||||
|
.FirstOrDefault(m => m.Name == canExecuteMethodName |
||||
|
&& m.GetParameters().Length == 1 |
||||
|
&& m.GetParameters()[0].ParameterType == typeof(object)); |
||||
|
if (canExecuteMethod == null) |
||||
|
{ |
||||
|
canExecute = AlwaysEnabled; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
canExecute = CreateCanExecute(target, canExecuteMethod); |
||||
|
dependencyProperties = canExecuteMethod |
||||
|
.GetCustomAttributes(typeof(Metadata.DependsOnAttribute), true) |
||||
|
.OfType<Metadata.DependsOnAttribute>() |
||||
|
.Select(x => x.Name) |
||||
|
.ToArray(); |
||||
|
if (dependencyProperties.Any() |
||||
|
&& target is INotifyPropertyChanged inpc) |
||||
|
{ |
||||
|
propertyChangedEventHandler = OnPropertyChanged; |
||||
|
weakPropertyChanged = new WeakPropertyChangedProxy(inpc, propertyChangedEventHandler); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void OnPropertyChanged(object sender,PropertyChangedEventArgs args) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(args.PropertyName) |
||||
|
|| dependencyProperties?.Contains(args.PropertyName) == true) |
||||
|
{ |
||||
|
Threading.Dispatcher.UIThread.Post(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty) |
||||
|
, Threading.DispatcherPriority.Input); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#pragma warning disable 0067
|
||||
|
public event EventHandler CanExecuteChanged; |
||||
|
#pragma warning restore 0067
|
||||
|
|
||||
|
public bool CanExecute(object parameter) => canExecute(parameter); |
||||
|
|
||||
|
public void Execute(object parameter) => execute(parameter); |
||||
|
|
||||
|
|
||||
|
static Action<object> CreateExecute(object target |
||||
|
, System.Reflection.MethodInfo method) |
||||
|
{ |
||||
|
|
||||
|
var parameter = Expression.Parameter(typeof(object), "parameter"); |
||||
|
|
||||
|
var instance = Expression.Convert |
||||
|
( |
||||
|
Expression.Constant(target), |
||||
|
method.DeclaringType |
||||
|
); |
||||
|
|
||||
|
|
||||
|
var call = Expression.Call |
||||
|
( |
||||
|
instance, |
||||
|
method |
||||
|
); |
||||
|
|
||||
|
|
||||
|
return Expression |
||||
|
.Lambda<Action<object>>(call, parameter) |
||||
|
.Compile(); |
||||
|
} |
||||
|
|
||||
|
static Action<object> CreateExecute(object target |
||||
|
, System.Reflection.MethodInfo method |
||||
|
, Type parameterType) |
||||
|
{ |
||||
|
|
||||
|
var parameter = Expression.Parameter(typeof(object), "parameter"); |
||||
|
|
||||
|
var instance = Expression.Convert |
||||
|
( |
||||
|
Expression.Constant(target), |
||||
|
method.DeclaringType |
||||
|
); |
||||
|
|
||||
|
Expression body; |
||||
|
|
||||
|
if (parameterType == typeof(object)) |
||||
|
{ |
||||
|
body = Expression.Call(instance, |
||||
|
method, |
||||
|
parameter |
||||
|
); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var arg0 = Expression.Variable(typeof(object), "argX"); |
||||
|
var convertCall = Expression.Call(tryConvert, |
||||
|
Expression.Constant(parameterType), |
||||
|
parameter, |
||||
|
Expression.Property(null, currentCulture), |
||||
|
arg0 |
||||
|
); |
||||
|
|
||||
|
var call = Expression.Call(instance, |
||||
|
method, |
||||
|
Expression.Convert(arg0, parameterType) |
||||
|
); |
||||
|
body = Expression.Block(new[] { arg0 }, |
||||
|
convertCall, |
||||
|
call |
||||
|
); |
||||
|
|
||||
|
} |
||||
|
Action<object> action = null; |
||||
|
try |
||||
|
{ |
||||
|
action = Expression |
||||
|
.Lambda<Action<object>>(body, parameter) |
||||
|
.Compile(); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
throw ex; |
||||
|
} |
||||
|
return action; |
||||
|
} |
||||
|
|
||||
|
static Func<object, bool> CreateCanExecute(object target |
||||
|
, System.Reflection.MethodInfo method) |
||||
|
{ |
||||
|
var parameter = Expression.Parameter(typeof(object), "parameter"); |
||||
|
var instance = Expression.Convert |
||||
|
( |
||||
|
Expression.Constant(target), |
||||
|
method.DeclaringType |
||||
|
); |
||||
|
var call = Expression.Call |
||||
|
( |
||||
|
instance, |
||||
|
method, |
||||
|
parameter |
||||
|
); |
||||
|
return Expression |
||||
|
.Lambda<Func<object, bool>>(call, parameter) |
||||
|
.Compile(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
internal class WeakPropertyChangedProxy |
||||
|
{ |
||||
|
readonly WeakReference<PropertyChangedEventHandler> _listener = new WeakReference<PropertyChangedEventHandler>(null); |
||||
|
readonly PropertyChangedEventHandler _handler; |
||||
|
internal WeakReference<INotifyPropertyChanged> Source { get; } = new WeakReference<INotifyPropertyChanged>(null); |
||||
|
|
||||
|
public WeakPropertyChangedProxy() |
||||
|
{ |
||||
|
_handler = new PropertyChangedEventHandler(OnPropertyChanged); |
||||
|
} |
||||
|
|
||||
|
public WeakPropertyChangedProxy(INotifyPropertyChanged source, PropertyChangedEventHandler listener) : this() |
||||
|
{ |
||||
|
SubscribeTo(source, listener); |
||||
|
} |
||||
|
|
||||
|
public void SubscribeTo(INotifyPropertyChanged source, PropertyChangedEventHandler listener) |
||||
|
{ |
||||
|
source.PropertyChanged += _handler; |
||||
|
|
||||
|
Source.SetTarget(source); |
||||
|
_listener.SetTarget(listener); |
||||
|
} |
||||
|
|
||||
|
public void Unsubscribe() |
||||
|
{ |
||||
|
if (Source.TryGetTarget(out INotifyPropertyChanged source) && source != null) |
||||
|
source.PropertyChanged -= _handler; |
||||
|
|
||||
|
Source.SetTarget(null); |
||||
|
_listener.SetTarget(null); |
||||
|
} |
||||
|
|
||||
|
void OnPropertyChanged(object sender, PropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (_listener.TryGetTarget(out var handler) && handler != null) |
||||
|
handler(sender, e); |
||||
|
else |
||||
|
Unsubscribe(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue