【问题标题】:WPF ICommand "Event Binding" - Getting the EventArgsWPF ICommand“事件绑定”-获取 EventArgs
【发布时间】:2017-10-27 13:07:55
【问题描述】:

问题:我有一个 WPF 项目,我想在其中使用 MVVM-pattern。我想使用 ICommand 来抽象事件。但问题是我无法取回我的 EventArgs,因为它位于命令“后面”(例如 OnDrop 路径)。

信息: ICommand 用于抽象事件,因为命令可以多次使用。命令更集中于背后的逻辑(MVVM-Pattern)。

我正在使用System.Windows.Interactivity 和中继命令。

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;
    }
}

我尝试了以下解决方案:

1.依赖对象的行为

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/

我无法取回我的事件参数。

2.关联对象和 OnAttached 的行为

https://wpftutorial.net/Behaviors.html

在这里我遇到了同样的问题,无法取回我的 EventArgs。

3.绑定方法

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>

这对我有用,但问题是 WPF 在 XAML 或代码中方法名称已更改时不会更改名称。


问题: 如何在不使用框架或库的情况下以有效的方式让我的 EventArgs 退出我的命令?

【问题讨论】:

  • CommandParameter 通常用于将参数传递给视图模型。但还有其他方法。您遇到问题的具体事件是什么?为什么你需要它的EventArgs
  • 是的,例如我需要DragEventArgs 来获取被删除文件的路径。
  • 只需在代码后面附加事件处理程序并从那里调用您的命令。像 drop 这样的事件是视图的一部分,它们的事件参数也是视图的一部分。你不想让你的视图模型依赖于视图特定的类。
  • 实际上,如果您在代码中附加处理程序,则不需要命令。在后面的代码中提取文件 drom DragEventArgs 并在视图模型中调用方法,例如 viewModel.ProcessDroppedFiles(files)

标签: c# wpf mvvm


【解决方案1】:

从事件开始。一旦您拥有处理事件的代码,通常可以将其重构为可重用的附加行为。

例如这是一个分配命令,它从拖放项接收传递的字符串。

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);
                }
            };
        }));
}

我猜它会与您的文件路径一起使用,只需在绑定中使用它:

<SomeUIElement AllowDrop="True"
               Background="Transparent"
               local:Behaviors.Drop="{Binding SomeCommand}" />

在哪里

SomeCommand = new RelayCommand(o => true, o => MessageBox.Show($"File {o}"));

另一点是开始拖动。您还可以使用附加行为,例如一个传递文本(可以是绑定):

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);
            };
        }));
}

用法是:

<Something local:Behaviors.Drag="text" ... />

【讨论】:

    【解决方案2】:

    如何在不使用框架或库的情况下以有效的方式让 EventArgs 退出我的命令?

    System.Windows.Interactivity.dllMicrosoft.Expressions.Interactions.dll 中都没有将 EventArgs 作为命令参数传递给命令的类。

    MvvmLight 中有一个 EventToCommand 类可供您使用: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/

    另一种选择是实现您自己的自定义TriggerAction&lt;FrameworkElement&gt;。你可以看看 MvvmLight 的实现:https://github.com/paulcbetts/mvvmlight/blob/dce4e748c538ed4e5f5a0ebbfee0f54856f52dc6/V3/GalaSoft.MvvmLight/GalaSoft.MvvmLight.Extras%20(NET35)/Command/EventToCommand.cs

    或者您可以在视图的代码隐藏中处理Drop 事件,然后按照@Evk 的建议从事件处理程序中调用您的命令:

    private void OnDrop(object sender, DragEventArgs e)
    {
        var viewModel = DataContext as YourViewModelType;
        if (viewModel != null)
            viewModel.OnRectangleDrop.Execute(e);
    }
    

    当然,您可以从DragEventArgs 中提取任何值并将其传递给命令。

    【讨论】:

    • 虽然我的建议是不要让 viewmodel 依赖于 WPF 特定的类 (DragEventArgs)。我想不使用 args 而是使用实际数据(如删除的文件路径)调用命令。
    • 那么从事件处理程序中的事件参数中提取数据呢?
    猜你喜欢
    • 2012-06-11
    • 2021-12-24
    • 2015-04-02
    • 2015-11-13
    • 2018-09-05
    • 2013-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多