wpf-mvvm的冗余ICommand类

本文关键字:ICommand 冗余 wpf-mvvm | 更新日期: 2023-09-27 18:30:03

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

在这篇文章中,我想维护的两件重要的事情是向命令传递参数的能力,如RelayCommand的第一个使用示例所示。第二,启用/禁用第二个命令中的命令的能力。

所以在我的工具中,我有一个helper类。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));
    }
}

wpf-mvvm的冗余ICommand类

如果您使用像MVVM Lite这样的库,那么它将为您提供RelayCommand实现。无论哪种方式,当你不需要传递参数时,都可以使用非通用的,例如"Ok"按钮:

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

在这种情况下,您需要通过命令参数传入客户端对象:

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

当与事件触发器一起使用时,传递参数也很方便,例如,您可以添加这样的内容来拦截MainWindow的关闭事件:

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

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

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

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