From 24b2615dd0378e0688d0dc2cc5bba8828a4ee40a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 28 Sep 2020 18:13:23 +0200 Subject: [PATCH 01/19] Fiexes issue #4657 --- src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml index 3623e95597..7dd4ed0832 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml @@ -12,6 +12,9 @@ + From 640bddf5cabf5f6a8bf4cb07fefe341549c62c03 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 14:59:29 +0200 Subject: [PATCH 02/19] Add interface ICommandSource --- src/Avalonia.Input/ICommandSource.cs | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Avalonia.Input/ICommandSource.cs diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Input/ICommandSource.cs new file mode 100644 index 0000000000..b0dd75e12e --- /dev/null +++ b/src/Avalonia.Input/ICommandSource.cs @@ -0,0 +1,31 @@ +using System.Windows.Input; + +namespace Avalonia.Input +{ + /// + /// An interface for classes that know how to invoke a Command. + /// + public interface ICommandSource + { + /// + /// The command that will be executed when the class is "invoked." + /// Classes that implement this interface should enable or disable based on the command's CanExecute return value. + /// The property may be implemented as read-write if desired. + /// + ICommand Command { get; } + + /// + /// The parameter that will be passed to the command when executing the command. + /// The property may be implemented as read-write if desired. + /// + object CommandParameter { get; } + + + /// + /// Bor the bheavior CanExecuteChanged + /// + /// + /// + void CanExecuteChanged(object sender, System.EventArgs e); + } +} From ef385675470f347aee079f3c9517f88014aa3789 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 15:48:10 +0200 Subject: [PATCH 03/19] Implemented ICommandSource --- src/Avalonia.Controls/Button.cs | 4 +++- src/Avalonia.Controls/MenuItem.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index e94d00b2ff..91eef3947b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -30,7 +30,7 @@ namespace Avalonia.Controls /// A button control. /// [PseudoClasses(":pressed")] - public class Button : ContentControl + public class Button : ContentControl, ICommandSource { /// /// Defines the property. @@ -492,5 +492,7 @@ namespace Avalonia.Controls { PseudoClasses.Set(":pressed", isPressed); } + + void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); } } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 7d4fef009d..d034f54df2 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls /// A menu item control. /// [PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")] - public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable + public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource { /// /// Defines the property. @@ -609,6 +609,8 @@ namespace Avalonia.Controls SelectedItem = null; } + void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); + /// /// A dependency resolver which returns a . /// From fa1476a6622101c383a19e499eb44ad352306e14 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 16:05:59 +0200 Subject: [PATCH 04/19] Fix Hotkey CommandParameter --- src/Avalonia.Controls/HotkeyManager.cs | 17 +++++++++++------ src/Avalonia.Controls/NativeMenuItem.cs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index 95752e7875..b104b7a9a8 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -1,6 +1,7 @@ using System; using System.Windows.Input; using Avalonia.Controls.Utils; +using Avalonia.Data.Core; using Avalonia.Input; namespace Avalonia.Controls @@ -12,14 +13,14 @@ namespace Avalonia.Controls class HotkeyCommandWrapper : ICommand { - public HotkeyCommandWrapper(IControl control) + public HotkeyCommandWrapper(ICommandSource control) { - Control = control; + CommandSource = control; } - public readonly IControl Control; + public readonly ICommandSource CommandSource; - private ICommand GetCommand() => Control.GetValue(Button.CommandProperty); + private ICommand GetCommand() => CommandSource.Command; public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false; @@ -37,6 +38,7 @@ namespace Avalonia.Controls private TopLevel _root; private IDisposable _parentSub; private IDisposable _hotkeySub; + private IDisposable _commandParameterChangedSubscriber; private KeyGesture _hotkey; private readonly HotkeyCommandWrapper _wrapper; private KeyBinding _binding; @@ -44,7 +46,7 @@ namespace Avalonia.Controls public Manager(IControl control) { _control = control; - _wrapper = new HotkeyCommandWrapper(_control); + _wrapper = new HotkeyCommandWrapper(_control as ICommandSource); } public void Init() @@ -77,6 +79,7 @@ namespace Avalonia.Controls { if (_root != null && _binding != null) _root.KeyBindings.Remove(_binding); + _commandParameterChangedSubscriber?.Dispose(); _binding = null; } @@ -85,6 +88,8 @@ namespace Avalonia.Controls if (_root != null && _hotkey != null) { _binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper}; + _commandParameterChangedSubscriber = _binding.Bind(KeyBinding.CommandParameterProperty + , ExpressionObserver.Create(((ICommandSource)_control), o => o.CommandParameter)); _root.KeyBindings.Add(_binding); } } @@ -102,7 +107,7 @@ namespace Avalonia.Controls HotKeyProperty.Changed.Subscribe(args => { var control = args.Sender as IControl; - if (args.OldValue != null|| control == null) + if (args.OldValue != null || control == null || !(control is ICommandSource)) return; new Manager(control).Init(); }); diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index a0fec9e677..d1adda3b0d 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -148,7 +148,7 @@ namespace Avalonia.Controls void CanExecuteChanged() { - IsEnabled = _command?.CanExecute(null) ?? true; + IsEnabled = _command?.CanExecute(CommandParameter) ?? true; } public bool HasClickHandlers => Clicked != null; From 9ffd78a3baa31772de40dd3aad018451486927e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 24 Sep 2020 16:29:51 +0200 Subject: [PATCH 05/19] Add Test --- .../Utils/HotKeyManagerTests.cs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index 9f2712c93c..a29eb0f0c7 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -5,6 +5,9 @@ using Avalonia.Input; using Avalonia.Platform; using Avalonia.Styling; using Xunit; +using System; +using Avalonia.Input.Raw; +using Factory = System.Func, Avalonia.Controls.Window, Avalonia.AvaloniaObject>; namespace Avalonia.Controls.UnitTests.Utils { @@ -54,6 +57,84 @@ namespace Avalonia.Controls.UnitTests.Utils } } + [Theory] + [MemberData(nameof(ElementsFactory))] + public void HotKeyManager_Should_Use_CommandParameter(string factoryName, Factory factory) + { + using (AvaloniaLocator.EnterScope()) + { + var styler = new Mock(); + var target = new KeyboardDevice(); + var commandResult = 0; + var expectedParameter = 1; + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new WindowingPlatformMock()) + .Bind().ToConstant(styler.Object); + + var gesture = new KeyGesture(Key.A, KeyModifiers.Control); + + var action = new Action(parameter => + { + if (parameter is int value) + { + commandResult = value; + } + }); + + var root = new Window(); + var element = factory(expectedParameter, action, root); + + root.Template = CreateWindowTemplate(); + root.ApplyTemplate(); + root.Presenter.ApplyTemplate(); + + HotKeyManager.SetHotKey(element, gesture); + + target.ProcessRawEvent(new RawKeyEventArgs(target, + 0, + root, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.Control)); + + Assert.True(expectedParameter == commandResult, $"{factoryName} HotKey did not carry the CommandParameter."); + } + } + + public static TheoryData ElementsFactory => + new TheoryData() + { + {nameof(Button), MakeButton}, + {nameof(MenuItem),MakeMenu}, + }; + + private static AvaloniaObject MakeMenu(int expectedParameter, Action action, Window root) + { + var menuitem = new MenuItem() + { + Command = new Command(action), + CommandParameter = expectedParameter, + }; + var rootMenu = new Menu(); + + rootMenu.Items = new[] { menuitem }; + + root.Content = rootMenu; + return menuitem; + } + + private static AvaloniaObject MakeButton(int expectedParameter, Action action, Window root) + { + var button = new Button() + { + Command = new Command(action), + CommandParameter = expectedParameter, + }; + + root.Content = button; + return button; + } + private FuncControlTemplate CreateWindowTemplate() { return new FuncControlTemplate((parent, scope) => @@ -65,5 +146,22 @@ namespace Avalonia.Controls.UnitTests.Utils }.RegisterInNameScope(scope); }); } + + class Command : System.Windows.Input.ICommand + { + private readonly Action _execeute; + +#pragma warning disable 67 // Event not used + public event EventHandler CanExecuteChanged; +#pragma warning restore 67 // Event not used + + public Command(Action execeute) + { + _execeute = execeute; + } + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) => _execeute?.Invoke(parameter); + } } } From f1f2df8dd48d42ddc7cc669235988b38cf24b259 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 25 Sep 2020 17:32:26 +0200 Subject: [PATCH 06/19] Fixes Issue #4734 : HotKeys trigger command when control is disabled --- src/Avalonia.Controls/HotkeyManager.cs | 2 +- src/Avalonia.Input/ICommandSource.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index b104b7a9a8..f210ecfdaf 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls private ICommand GetCommand() => CommandSource.Command; - public bool CanExecute(object parameter) => GetCommand()?.CanExecute(parameter) ?? false; + public bool CanExecute(object parameter) => CommandSource.IsEffectivelyEnabled; public void Execute(object parameter) => GetCommand()?.Execute(parameter); diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Input/ICommandSource.cs index b0dd75e12e..ba2e8eed4e 100644 --- a/src/Avalonia.Input/ICommandSource.cs +++ b/src/Avalonia.Input/ICommandSource.cs @@ -27,5 +27,10 @@ namespace Avalonia.Input /// /// void CanExecuteChanged(object sender, System.EventArgs e); + + /// + /// Gets a value indicating whether this control and all its parents are enabled. + /// + bool IsEffectivelyEnabled { get; } } } From f0efe9aef0d2e4953f5ed034e21359c95dcc4499 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 25 Sep 2020 17:33:13 +0200 Subject: [PATCH 07/19] Add test Issue #4734 --- .../Utils/HotKeyManagerTests.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs index a29eb0f0c7..151c3070f4 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs @@ -101,6 +101,49 @@ namespace Avalonia.Controls.UnitTests.Utils } } + + [Theory] + [MemberData(nameof(ElementsFactory))] + public void HotKeyManager_Should_Do_Not_Executed_When_IsEnabled_False(string factoryName, Factory factory) + { + using (AvaloniaLocator.EnterScope()) + { + var styler = new Mock(); + var target = new KeyboardDevice(); + var isExecuted = false; + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(new WindowingPlatformMock()) + .Bind().ToConstant(styler.Object); + + var gesture = new KeyGesture(Key.A, KeyModifiers.Control); + + var action = new Action(parameter => + { + isExecuted = true; + }); + + var root = new Window(); + var element = factory(0, action, root) as InputElement; + + element.IsEnabled = false; + + root.Template = CreateWindowTemplate(); + root.ApplyTemplate(); + root.Presenter.ApplyTemplate(); + + HotKeyManager.SetHotKey(element, gesture); + + target.ProcessRawEvent(new RawKeyEventArgs(target, + 0, + root, + RawKeyEventType.KeyDown, + Key.A, + RawInputModifiers.Control)); + + Assert.True(isExecuted == false, $"{factoryName} Execution raised when IsEnabled is false."); + } + } + public static TheoryData ElementsFactory => new TheoryData() { From c7a21d7268aad472e9022777b626120cdc6b29e3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 9 Oct 2020 09:51:54 +0200 Subject: [PATCH 08/19] Removed CommandParameter Binding --- src/Avalonia.Controls/HotkeyManager.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index f210ecfdaf..dabc35dd24 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -1,7 +1,6 @@ using System; using System.Windows.Input; using Avalonia.Controls.Utils; -using Avalonia.Data.Core; using Avalonia.Input; namespace Avalonia.Controls @@ -22,9 +21,12 @@ namespace Avalonia.Controls private ICommand GetCommand() => CommandSource.Command; - public bool CanExecute(object parameter) => CommandSource.IsEffectivelyEnabled; + public bool CanExecute(object parameter) => + CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true + && CommandSource.IsEffectivelyEnabled; - public void Execute(object parameter) => GetCommand()?.Execute(parameter); + public void Execute(object parameter) => + GetCommand()?.Execute(CommandSource.CommandParameter); #pragma warning disable 67 // Event not used public event EventHandler CanExecuteChanged; @@ -38,7 +40,6 @@ namespace Avalonia.Controls private TopLevel _root; private IDisposable _parentSub; private IDisposable _hotkeySub; - private IDisposable _commandParameterChangedSubscriber; private KeyGesture _hotkey; private readonly HotkeyCommandWrapper _wrapper; private KeyBinding _binding; @@ -79,7 +80,6 @@ namespace Avalonia.Controls { if (_root != null && _binding != null) _root.KeyBindings.Remove(_binding); - _commandParameterChangedSubscriber?.Dispose(); _binding = null; } @@ -87,9 +87,7 @@ namespace Avalonia.Controls { if (_root != null && _hotkey != null) { - _binding = new KeyBinding() {Gesture = _hotkey, Command = _wrapper}; - _commandParameterChangedSubscriber = _binding.Bind(KeyBinding.CommandParameterProperty - , ExpressionObserver.Create(((ICommandSource)_control), o => o.CommandParameter)); + _binding = new KeyBinding() { Gesture = _hotkey, Command = _wrapper }; _root.KeyBindings.Add(_binding); } } From b8728c8f8ccf08a95f68114d0c3f2f00bb7ff0d3 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 9 Oct 2020 09:53:54 +0200 Subject: [PATCH 09/19] Add warning when HotKey is attached to a control that does not support ICommandSource. --- src/Avalonia.Controls/HotkeyManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index dabc35dd24..baae37e022 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -105,8 +105,13 @@ namespace Avalonia.Controls HotKeyProperty.Changed.Subscribe(args => { var control = args.Sender as IControl; - if (args.OldValue != null || control == null || !(control is ICommandSource)) + if (args.OldValue != null || control == null || !(control is ICommandSource)) + { + Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?. + Log(control, $"The element {args.Sender.GetType().Name} do not support HotKey({args.NewValue})."); return; + } + new Manager(control).Init(); }); } From abff848c8d844eebd32a8a61062a471ccc807db7 Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Tue, 13 Oct 2020 17:01:37 +0200 Subject: [PATCH 10/19] Update warning message Co-authored-by: Steven Kirk --- src/Avalonia.Controls/HotkeyManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index baae37e022..b76b7fbed5 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -108,7 +108,7 @@ namespace Avalonia.Controls if (args.OldValue != null || control == null || !(control is ICommandSource)) { Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?. - Log(control, $"The element {args.Sender.GetType().Name} do not support HotKey({args.NewValue})."); + Log(control, $"The element {args.Sender.GetType().Name} does not support binding a HotKey ({args.NewValue})."); return; } From 1b0ff07133851b32f853d6815a7cc17612475f0e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 13 Nov 2020 13:21:23 +0100 Subject: [PATCH 11/19] Fixes Issue 4980: Message loop exit when GetMessage < 0 --- src/Avalonia.Base/Logging/LogArea.cs | 5 +++++ .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 4 ++-- src/Windows/Avalonia.Win32/Win32Platform.cs | 11 ++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Logging/LogArea.cs b/src/Avalonia.Base/Logging/LogArea.cs index 3c19b47a05..ce330247f3 100644 --- a/src/Avalonia.Base/Logging/LogArea.cs +++ b/src/Avalonia.Base/Logging/LogArea.cs @@ -34,5 +34,10 @@ namespace Avalonia.Logging /// The log event comes from the control system. /// public const string Control = "Control"; + + /// + /// The log evevnt come from Win32Platform + /// + public const string Win32Platform = nameof(Win32Platform); } } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index f88c57cf59..85938829f6 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -931,8 +931,8 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll", EntryPoint = "MapVirtualKeyW")] public static extern uint MapVirtualKey(uint uCode, uint uMapType); - [DllImport("user32.dll", EntryPoint = "GetMessageW")] - public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); + [DllImport("user32.dll", EntryPoint = "GetMessageW",SetLastError = true)] + public static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); [DllImport("user32.dll")] public static extern int GetMessageTime(); diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 5b16cae26e..209cb63603 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -137,13 +137,18 @@ namespace Avalonia.Win32 public void RunLoop(CancellationToken cancellationToken) { - while (!cancellationToken.IsCancellationRequested) + var result = 0; + while (!cancellationToken.IsCancellationRequested + && (result = UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0)) > 0) { - UnmanagedMethods.MSG msg; - UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0); UnmanagedMethods.TranslateMessage(ref msg); UnmanagedMethods.DispatchMessage(ref msg); } + if (result < 0) + { + Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform) + ?.Log(this, "Unmanaged error in message loop. Error Code: {0}", Marshal.GetLastWin32Error()); + } } public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action callback) From b31b6d68c9791085766550cdc29bcde12b1608a0 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 13 Nov 2020 16:17:03 +0100 Subject: [PATCH 12/19] Fixes ProcessMessage --- src/Windows/Avalonia.Win32/Win32Platform.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 209cb63603..1ed4d8dc9c 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -129,10 +129,18 @@ namespace Avalonia.Win32 public void ProcessMessage() { - UnmanagedMethods.MSG msg; - UnmanagedMethods.GetMessage(out msg, IntPtr.Zero, 0, 0); - UnmanagedMethods.TranslateMessage(ref msg); - UnmanagedMethods.DispatchMessage(ref msg); + + if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) > 0) + { + UnmanagedMethods.TranslateMessage(ref msg); + UnmanagedMethods.DispatchMessage(ref msg); + } + else + { + Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform) + ?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(ProcessMessage), Marshal.GetLastWin32Error()); + + } } public void RunLoop(CancellationToken cancellationToken) @@ -147,7 +155,7 @@ namespace Avalonia.Win32 if (result < 0) { Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform) - ?.Log(this, "Unmanaged error in message loop. Error Code: {0}", Marshal.GetLastWin32Error()); + ?.Log(this, "Unmanaged error in {0}. Error Code: {1}" ,nameof(RunLoop), Marshal.GetLastWin32Error()); } } From cfb81aedcaae7d501017a4224f18ffc8348ae4de Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 14 Nov 2020 00:44:44 +0100 Subject: [PATCH 13/19] fixes typo. --- src/Avalonia.Base/Logging/LogArea.cs | 2 +- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Logging/LogArea.cs b/src/Avalonia.Base/Logging/LogArea.cs index ce330247f3..2ad220dddd 100644 --- a/src/Avalonia.Base/Logging/LogArea.cs +++ b/src/Avalonia.Base/Logging/LogArea.cs @@ -36,7 +36,7 @@ namespace Avalonia.Logging public const string Control = "Control"; /// - /// The log evevnt come from Win32Platform + /// The log event comes from Win32Platform. /// public const string Win32Platform = nameof(Win32Platform); } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 85938829f6..b164bcc611 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -931,7 +931,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll", EntryPoint = "MapVirtualKeyW")] public static extern uint MapVirtualKey(uint uCode, uint uMapType); - [DllImport("user32.dll", EntryPoint = "GetMessageW",SetLastError = true)] + [DllImport("user32.dll", EntryPoint = "GetMessageW", SetLastError = true)] public static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); [DllImport("user32.dll")] diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 1ed4d8dc9c..345c825a5d 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -155,7 +155,7 @@ namespace Avalonia.Win32 if (result < 0) { Logging.Logger.TryGet(Logging.LogEventLevel.Error, Logging.LogArea.Win32Platform) - ?.Log(this, "Unmanaged error in {0}. Error Code: {1}" ,nameof(RunLoop), Marshal.GetLastWin32Error()); + ?.Log(this, "Unmanaged error in {0}. Error Code: {1}", nameof(RunLoop), Marshal.GetLastWin32Error()); } } From 7ff860c8ef54c5ce38b2e6c672297e651c1142c2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 14 Nov 2020 14:40:21 +0100 Subject: [PATCH 14/19] fix ProcessMessage issue --- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 345c825a5d..e854ceae0b 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -130,7 +130,7 @@ namespace Avalonia.Win32 public void ProcessMessage() { - if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) > 0) + if (UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0) > -1) { UnmanagedMethods.TranslateMessage(ref msg); UnmanagedMethods.DispatchMessage(ref msg); From 80a87d7aa31a8b177690207d3d043e90d68f093e Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 15 Nov 2020 17:32:43 +0200 Subject: [PATCH 15/19] add failing tests for CompiledBidning with Source set to StaticResource and x:Static --- .../CompiledBindingExtensionTests.cs | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 50dfa2c7b0..c19640c244 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Data.Converters; using Avalonia.Data.Core; using Avalonia.Markup.Data; +using Avalonia.Media; using Avalonia.UnitTests; using XamlX; using Xunit; @@ -536,7 +537,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } [Fact] - public void ResolvesSourceBindingLongForm() + public void Binds_To_Source() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { @@ -559,6 +560,49 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Binds_To_Source_StaticResource() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + Assert.Equal("foobar", textBlock.Text); + } + } + + [Fact] + public void Binds_To_Source_xStatic() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + Assert.Equal(Brushes.Red.Color, contentControl.Content); + } + } + [Fact] public void CompilesBindingWhenRequested() { From 06d81f6e23cde111166393f441558dc1354247df Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 15 Nov 2020 17:40:34 +0200 Subject: [PATCH 16/19] make CompiledBinding work with x:Static and StaticResource --- .../AvaloniaXamlIlBindingPathTransformer.cs | 33 ++++++++++++++++--- ...ransformSyntheticCompiledBindingMembers.cs | 4 +-- .../CompiledBindingExtension.cs | 10 ++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs index fc32084687..0d54e97bd9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs @@ -15,14 +15,37 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { if (node is XamlAstConstructableObjectNode binding && binding.Type.GetClrType().Equals(context.GetAvaloniaTypes().CompiledBindingExtension)) { - IXamlType startType; - var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault(); - if (parentDataContextNode is null) + IXamlType startType = null; + var sourceProperty = binding.Children.OfType().FirstOrDefault(c => c.Property.Name == "Source"); + if ((sourceProperty?.Values.Count ?? 0) == 1) { - throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding); + var sourceValue = sourceProperty.Values[0]; + switch (sourceValue) + { + case XamlAstTextNode textNode: + startType = textNode.Type?.GetClrType(); + break; + + case XamlMarkupExtensionNode extension: + startType = extension.Type?.GetClrType(); + break; + + case XamlStaticExtensionNode staticExtension: + startType = staticExtension.Type?.GetClrType(); + break; + } } - startType = parentDataContextNode.DataContextType; + if (startType == null) + { + var parentDataContextNode = context.ParentNodes().OfType().FirstOrDefault(); + if (parentDataContextNode is null) + { + throw new XamlX.XamlParseException("Cannot parse a compiled binding without an explicit x:DataType directive to give a starting data type for bindings.", binding); + } + + startType = parentDataContextNode.DataContextType; + } XamlIlBindingPathHelper.UpdateCompiledBindingExtension(context, binding, startType); } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs index 154c6a235c..6cb6e4e2d1 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs @@ -27,8 +27,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } else if (prop.Name == "Source") { - return new AvaloniaSyntheticCompiledBindingProperty(node, - SyntheticCompiledBindingPropertyName.Source); + //return new AvaloniaSyntheticCompiledBindingProperty(node, + // SyntheticCompiledBindingPropertyName.Source); } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index da39920eb3..17d2ea7ae9 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs @@ -32,6 +32,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions Mode = Mode, Priority = Priority, StringFormat = StringFormat, + Source = Source, DefaultAnchor = new WeakReference(GetDefaultAnchor(provider)) }; } @@ -52,6 +53,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions protected override ExpressionObserver CreateExpressionObserver(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor, bool enableDataValidation) { + if (Source != null) + { + return CreateSourceObserver( + Source, + Path.BuildExpression(enableDataValidation)); + } + if (Path.RawSource != null) { return CreateSourceObserver( @@ -77,5 +85,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions [ConstructorArgument("path")] public CompiledBindingPath Path { get; set; } + + public object Source { get; set; } } } From f9de2a9c8236573df5ffe8dd0acc4417f3e43665 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 15 Nov 2020 17:42:05 +0200 Subject: [PATCH 17/19] add more tests for CompiledBinding Source with StaticResource --- .../CompiledBindingExtensionTests.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index c19640c244..de9ecc8bb0 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -583,6 +583,81 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void Binds_To_Source_StaticResource1() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + test + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + Assert.Equal("foobar", textBlock.Text); + } + } + + [Fact] + public void Binds_To_Source_StaticResource_In_ResourceDictionary() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + Assert.Equal("foobar", textBlock.Text); + } + } + + [Fact] + public void Binds_To_Source_StaticResource_In_ResourceDictionary1() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + test + + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + Assert.Equal("foobar", textBlock.Text); + } + } + [Fact] public void Binds_To_Source_xStatic() { From 13c25983f70d2c606eaaa5d83f318413bf65773b Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 15 Nov 2020 17:45:04 +0200 Subject: [PATCH 18/19] try infer StaticResource type in CompiledBinding Soure --- .../AvaloniaXamlIlBindingPathTransformer.cs | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs index 0d54e97bd9..4d8c940bbc 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathTransformer.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using XamlX; using XamlX.Ast; using XamlX.Transform; using XamlX.TypeSystem; @@ -28,6 +26,71 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers case XamlMarkupExtensionNode extension: startType = extension.Type?.GetClrType(); + + //let's try to infer StaticResource type from parent resources in xaml + if (extension.Value.Type.GetClrType().FullName == "Avalonia.Markup.Xaml.MarkupExtensions.StaticResourceExtension" && + extension.Value is XamlAstConstructableObjectNode cn && + cn.Arguments.Count == 1 && cn.Arguments[0] is XamlAstTextNode keyNode) + { + bool matchProperty(IXamlAstNode node, IXamlType styledElementType, string propertyName) + { + return (node is XamlPropertyAssignmentNode p && + p.Property.DeclaringType == styledElementType && p.Property.Name == propertyName) + || + (node is XamlManipulationGroupNode m && m.Children.Count > 0 && + m.Children[0] is XamlPropertyAssignmentNode pm && + pm.Property.DeclaringType == styledElementType && pm.Property.Name == propertyName); + } + + string getResourceValue_xKey(XamlPropertyAssignmentNode node) + => node.Values.Count == 2 && node.Values[0] is XamlAstTextNode t ? t.Text : ""; + + IXamlType getResourceValue_Type(XamlPropertyAssignmentNode node, IXamlType xamlType) + => node.Values.Count == 2 ? node.Values[1].Type.GetClrType() : xamlType; + + IEnumerable getResourceValues(IXamlAstNode node) + { + if (node is XamlPropertyAssignmentNode propertyNode) + { + if (propertyNode.Values.Count == 1 && + propertyNode.Values[0] is XamlAstConstructableObjectNode obj && + obj.Type.GetClrType().FullName == "Avalonia.Controls.ResourceDictionary") + { + foreach (var r in obj.Children.SelectMany(c => getResourceValues(c))) + { + yield return r; + } + } + else + { + yield return propertyNode; + } + } + else if (node is XamlManipulationGroupNode m) + { + foreach (var r in m.Children.OfType()) + { + yield return r; + } + } + } + + string key = keyNode.Text; + + var styledElement = context.GetAvaloniaTypes().StyledElement; + var resource = context.ParentNodes() + .OfType() + .Where(o => styledElement.IsAssignableFrom(o.Type.GetClrType())) + .Select(o => o.Children.FirstOrDefault(p => matchProperty(p, styledElement, "Resources"))) + .Where(r => r != null) + .SelectMany(r => getResourceValues(r)) + .FirstOrDefault(r => getResourceValue_xKey(r) == key); + + if (resource != null) + { + startType = getResourceValue_Type(resource, startType); + } + } break; case XamlStaticExtensionNode staticExtension: From bc39305fd0a088a306e8c13358fa8fc426fe9fc0 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 15 Nov 2020 18:16:58 +0200 Subject: [PATCH 19/19] clean up --- .../AvaloniaXamlIlBindingPathParser.cs | 23 ++++++------------- ...ransformSyntheticCompiledBindingMembers.cs | 8 +------ 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs index 7944d8b569..24cded1d22 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlBindingPathParser.cs @@ -72,16 +72,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers v.Property is AvaloniaSyntheticCompiledBindingProperty prop && prop.Name == SyntheticCompiledBindingPropertyName.ElementName); - var sourceProperty = syntheticCompiledBindingProperties - .FirstOrDefault(v => - v.Property is AvaloniaSyntheticCompiledBindingProperty prop - && prop.Name == SyntheticCompiledBindingPropertyName.Source); - var relativeSourceProperty = syntheticCompiledBindingProperties .FirstOrDefault(v => v.Property is AvaloniaSyntheticCompiledBindingProperty prop && prop.Name == SyntheticCompiledBindingPropertyName.RelativeSource); + var sourceProperty = binding.Children.OfType() + .FirstOrDefault(v => + v.Property is XamlAstClrProperty prop + && prop.Name == "Source"); + if (elementNameProperty?.Values[0] is XamlAstTextNode elementName) { convertedNode = new BindingExpressionGrammar.NameNode { Name = elementName.Text }; @@ -91,14 +91,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers throw new XamlParseException($"Invalid ElementName '{elementNameProperty.Values[0]}'.", elementNameProperty.Values[0]); } - if (sourceProperty?.Values[0] != null) + if (sourceProperty != null && convertedNode != null) { - if (convertedNode != null) - { - throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding); - } - - convertedNode = new RawSourceBindingExpressionNode(sourceProperty?.Values[0]); + throw new XamlParseException("Only one of ElementName, Source, or RelativeSource specified as a binding source. Only one property is allowed.", binding); } if (GetRelativeSourceObjectFromAssignment( @@ -223,10 +218,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { binding.Children.Remove(elementNameProperty); } - if (sourceProperty != null) - { - binding.Children.Remove(sourceProperty); - } if (relativeSourceProperty != null) { binding.Children.Remove(relativeSourceProperty); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs index 6cb6e4e2d1..ba05347a52 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformSyntheticCompiledBindingMembers.cs @@ -25,11 +25,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return new AvaloniaSyntheticCompiledBindingProperty(node, SyntheticCompiledBindingPropertyName.RelativeSource); } - else if (prop.Name == "Source") - { - //return new AvaloniaSyntheticCompiledBindingProperty(node, - // SyntheticCompiledBindingPropertyName.Source); - } } return node; @@ -39,8 +34,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers enum SyntheticCompiledBindingPropertyName { ElementName, - RelativeSource, - Source + RelativeSource } class AvaloniaSyntheticCompiledBindingProperty : XamlAstNode, IXamlAstPropertyReference