1
1
using System ;
2
2
using System . Collections . Generic ;
3
3
using System . Linq ;
4
- using Avalonia . Reactive ;
5
4
using System . Windows . Input ;
6
5
using Avalonia . Automation . Peers ;
7
6
using Avalonia . Controls . Metadata ;
13
12
using Avalonia . Input ;
14
13
using Avalonia . Interactivity ;
15
14
using Avalonia . LogicalTree ;
16
- using Avalonia . Layout ;
15
+ using Avalonia . Reactive ;
17
16
18
17
namespace Avalonia . Controls
19
18
{
@@ -24,6 +23,9 @@ namespace Avalonia.Controls
24
23
[ PseudoClasses ( ":separator" , ":radio" , ":toggle" , ":checked" , ":icon" , ":open" , ":pressed" , ":selected" ) ]
25
24
public class MenuItem : HeaderedSelectingItemsControl , IMenuItem , ISelectable , ICommandSource , IClickableControl , IRadioButton
26
25
{
26
+ private EventHandler ? _canExecuteChangeHandler = default ;
27
+ private EventHandler CanExecuteChangedHandler => _canExecuteChangeHandler ??= new ( CanExecuteChanged ) ;
28
+
27
29
/// <summary>
28
30
/// Defines the <see cref="Command"/> property.
29
31
/// </summary>
@@ -83,7 +85,7 @@ public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, I
83
85
/// </summary>
84
86
public static readonly StyledProperty < string ? > GroupNameProperty =
85
87
RadioButton . GroupNameProperty . AddOwner < MenuItem > ( ) ;
86
-
88
+
87
89
/// <summary>
88
90
/// Defines the <see cref="Click"/> event.
89
91
/// </summary>
@@ -292,7 +294,7 @@ public bool StaysOpenOnClick
292
294
get => GetValue ( StaysOpenOnClickProperty ) ;
293
295
set => SetValue ( StaysOpenOnClickProperty , value ) ;
294
296
}
295
-
297
+
296
298
/// <inheritdoc cref="IMenuItem.ToggleType" />
297
299
public MenuItemToggleType ToggleType
298
300
{
@@ -306,7 +308,7 @@ public bool IsChecked
306
308
get => GetValue ( IsCheckedProperty ) ;
307
309
set => SetValue ( IsCheckedProperty , value ) ;
308
310
}
309
-
311
+
310
312
bool IRadioButton . IsChecked
311
313
{
312
314
get => IsChecked ;
@@ -319,7 +321,7 @@ public string? GroupName
319
321
get => GetValue ( GroupNameProperty ) ;
320
322
set => SetValue ( GroupNameProperty , value ) ;
321
323
}
322
-
324
+
323
325
/// <summary>
324
326
/// Gets or sets a value that indicates whether the <see cref="MenuItem"/> has a submenu.
325
327
/// </summary>
@@ -413,15 +415,16 @@ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e
413
415
{
414
416
SetCurrentValue ( HotKeyProperty , _hotkey ) ;
415
417
}
416
-
418
+
417
419
base . OnAttachedToLogicalTree ( e ) ;
418
420
419
- if ( Command != null )
421
+ ( var command , var parameter ) = ( Command , CommandParameter ) ;
422
+ if ( command is not null )
420
423
{
421
- Command . CanExecuteChanged += CanExecuteChanged ;
424
+ command . CanExecuteChanged += CanExecuteChangedHandler ;
422
425
}
423
-
424
- TryUpdateCanExecute ( ) ;
426
+
427
+ TryUpdateCanExecute ( command , parameter ) ;
425
428
426
429
var parent = Parent ;
427
430
@@ -437,7 +440,7 @@ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e
437
440
protected override void OnAttachedToVisualTree ( VisualTreeAttachmentEventArgs e )
438
441
{
439
442
base . OnAttachedToVisualTree ( e ) ;
440
-
443
+
441
444
TryUpdateCanExecute ( ) ;
442
445
}
443
446
@@ -454,7 +457,7 @@ protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs
454
457
455
458
if ( Command != null )
456
459
{
457
- Command . CanExecuteChanged -= CanExecuteChanged ;
460
+ Command . CanExecuteChanged -= CanExecuteChangedHandler ;
458
461
}
459
462
}
460
463
@@ -464,9 +467,10 @@ protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs
464
467
/// <param name="e">The click event args.</param>
465
468
protected virtual void OnClick ( RoutedEventArgs e )
466
469
{
467
- if ( ! e . Handled && Command ? . CanExecute ( CommandParameter ) == true )
470
+ ( var command , var parameter ) = ( Command , CommandParameter ) ;
471
+ if ( ! e . Handled && command is not null && command . CanExecute ( parameter ) == true )
468
472
{
469
- Command . Execute ( CommandParameter ) ;
473
+ command . Execute ( parameter ) ;
470
474
e . Handled = true ;
471
475
}
472
476
}
@@ -577,21 +581,25 @@ private void CloseSubmenus()
577
581
/// <param name="e">The event args.</param>
578
582
private static void CommandChanged ( AvaloniaPropertyChangedEventArgs e )
579
583
{
580
- if ( e . Sender is MenuItem menuItem &&
581
- ( ( ILogical ) menuItem ) . IsAttachedToLogicalTree )
584
+ var newCommand = e . NewValue as ICommand ;
585
+ if ( e . Sender is MenuItem menuItem )
586
+
582
587
{
583
- if ( e . OldValue is ICommand oldCommand )
588
+ if ( ( ( ILogical ) menuItem ) . IsAttachedToLogicalTree )
584
589
{
585
- oldCommand . CanExecuteChanged -= menuItem . CanExecuteChanged ;
586
- }
590
+ if ( e . OldValue is ICommand oldCommand )
591
+ {
592
+ oldCommand . CanExecuteChanged -= menuItem . CanExecuteChangedHandler ;
593
+ }
587
594
588
- if ( e . NewValue is ICommand newCommand )
589
- {
590
- newCommand . CanExecuteChanged += menuItem . CanExecuteChanged ;
595
+ if ( newCommand is not null )
596
+ {
597
+ newCommand . CanExecuteChanged += menuItem . CanExecuteChangedHandler ;
598
+ }
591
599
}
592
-
593
- menuItem . TryUpdateCanExecute ( ) ;
600
+ menuItem . TryUpdateCanExecute ( newCommand , menuItem . CommandParameter ) ;
594
601
}
602
+
595
603
}
596
604
597
605
/// <summary>
@@ -602,7 +610,8 @@ private static void CommandParameterChanged(AvaloniaPropertyChangedEventArgs e)
602
610
{
603
611
if ( e . Sender is MenuItem menuItem )
604
612
{
605
- menuItem . TryUpdateCanExecute ( ) ;
613
+ ( var command , var parameter ) = ( menuItem . Command , e . NewValue ) ;
614
+ menuItem . TryUpdateCanExecute ( command , parameter ) ;
606
615
}
607
616
}
608
617
@@ -621,21 +630,27 @@ private void CanExecuteChanged(object? sender, EventArgs e)
621
630
/// </summary>
622
631
private void TryUpdateCanExecute ( )
623
632
{
624
- if ( Command == null )
633
+ TryUpdateCanExecute ( Command , CommandParameter ) ;
634
+ }
635
+
636
+ [ System . Runtime . CompilerServices . MethodImpl ( System . Runtime . CompilerServices . MethodImplOptions . AggressiveInlining ) ]
637
+ private void TryUpdateCanExecute ( ICommand ? command , object ? parameter )
638
+ {
639
+ if ( command == null )
625
640
{
626
641
_commandCanExecute = ! _commandBindingError ;
627
642
UpdateIsEffectivelyEnabled ( ) ;
628
643
return ;
629
644
}
630
-
645
+
631
646
//Perf optimization - only raise CanExecute event if the menu is open
632
647
if ( ! ( ( ILogical ) this ) . IsAttachedToLogicalTree ||
633
648
Parent is MenuItem { IsSubMenuOpen : false } )
634
649
{
635
650
return ;
636
651
}
637
-
638
- var canExecute = Command . CanExecute ( CommandParameter ) ;
652
+
653
+ var canExecute = command . CanExecute ( parameter ) ;
639
654
if ( canExecute != _commandCanExecute )
640
655
{
641
656
_commandCanExecute = canExecute ;
@@ -720,7 +735,7 @@ private void IsCheckedChanged(AvaloniaPropertyChangedEventArgs e)
720
735
( MenuInteractionHandler as DefaultMenuInteractionHandler ) ? . OnCheckedChanged ( this ) ;
721
736
}
722
737
}
723
-
738
+
724
739
/// <summary>
725
740
/// Called when the <see cref="HeaderedSelectingItemsControl.Header"/> property changes.
726
741
/// </summary>
@@ -834,7 +849,7 @@ private void PopupClosed(object? sender, EventArgs e)
834
849
SelectedItem = null ;
835
850
}
836
851
837
- void ICommandSource . CanExecuteChanged ( object sender , EventArgs e ) => this . CanExecuteChanged ( sender , e ) ;
852
+ void ICommandSource . CanExecuteChanged ( object sender , EventArgs e ) => CanExecuteChangedHandler ( sender , e ) ;
838
853
839
854
void IClickableControl . RaiseClick ( )
840
855
{
0 commit comments