wpfcallbackcommandbinding

Is there a way to get a callback when a Button completes execution of ICommand?


Is there a way I can attach a handler (callback) in buttons of my control (which eventually execute the ICommand) so that my control knows when a command execution is completed?


Solution

  • You can create abstract CallbackableCommand that would raise callback method.

    abstract class CallbackableCommand : ICommand
      {
        private IInputElement getRaisedElement()
        {
          return Keyboard.FocusedElement;      
        }
    
        public void Execute(object parameter)
        {
          ExecuteImpl(parameter);
          var element = getRaisedElement();
          if(element == null) return;
    
          //var ci = typeof(ExecutedRoutedEventArgs).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
          //var routedEventArgs = (RoutedEventArgs)ci.Invoke(new object[] { this, parameter });
          var routedEventArgs = new RoutedEventArgs();
          //routedEventArgs.RoutedEvent = CommandManager.ExecutedEvent;
          routedEventArgs.RoutedEvent = Callbackable.CommandExecutedEvent;
          routedEventArgs.Source = element;
          routedEventArgs.Handled = false;
    
          element.RaiseEvent(routedEventArgs);            
        }    
    
        public abstract void ExecuteImpl(object parameter);
    
        abstract public bool CanExecute(object parameter);
    
        abstract public event EventHandler CanExecuteChanged;
      }
    

    Inherit your command from CallbackableCommand and override CanExecute, CanExecuteChanged and ExecuteImpl(instead of Execute)

      class SimpleCommand : CallbackableCommand
      {
        public override void ExecuteImpl(object parameter)
        {
          MessageBox.Show("Simple command execute with parameter: " 
            + parameter ?? "null");
        }
    
        public override bool CanExecute(object parameter)
        {
          return true;
        }
    
        public override event EventHandler CanExecuteChanged;
      }
    

    Own element to specify CommandExecuted event:

      public class Callbackable : ContentControl
      {
        public static readonly RoutedEvent CommandExecutedEvent = EventManager.RegisterRoutedEvent(
            "CommandExecuted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Callbackable));
    
        // Provide CLR accessors for the event
        public event RoutedEventHandler CommandExecuted
        {
          add { AddHandler(CommandExecutedEvent, value); }
          remove { RemoveHandler(CommandExecutedEvent, value); }
        }
      }
    

    Edit: In your control specify Callbackable.CommandExecuted event

    <Grid>
        <Grid.Resources>
            <local:SimpleCommand x:Key="btnCommand" />            
        </Grid.Resources>
        <local:Callbackable>
            <Button Command="{StaticResource btnCommand}"   
                    CommandParameter="param"                
                    local:Callbackable.CommandExecuted="Button_Executed" >
                Click me
            </Button>
        </local:Callbackable>
    </Grid>
    

    Executed event handler:

    private void Button_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      MessageBox.Show("Executed");
    }