【问题标题】:redundant ICommand class for wpf mvvmwpf mvvm 的冗余 ICommand 类
【发布时间】:2016-01-12 03:55:48
【问题描述】:

我仍在学习 wpf,但是我熟悉如何在 wpf c# 中设置 mvvm。然而,当涉及到 ICommand/RelayCommand 的东西时,它对我来说有点令人困惑。在过去的几个月里,我编译了一些 ICommand 类的实现,以创建我的工具。但是,我现在已经阅读了几篇文章并且已经查看了足够长的代码,我正在寻找可以帮助我的人并简单地说这里发生了什么,如果是这样,我如何组合/清理这些类。目前代码似乎是多余的,我不确定如何优化它。希望这不是要求太多。谢谢。

我想在此维护的两件重要的事情是能够将参数传递给命令,如 RelayCommand 的第一个用法示例中所示。其次,启用/禁用命令的能力,如第二个命令所示。

所以在我的工具中我有这个助手类。 1.我没有得到这个RelayCommand.cs中两个类之间的使用差异。有一个公共类和一个内部类。 2. 两者都需要还是可以结合使用? RelayCommand.cs

using System;
using System.Windows.Input;

namespace WpfApplication1.Helper
{
    public class RelayCommand<T> : ICommand
    {
        private readonly Action<T> execute;
        private readonly Predicate<T> canExecute;

        public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            this.execute = execute;
            this.canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (parameter == null)
            {
                return true;
            }
            else
            {
                return canExecute == null || canExecute((T)parameter);
            }
        }

        public void Execute(object parameter)
        {
            execute((T)parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }

    // added
    internal class RelayCommand : ICommand
    {
        private readonly Predicate<object> canExecute;
        private readonly Action<object> execute;

        public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            this.execute = execute;
            this.canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return canExecute == null || canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            execute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
}

我在名为 Customer.cs 的类对象中使用 RelayCommand.cs 的示例

private ICommand addNewLicense_Command;
public ICommand AddNewLicense_Command
{
    get
    {
        return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
        {
            AddNewLicense_Execute(n);
        }));
    }
}

那么在我的 MainViewModel.cs 中,我在上面提到的 Helper 类所属的同一个项目中还有另一个 ICommand 类。这门课有必要吗?它看起来与 RelayCommand 类非常相似。

public class CommandHandler : ICommand
{
    private Action _action;
    private bool _canExecute;
    public CommandHandler(Action action, bool canExecute)
    {
        _action = action;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _action();
    }
}

我在 MainViewModel.cs 中使用 CommandHandler 的示例

private ICommand addNewUser_Command;
public ICommand AddNewUser_Command
{
    get
    {
        return addNewUser_Command ?? (addNewUser_Command = new CommandHandler(() => AddNewUser_Execute(), true));
    }
}

【问题讨论】:

  • 只保留一个类中继命令。无需保留其他类。将其保持在内部或公开状态取决于您的使用模式。如果您想将中继命令保留在不同的程序集中,请将其公开,如果它位于存在 VM 的同一程序集中,请将其设为内部
  • @SatyakiChatterjee 在 RelayCommand.cs 代码块内的两个类有什么区别?我注意到一个类以 结尾,而另一个则没有。一个比另一个有什么好处?我是否发现省略一个而只保留另一个?最后,无论我决定保留哪一个,我如何使用它并使用 can execute 控制它的启用或禁用。
  • 那么以 结尾的类是泛型类,而以 object 结尾的类不是泛型类。最好将类与 一起使用。使用 T 您可以使用任何使您的类更通用的类型。但是对于对象,您只能使用对象。我会要求你看看 c# 泛型.. 不管怎样,用 one
  • 你的意思是我只能使用对象?您的意思是我使用中继命令的每个团队都必须将对象传递给它吗?即使我不需要?
  • Action 指向返回类型为 void 并接受一个参数的方法。 T 表示泛型类型。您可以传递字符串、对象、自定义类甚至 null(如果您不需要任何特定情况的参数)。请查看有关泛型、动作和谓词的文档

标签: c# wpf


【解决方案1】:

如果您使用像 MVVM Lite 这样的库,那么它将为您提供 RelayCommand 实现。当您不需要传入的参数时,无论哪种方式都使用非泛型,例如“确定”按钮:

public ICommand OkCommand { get { return new RelayCommand(Ok); } }
protected virtual void Ok()
{
    // ... do something ...
}

相关的 XAML 类似于:

<Button Content="Ok" Command="{Binding OkCommand}" IsDefault="True" />

要传递参数时使用泛型:

public ICommand OpenClientCommand { get { return new RelayCommand<Client>(OnOpenClient); } }
private void OnOpenClient(Client client)
{
    // ... do something with client ...
}

这种情况下需要通过命令参数传入一个Client对象:

<Button Content="Open" Command="{Binding OpenClientCommand}" CommandParameter="{Binding SelectedClient}"/>

与事件触发器一起使用时传递参数也很方便,例如你可以添加这样的东西来拦截你的 MainWindow 的 Closing 事件:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

此触发器将消息参数传递到您的处理程序中,这样您就有机会在用户尚未保存更改时取消它:

public ICommand ClosingCommand { get { return new RelayCommand<CancelEventArgs>(OnClosing); } }
private void OnClosing(CancelEventArgs args)
{
    if (!PromptUserForClose())
        args.Cancel = true;
}

【讨论】:

    【解决方案2】:

    您所拥有的 RelayCommand 就是您所需要的。如果您想禁用该命令,您可以在构造函数中传递一个方法:

    return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
        {
            AddNewLicense_Execute(n);
        },AllowAddNeLicense));
    
    ...
    
    bool AllowAddNewLicense() 
    {
        return _allowAddEnabled;
    }
    

    称为 CommandHandler 的第二个类只是 ICommand 的另一个实现。不同之处在于您可以在构造函数中传递“启用”布尔值,这意味着除非您创建它的新实例,否则它将保持不变。在 RelayCommand 中,您可以传递一个每次都会执行的函数*,这样您就可以影响结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-02
      • 1970-01-01
      • 2011-09-25
      • 2013-08-05
      • 1970-01-01
      • 2010-11-30
      • 1970-01-01
      • 2012-07-06
      相关资源
      最近更新 更多