diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index d8c31fc4779..821aef77588 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -34,8 +34,10 @@ public enum ClickMode
[PseudoClasses(pcFlyoutOpen, pcPressed)]
public class Button : ContentControl, ICommandSource, IClickableControl
{
- private const string pcPressed = ":pressed";
+ private const string pcPressed = ":pressed";
private const string pcFlyoutOpen = ":flyout-open";
+ private EventHandler? _canExecuteChangeHandler = default;
+ private EventHandler CanExecuteChangedHandler => _canExecuteChangeHandler ??= new(CanExecuteChanged);
///
/// Defines the property.
@@ -250,10 +252,11 @@ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e
base.OnAttachedToLogicalTree(e);
- if (Command != null)
+ (var command, var parameter) = (Command, CommandParameter);
+ if (command is not null)
{
- Command.CanExecuteChanged += CanExecuteChanged;
- CanExecuteChanged(this, EventArgs.Empty);
+ command.CanExecuteChanged += CanExecuteChangedHandler;
+ CanExecuteChanged(command, parameter);
}
}
@@ -269,9 +272,9 @@ protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs
base.OnDetachedFromLogicalTree(e);
- if (Command != null)
+ if (Command is { } command)
{
- Command.CanExecuteChanged -= CanExecuteChanged;
+ command.CanExecuteChanged -= CanExecuteChangedHandler;
}
}
@@ -343,9 +346,10 @@ protected virtual void OnClick()
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
- if (!e.Handled && Command?.CanExecute(CommandParameter) == true)
+ (var command, var parameter) = (Command, CommandParameter);
+ if (!e.Handled && command is not null && command.CanExecute(parameter))
{
- Command.Execute(CommandParameter);
+ command.Execute(parameter);
e.Handled = true;
}
}
@@ -451,25 +455,24 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
if (change.Property == CommandProperty)
{
+ var (oldValue, newValue) = change.GetOldAndNewValue();
if (((ILogical)this).IsAttachedToLogicalTree)
{
- var (oldValue, newValue) = change.GetOldAndNewValue();
if (oldValue is ICommand oldCommand)
{
- oldCommand.CanExecuteChanged -= CanExecuteChanged;
+ oldCommand.CanExecuteChanged -= CanExecuteChangedHandler;
}
if (newValue is ICommand newCommand)
{
- newCommand.CanExecuteChanged += CanExecuteChanged;
+ newCommand.CanExecuteChanged += CanExecuteChangedHandler;
}
}
-
- CanExecuteChanged(this, EventArgs.Empty);
+ CanExecuteChanged(newValue, CommandParameter);
}
else if (change.Property == CommandParameterProperty)
{
- CanExecuteChanged(this, EventArgs.Empty);
+ CanExecuteChanged(Command, change.NewValue);
}
else if (change.Property == IsCancelProperty)
{
@@ -557,7 +560,18 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
/// The event args.
private void CanExecuteChanged(object? sender, EventArgs e)
{
- var canExecute = Command == null || Command.CanExecute(CommandParameter);
+ CanExecuteChanged(Command, CommandParameter);
+ }
+
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ private void CanExecuteChanged(ICommand? command, object? parameter)
+ {
+ if (!((ILogical)this).IsAttachedToLogicalTree)
+ {
+ return;
+ }
+
+ var canExecute = command == null || command.CanExecute(parameter);
if (canExecute != _commandCanExecute)
{
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index a81ca13e811..a2a54d5550f 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Avalonia.Reactive;
using System.Windows.Input;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
@@ -13,7 +12,7 @@
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
-using Avalonia.Layout;
+using Avalonia.Reactive;
namespace Avalonia.Controls
{
@@ -24,6 +23,9 @@ namespace Avalonia.Controls
[PseudoClasses(":separator", ":radio", ":toggle", ":checked", ":icon", ":open", ":pressed", ":selected")]
public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource, IClickableControl, IRadioButton
{
+ private EventHandler? _canExecuteChangeHandler = default;
+ private EventHandler CanExecuteChangedHandler => _canExecuteChangeHandler ??= new(CanExecuteChanged);
+
///
/// Defines the property.
///
@@ -83,7 +85,7 @@ public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, I
///
public static readonly StyledProperty GroupNameProperty =
RadioButton.GroupNameProperty.AddOwner