Home > database >  How to implement event EventHandler ICommand.CanExecuteChanged.add? (C#)
How to implement event EventHandler ICommand.CanExecuteChanged.add? (C#)

Time:07-19

I was following this video on how to create a Windows.InputBinding for the Exit MenuItem:

https://www.youtube.com/watch?v=bdmVWGjpA_8

Here's my attempt, But I'm wondering how to implement:

    event EventHandler ICommand.CanExecuteChanged add { }???

Seems this CanExecureChanged is a new requirement for ICommand Interface in DotNet 6 WPF??

Heres's part of my c# MainWindow.cs:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApp2
{
    public class ExitKey : ICommand
    {
        private MainWindow mw;

        public ExitKey(MainWindow mw0) { mw = mw0;}

        event EventHandler ICommand.CanExecuteChanged {
            add {
                //How to Implment this???
                throw new NotImplementedException();
            }

            remove {
                throw new NotImplementedException();
            }
        }

        bool ICommand.CanExecute(object parameter) {
            return true;
        }

        void ICommand.Execute(object parameter) {
              mw.menu1_file_exit.RaiseEvent(new 
              RoutedEventArgs(MenuItem.ClickEvent));
        }
    }

    public class KeyContext
    {
        private MainWindow mw;

        public KeyContext(MainWindow mw0) { mw = mw0; }

        // {Binding MyExitCommand}
        public ICommand MyExitCommand
        {
            get { return new ExitKey(mw); }
        }

    } //Class

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new KeyContext(this);            
        }

        private void menu1_file_exit_Click(
          object sender, RoutedEventArgs e)
        {
            MessageBox.Show("EXIT!");
        }
    } //Class

} //Namespace        
<Window x:Class="WpfApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Garbo" Height="900" Width="900" MinHeight="240" MinWidth="320">
    <Window.InputBindings>
        <KeyBinding Key="F5"
                    Command="{Binding MyExitCommand}"
        />
    </Window.InputBindings>

    <DockPanel>
        <Menu Name="menu1" DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem 
                    Name="menu1_file_exit"       
                    Header="E_xit" 
                    Command="{Binding ExitCommand}" 
                    InputGestureText="F5"  
                    Click="menu1_file_exit_Click"/>
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

CodePudding user response:

You implement it by forwarding the calls to your own event handler.

However you don't really need to implement your own commands, there are plenty already done out there. The MVVM Community Toolkit has its RelayCommand and the one I use is ReactiveUI's ReactiveCommand.

CodePudding user response:

In WPF you would usually delegate event subscribers to the CommandManager.RequerySuggested event:

public event EventHandler CanExecuteChanged
{
  add 
  { 
    CommandManager.RequerySuggested  = value; 
  }
  remove 
  { 
    CommandManager.RequerySuggested -= value; 
  }
}

This way the CommandManager will be responsible to raise the ICommand.CanExecuteChanged event implicitly.
The CommandManager would observe the UI interaction like mouse move and focus changes and will raise the CommandManager.RequerySuggested event to notify the ICommandSource, that usually listens to the ICommand.CanExecuteChanged event. The command source will then call ICommand.CanExecute.

You can trigger the CommandManager.RequerySuggested event (and therefore the ICommand.CanExecuteChanged event) explicitly by calling CommandManager.InvalidateRequerySuggested.

An alternative implementation is to bypass the CommandManager and allow the command target to raise this event explicitly: public

public event EventHandler CanExecuteChanged;
public void InvalidateCommand() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);

You can follow the Microsoft Docs: Relaying Command Logic example on how to implement a reusable RelayCommand. This example generally shows how to implement ICommand properly.

Note that in your case the ICommand ExitKey should be a RoutedCommand: How to: Create a RoutedCommand.

RoutedCommand also allows to register a KeyGesture with the command:

partial class MainWindow : Window
{
  public static RoutedCommand MyExitCommand = new RoutedCommand();

  public MainWindow()
  {
    InitializeComponent();

    var exitCommandKeyGesture = new KeyGesture(Key.F5);
    MyExitCommand.InputGestures.Add(exitCommandKeyGesture); 

    var commandBinding = new CommandBinding(MyExitCommand, ExecutedMyExitCommand, CanExecuteMyExitCommand);
    this.CommandBindings.Add(commandBinding); 
  }
}
<Window>
    <DockPanel>
        <Menu Name="menu1" DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem x:Name="menu1_file_exit"       
                          Header="E_xit" 
                          Command="{x:Static local:MyExitCommand}" 
                          InputGestureText="F5" />
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>
  • Related