diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 6dd3521183..7a8e95df13 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -19,10 +19,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlMethod AvaloniaObjectBindMethod { get; } public IXamlMethod AvaloniaObjectSetValueMethod { get; } public IXamlType IDisposable { get; } + public IXamlType ICommand { get; } public XamlTypeWellKnownTypes XamlIlTypes { get; } public XamlLanguageTypeMappings XamlIlMappings { get; } public IXamlType Transitions { get; } public IXamlType AssignBindingAttribute { get; } + public IXamlType DependsOnAttribute { get; } public IXamlType UnsetValueType { get; } public IXamlType StyledElement { get; } public IXamlType IStyledElement { get; } @@ -96,8 +98,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority"); IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding"); IDisposable = cfg.TypeSystem.GetType("System.IDisposable"); + IDisposable = cfg.TypeSystem.GetType("System.Windows.Input.ICommand"); Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); + DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute"); AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, cfg.WellKnownTypes.Object); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index 028f2f5fa8..07a5a74388 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -32,6 +32,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions selfType, bindingPath.Path); + transformed = TransformForTargetTyping(transformed, context); + bindingResultType = transformed.BindingResultType; binding.Arguments[0] = transformed; } @@ -54,6 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions selfType, bindingPathNode.Path); + transformed = TransformForTargetTyping(transformed, context); + bindingResultType = transformed.BindingResultType; bindingPathAssignment.Values[0] = transformed; } @@ -66,7 +70,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return bindingResultType; } - private static IXamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func startTypeResolver, IXamlType selfType, IEnumerable bindingExpression) + private static XamlIlBindingPathNode TransformForTargetTyping(XamlIlBindingPathNode transformed, AstTransformationContext context) + { + if (context.ParentNodes().OfType().FirstOrDefault().Property.Getter.ReturnType == context.GetAvaloniaTypes().ICommand + && transformed.Elements[transformed.Elements.Count - 1] is XamlIlClrMethodPathElementNode method) + { + IXamlMethod executeMethod = method.Method; + IXamlMethod canExecuteMethod = executeMethod.DeclaringType.FindMethod(new FindMethodMethodSignature($"Can{executeMethod.Name}", context.Configuration.WellKnownTypes.Boolean, context.Configuration.WellKnownTypes.Object)); + List dependsOnProperties = new(); + if (canExecuteMethod is not null) + { + foreach (var attr in canExecuteMethod.CustomAttributes) + { + if (attr.Type == context.GetAvaloniaTypes().DependsOnAttribute) + { + dependsOnProperties.Add((string)attr.Parameters[0]); + } + } + } + transformed.Elements.RemoveAt(transformed.Elements.Count - 1); + transformed.Elements.Add(new XamlIlClrMethodAsCommandPathElementNode(context.GetAvaloniaTypes().ICommand, executeMethod, canExecuteMethod, dependsOnProperties)); + } + + return transformed; + } + + private static XamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func startTypeResolver, IXamlType selfType, IEnumerable bindingExpression) { List transformNodes = new List(); List nodes = new List(); @@ -553,13 +582,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode { - private readonly IXamlMethod _method; public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType) { - _method = method; + Method = method; Type = systemDelegateType; } + public IXamlMethod Method { get; } public IXamlType Type { get; } @@ -567,35 +596,35 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { IXamlTypeBuilder newDelegateTypeBuilder = null; IXamlType specificDelegateType; - if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count == 0) + if (Method.ReturnType == context.Configuration.WellKnownTypes.Void && Method.Parameters.Count == 0) { specificDelegateType = context.Configuration.TypeSystem .GetType("System.Action"); } - else if (_method.ReturnType == context.Configuration.WellKnownTypes.Void && _method.Parameters.Count <= 16) + else if (Method.ReturnType == context.Configuration.WellKnownTypes.Void && Method.Parameters.Count <= 16) { specificDelegateType = context.Configuration.TypeSystem - .GetType($"System.Action`{_method.Parameters.Count}") - .MakeGenericType(_method.Parameters); + .GetType($"System.Action`{Method.Parameters.Count}") + .MakeGenericType(Method.Parameters); } - else if (_method.Parameters.Count <= 16) + else if (Method.Parameters.Count <= 16) { List genericParameters = new(); - genericParameters.AddRange(_method.Parameters); - genericParameters.Add(_method.ReturnType); + genericParameters.AddRange(Method.Parameters); + genericParameters.Add(Method.ReturnType); specificDelegateType = context.Configuration.TypeSystem - .GetType($"System.Func`{_method.Parameters.Count + 1}") + .GetType($"System.Func`{Method.Parameters.Count + 1}") .MakeGenericType(genericParameters); } else { // In this case, we need to emit our own delegate type. string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); - specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, _method.ReturnType, _method.Parameters); + specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, Method.ReturnType, Method.Parameters); } codeGen - .Ldtoken(_method) + .Ldtoken(Method) .Ldtoken(specificDelegateType) .EmitCall(context.GetAvaloniaTypes() .CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method")); @@ -803,7 +832,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode { private readonly List _transformElements; - private readonly List _elements; public XamlIlBindingPathNode(IXamlLineInfo lineInfo, IXamlType bindingPathType, @@ -812,16 +840,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { Type = new XamlAstClrTypeReference(lineInfo, bindingPathType, false); _transformElements = transformElements; - _elements = elements; + Elements = elements; } public IXamlType BindingResultType => _transformElements.Count > 0 ? _transformElements[0].Type - : _elements[_elements.Count - 1].Type; + : Elements[Elements.Count - 1].Type; public IXamlAstTypeReference Type { get; } + public List Elements { get; } + public XamlILNodeEmitResult Emit(XamlIlEmitContext context, IXamlILEmitter codeGen) { var types = context.GetAvaloniaTypes(); @@ -832,7 +862,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions transform.Emit(context, codeGen); } - foreach (var element in _elements) + foreach (var element in Elements) { element.Emit(context, codeGen); } @@ -850,11 +880,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions _transformElements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor); } } - for (int i = 0; i < _elements.Count; i++) + for (int i = 0; i < Elements.Count; i++) { - if (_elements[i] is IXamlAstNode ast) + if (Elements[i] is IXamlAstNode ast) { - _elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor); + Elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor); } } }