Coder949
Coder949

Reputation: 1007

WPF ICommand "Event Binding" - Getting the EventArgs

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

https://gayotfow.wordpress.com/2014/04/27/to-implement-drag-and-drop-in-mvvm-without-much-experience-in-wpf-you-can-refer-to-5-steps-i-will-outline-these/

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

Answers (2)

Sinatr
Sinatr

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

mm8
mm8

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

Related Questions