Executing RoutedCommand via Attached Behaviour
-
Mittwoch, 20. Juni 2012 08:48
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?
- Bearbeitet Dirkster9999 Mittwoch, 20. Juni 2012 10:09
Alle Antworten
-
Mittwoch, 20. Juni 2012 10:13
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
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.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); } } } }
- Bearbeitet Kane Nguyen Mittwoch, 20. Juni 2012 10:15
- Bearbeitet Kane Nguyen Mittwoch, 20. Juni 2012 10:19
- Als Antwort markiert Dirkster9999 Mittwoch, 20. Juni 2012 11:47
-
Mittwoch, 20. Juni 2012 11:50
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...

