none
Executing RoutedCommand via Attached Behaviour

    Frage

  • I am trying to convert a file drag&drop event into a routed command that I have registered via static class and bound to my mainWindow via CommandBindings. I can get this to work if I bind to a delegate (RelayCommand) command implemented in the ViewModel, but I cannot seem to get this to work if I use a RoutedCommand (although the same RoutedCommand does work somewhere else in the MainWindow).

    So, I am wondering whether it is possible to execute RoutedCommands from within an attached behaviour? Are there restrictions and/or workarounds that should be used?

    I am using 'command.Execute(droppedFilePath);' in my Attached Behavior - do I need something else to execute a RoutedCommand?
    If yes, is it possible to recognize in the attached behavior whether a RoutedCommand is bound or whether a delegate is bound -and execute the corresponding code?

    Here is the code I am using:

    The Attached behaviour

      using System.Windows.Input;
      using System.Windows;
    
      /// <summary>
      /// Source: http://stackoverflow.com/questions/1034374/drag-and-drop-in-mvvm-with-scatterview
      /// </summary>
      public static class PropertyHelper
      {
        public static readonly DependencyProperty DropCommandProperty = DependencyProperty.RegisterAttached(
            "DropCommand",
            typeof(ICommand),
            typeof(PropertyHelper),
            new PropertyMetadata(null, OnDropCommandChange));
    
        public static void SetDropCommand(DependencyObject source, ICommand value)
        {
          source.SetValue(DropCommandProperty, value);
        }
    
        public static ICommand GetDropCommand(DependencyObject source)
        {
          return (ICommand)source.GetValue(DropCommandProperty);
        }
    
        private static void OnDropCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
          ICommand command = e.NewValue as ICommand;
          UIElement uiElement = d as UIElement;
          if (command != null && uiElement != null)
          {
            uiElement.Drop += (sender, args) =>
            {
              DragEventArgs eArgs = args as DragEventArgs;
    
              if (eArgs == null)
                return;
    
              if (eArgs.Data.GetDataPresent(DataFormats.FileDrop))
              {
                string[] droppedFilePaths =
                eArgs.Data.GetData(DataFormats.FileDrop, true) as string[];
    
                foreach (string droppedFilePath in droppedFilePaths)
                {
                  command.Execute(droppedFilePath);
                  ///AppCommand.LoadFile.Execute(droppedFilePath, (System.Windows.IInputElement)sender);
                }
              }
            };
          }
    
          // todo: if e.OldValue is not null, detatch the handler that references it
        }
      }

    The Attached behaviour in the MainWindow with binding to delegate in ViewModel

    <Window ...
    AllowDrop="True"
            behav:PropertyHelper.DropCommand="{Binding LoadFileCommand}">
    
    ...
    
    </Window>

    The Attached behaviour in the MainWindow with binding to the RoutedCommand (problem the bound code is never executed)

    <Window
    ...
    
    AllowDrop="True"
    behav:PropertyHelper.DropCommand="cmd:AppCommand.LoadFile">
    
    ...
    </Window>

    By RoutedCommand I mean something like this

    public static RoutedUICommand LoadFile;
    
    AppCommand.LoadFile = new RoutedUICommand("Open ...", "LoadFile", typeof(AppCommand), inputs);

    and binding code to it is something like this

    MainWindow.CommandBindings.Add
    (new CommandBinding(AppCommand.LoadFile,
          (s, e) =>
          {
            if (e == null)
              return;
    
            string filename = e.Parameter as string;
    
            if (filename == null)
              return;
    
            this.Open(filename);
          }));
    
    My problem is that the last code snippet is never executed when I use the RoutedCommand in the Attached Behaviour. Does anyone know why?

    Mittwoch, 20. Juni 2012 08:48

Antworten

  • Have you implemented the CommandBinding for cmd:AppCommand.LoadFile somewhere? For the RoutedUICommand, you must add CommandBinding for it. In this case you should add command binding into Window.CommandBindings.

    So, I am wondering whether it is possible to execute RoutedCommands from within an atatched behaviour? Are there restrictions and/or workarounds that should be used?

    Yes, but you have to specify the target IInputElement so that It can find the handler from the VisualTree. The command finding process bubbles from the target IInputElement to the top of the VisualTree (That's the reason why it is called RoutedCommand)

    Here I add the CommandBindings

    My MainWindow.xaml

    <Window x:Class="WPF70511_2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
            xmlns:local="clr-namespace:WPF70511_2"
            Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" AllowDrop="True"
            local:PropertyHelper.DropCommand="local:FileCommands.LoadFile"
            >
        <Window.CommandBindings>
            <CommandBinding Command="local:FileCommands.LoadFile" Executed="CommandBinding_Executed"/>
        </Window.CommandBindings>
    ...

    I am using 'command.Execute(droppedFilePath);' in my Attached Behaviour - do I need something else to execute a RoutedCommand?
    If yes, is it possible to recognize in the attached behaviour whether a routed event is bound or whether a delegate is bound -and execute the corresponding code?

    I rewrite your code a little bit to support RoutedCommand

    PropertyHelper.cs

    public static class PropertyHelper { public static readonly DependencyProperty DropCommandProperty = DependencyProperty.RegisterAttached( "DropCommand", typeof(ICommand), typeof(PropertyHelper), new PropertyMetadata(null, OnDropCommandChange)); public static void SetDropCommand(DependencyObject source, ICommand value) { source.SetValue(DropCommandProperty, value); } public static ICommand GetDropCommand(DependencyObject source) { return (ICommand)source.GetValue(DropCommandProperty); } private static void OnDropCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement uiElement = d as UIElement;

    // Remove the handler if it exist to avoid memory leak uiElement.Drop -= UIElement_Drop; var command = e.NewValue as ICommand; if (command != null) { uiElement.Drop += UIElement_Drop; } } private static void UIElement_Drop(object sender, DragEventArgs e) { UIElement uiElement = sender as UIElement; ICommand command = GetDropCommand(uiElement); if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[]; foreach (string droppedFilePath in droppedFilePaths) { if (command is RoutedCommand) { (command as RoutedCommand).Execute(droppedFilePath, uiElement); } else { command.Execute(droppedFilePath); } ///AppCommand.LoadFile.Execute(droppedFilePath, (System.Windows.IInputElement)sender); } } } }

    Read this http://msdn.microsoft.com/en-us/magazine/cc785480.aspx . Once understanding how RoutedCommand works, you will be able to answer all your question.




    • Bearbeitet Kane Nguyen Mittwoch, 20. Juni 2012 10:19
    • Als Antwort markiert Dirkster9999 Mittwoch, 20. Juni 2012 11:47
    Mittwoch, 20. Juni 2012 10:13

Alle Antworten

  • Have you implemented the CommandBinding for cmd:AppCommand.LoadFile somewhere? For the RoutedUICommand, you must add CommandBinding for it. In this case you should add command binding into Window.CommandBindings.

    So, I am wondering whether it is possible to execute RoutedCommands from within an atatched behaviour? Are there restrictions and/or workarounds that should be used?

    Yes, but you have to specify the target IInputElement so that It can find the handler from the VisualTree. The command finding process bubbles from the target IInputElement to the top of the VisualTree (That's the reason why it is called RoutedCommand)

    Here I add the CommandBindings

    My MainWindow.xaml

    <Window x:Class="WPF70511_2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
            xmlns:local="clr-namespace:WPF70511_2"
            Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" AllowDrop="True"
            local:PropertyHelper.DropCommand="local:FileCommands.LoadFile"
            >
        <Window.CommandBindings>
            <CommandBinding Command="local:FileCommands.LoadFile" Executed="CommandBinding_Executed"/>
        </Window.CommandBindings>
    ...

    I am using 'command.Execute(droppedFilePath);' in my Attached Behaviour - do I need something else to execute a RoutedCommand?
    If yes, is it possible to recognize in the attached behaviour whether a routed event is bound or whether a delegate is bound -and execute the corresponding code?

    I rewrite your code a little bit to support RoutedCommand

    PropertyHelper.cs

    public static class PropertyHelper { public static readonly DependencyProperty DropCommandProperty = DependencyProperty.RegisterAttached( "DropCommand", typeof(ICommand), typeof(PropertyHelper), new PropertyMetadata(null, OnDropCommandChange)); public static void SetDropCommand(DependencyObject source, ICommand value) { source.SetValue(DropCommandProperty, value); } public static ICommand GetDropCommand(DependencyObject source) { return (ICommand)source.GetValue(DropCommandProperty); } private static void OnDropCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement uiElement = d as UIElement;

    // Remove the handler if it exist to avoid memory leak uiElement.Drop -= UIElement_Drop; var command = e.NewValue as ICommand; if (command != null) { uiElement.Drop += UIElement_Drop; } } private static void UIElement_Drop(object sender, DragEventArgs e) { UIElement uiElement = sender as UIElement; ICommand command = GetDropCommand(uiElement); if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[]; foreach (string droppedFilePath in droppedFilePaths) { if (command is RoutedCommand) { (command as RoutedCommand).Execute(droppedFilePath, uiElement); } else { command.Execute(droppedFilePath); } ///AppCommand.LoadFile.Execute(droppedFilePath, (System.Windows.IInputElement)sender); } } } }

    Read this http://msdn.microsoft.com/en-us/magazine/cc785480.aspx . Once understanding how RoutedCommand works, you will be able to answer all your question.




    • Bearbeitet Kane Nguyen Mittwoch, 20. Juni 2012 10:19
    • Als Antwort markiert Dirkster9999 Mittwoch, 20. Juni 2012 11:47
    Mittwoch, 20. Juni 2012 10:13
  • Thanks for your quick answer. I was just missing the if statement at the bottom:

     if (command is RoutedCommand)
    {
      (command as RoutedCommand).Execute(droppedFilePath, uiElement);
    }
    else
    {
      command.Execute(droppedFilePath);
    }
    which means basically that whenever yoiu have an ICommand property its a good practice to check whether it can be executed as RoutedCommand or the 'normal' delegate viemodel bound (RelayCommand) command. I am printing and studying the MSDN article now...

    Mittwoch, 20. Juni 2012 11:50