Reputation: 1007
Problem: I have a WPF-project where I want to use MVVM-pattern. I want to use ICommand to abstract the events. But the problem is that I can not get my EventArgs back because it is "behind" the command (for example OnDrop the path).
Information: ICommand is used to abstract the events because commands can be used multiple times. Commands are more concentrated on the logic behind (MVVM-Pattern).
I am using System.Windows.Interactivity
and Relay Command.
RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
I tried the following solutions:
1. Behaviors with Dependency Object
I could not get back my Event Args.
2. Behaviors with Associated Object and OnAttached
https://wpftutorial.net/Behaviors.html
Here I had the same problem and could not get back my EventArgs.
3. Binding on methods
MainView.xaml
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
<i:EventTrigger EventName="Drop">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="OnRectangleDrop"/>
</i:EventTrigger>
That worked for me but here was the problem that WPF does not change the names when in XAML or in Code the method name has changed.
Question: How can I get my EventArgs back out of my command in an efficient way without using a framework or library ?
Upvotes: 1
Views: 2903
Reputation: 21999
Start with events. Once you have the code which works with events it can be typically refactored to reusable attached behaviors.
E.g. here is one to assign command, which receive passed string from drag-and-dropped item.
public static partial class Behaviors
{
public static ICommand GetDrop(DependencyObject obj) => (ICommand)obj.GetValue(DropProperty);
public static void SetDrop(DependencyObject obj, ICommand value) => obj.SetValue(DropProperty, value);
public static readonly DependencyProperty DropProperty =
DependencyProperty.RegisterAttached("Drop", typeof(ICommand), typeof(Behaviors), new PropertyMetadata(null, (d, e) =>
{
var element = d as UIElement;
element.Drop += (s, a) =>
{
if (a.Data.GetDataPresent(DataFormats.StringFormat))
{
var command = (ICommand)e.NewValue;
var data = (string)a.Data.GetData(DataFormats.StringFormat);
if (command.CanExecute(data))
command.Execute(data);
}
};
}));
}
I guess it will work with your file path, simply use it in the binding:
<SomeUIElement AllowDrop="True"
Background="Transparent"
local:Behaviors.Drop="{Binding SomeCommand}" />
where
SomeCommand = new RelayCommand(o => true, o => MessageBox.Show($"File {o}"));
Another point is to start dragging. You can also use attached behavior, e.g. one which pass text (can be a binding):
public static partial class Behaviors
{
public static string GetDrag(DependencyObject obj) => (string)obj.GetValue(DragProperty);
public static void SetDrag(DependencyObject obj, string value) => obj.SetValue(DragProperty, value);
public static readonly DependencyProperty DragProperty =
DependencyProperty.RegisterAttached("Drag", typeof(string), typeof(Behaviors), new PropertyMetadata(null, (d, e) =>
{
var element = d as UIElement;
element.MouseMove += (s, a) =>
{
if (a.LeftButton == MouseButtonState.Pressed)
DragDrop.DoDragDrop(element, e.NewValue, DragDropEffects.Copy);
};
}));
}
Usage is:
<Something local:Behaviors.Drag="text" ... />
Upvotes: 1
Reputation: 169200
How can I get my EventArgs back out of my command in an efficient way without using a framework or library ?
There is no class in either System.Windows.Interactivity.dll
nor Microsoft.Expressions.Interactions.dll
that passes the EventArgs as a command parameter to the command.
There is a EventToCommand
class in MvvmLight that you can use: http://blog.galasoft.ch/posts/2014/01/using-the-eventargsconverter-in-mvvm-light-and-why-is-there-no-eventtocommand-in-the-windows-8-1-version/.
The other option would be to implement your own custom TriggerAction<FrameworkElement>
. You could take a look at the MvvmLight implementation: https://github.com/paulcbetts/mvvmlight/blob/dce4e748c538ed4e5f5a0ebbfee0f54856f52dc6/V3/GalaSoft.MvvmLight/GalaSoft.MvvmLight.Extras%20(NET35)/Command/EventToCommand.cs
Or you could handle the Drop
event in the code-behind of the view and simply invoke your command from the event handler as suggested by @Evk:
private void OnDrop(object sender, DragEventArgs e)
{
var viewModel = DataContext as YourViewModelType;
if (viewModel != null)
viewModel.OnRectangleDrop.Execute(e);
}
Of course you could extract any value from the DragEventArgs
and pass this one to the command.
Upvotes: 1