【问题标题】:Is a command a sort of event handler or why does my button execute the command?命令是一种事件处理程序,还是我的按钮为什么要执行命令?
【发布时间】:2017-03-24 09:29:23
【问题描述】:

我正在学习 wpf、delegate、event 并且我对什么做什么有一些线索,但是在实现 ICommand 时我有点迷茫

我有一个类可以像这样实现ICommand 接口

class RelayCommand : ICommand
{
    private Action<object> _execute;
    private Func<object, bool> _canExecute;

    public RelayCommand(Action<object> execute) : this (execute, null)
    {

    }

    public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
    {
        this._execute = execute;
        this._canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        //throw new NotImplementedException();
        return true;
    }

    public void Execute(object parameter)
    {
        //throw new NotImplementedException();
        this._execute(parameter);
    }

    public void OnCanExecute()
    {
        if (CanExecuteChanged != null)
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }
    }

然后是我使用 i 的 ViewModel。

class PersonViewModel
{
    public ICommand ICommandPresenter { get; set; }
    public PersonModel PM;
    private string _btnString = "";

    #region Propertys
    public string ButtonString
    {
        get {return _btnString; }
        set
        {
            if (_btnString.Equals(value))
            {
                return;
            }

            _btnString = value;
        }
    }
    public string Name { get { return PM.Name; } set
        {
            PM.Name = value;
        }
     }
    #endregion Propertys
    public PersonViewModel()
    {
        PM = new PersonModel();
        ICommandPresenter = new RelayCommand(ChangeName);
    }

    public void ChangeName(object a)
    {
        string s = a as string;
        MessageBox.Show("Hello from PersonViewModel Commander: " + s);
    }
}

这就是让我感到困惑的地方。在RelayCommand 类中,我有一个事件CanExecuteChanged,但该事件从未执行/触发。从我对事件的阅读中了解到,您“不需要”订阅某个事件,但如果您要拥有一个事件,您至少应该在代码中的某个位置执行它。但是我的代码中没有它,但由于某种原因,我的按钮仍然执行我命令它执行的操作。我仍然明白我已经点击了该按钮,但我的代码中没有任何订阅该按钮的内容。

  • 为什么我的按钮即使没有连接事件也能执行我的代码?
  • 由于我没有任何订阅者连接到CanExecuteChanged 事件,它变得无用了吗?
  • 命令是否像事件一样起作用?如果有,请描述从点击按钮到执行命令的整个过程。

【问题讨论】:

    标签: c# wpf events


    【解决方案1】:

    CanExecuteChanged 是 ICommand 类的成员,为了简化事情,wpf 框架使用它来根据 CanExecute() 方法的结果启用/禁用您的按钮。 CanExecute 并不严格于您单击按钮时要执行的代码,而是在合法执行的条件下。

    Command 执行您的代码,因为您在此处发送 指向您的方法(ChangeName 方法)的指针

    ICommandPresenter = new RelayCommand(ChangeName);

    所以你根本没有使用CanExecuteChange,因为你正在调用这个构造函数:

    public RelayCommand(Action&lt;object&gt; execute).

    要拥有 CanExecute,您必须调用接受 CanExecute 谓词的重载构造函数:

    public RelayCommand(Action&lt;object&gt; execute, Func&lt;object,bool&gt; canExecute)

    要调用它,只需传递一些返回 bool 作为第二个参数的函数:

    ICommandPresenter = new RelayCommand(ChangeName, ()=&gt;MyCustomLogicWhenButtonIsActive());

    【讨论】:

    • 是的,我明白这一点,但我的问题(也许我说得不好)更多的是关于按钮的。能够让按钮触发事件的示例如下&lt;Button OnClick="My_function"&gt;,这解释为Button.Event += new NameOfEventHandler(My_function)。这是对我的按钮执行的命令吗?
    • 如果您将使用 OnClick 事件,那么您将被限制在代码隐藏 xaml.cs 文件中指示 My_Function。拥有业务逻辑是一种不好的做法,因为您将其与 UI 表示逻辑混合在一起。当您使用 ICommands 时,这意味着您正在使用 MVVM,因此所有业务逻辑都在 ViewModel 中隔离。通过绑定进行连接。所以你的代码看起来像
    • 命令没有对你的按钮做任何事情。有一些 WPF“魔法”,在按钮点击内部检查是否有与被点击的按钮相关联的命令,如果有,则调用该命令。
    • 有没有办法理解这个“魔法”?因为我认为这让我很难理解它是如何工作的。因为使用public void Execute(object parameter) { this._execute(parameter); } 没有意义。我知道它是一个代表,但是单击时调用Action 的按钮?为什么你需要按钮来拥有CanExecute = false
    【解决方案2】:

    根据我在.Net的源代码中看到的,分配给按钮Command属性的命令按如下方式执行:

    1. 单击按钮会调用按钮的OnClick() 方法(位于按钮的ButtonBase 基类中)。
    2. OnClick() 方法 (see source here) 调用Commands.CommandHelpers.ExecuteCommandSource() 方法,将this 作为命令源参数传递。
    3. ExecuteCommandSource() (see source here) 进一步调用CriticalExecuteCommandSource() 方法,将相同的命令源作为参数传递。
    4. 最后,CriticalExecuteCommandSource() 方法(see source here)访问命令源的Command 成员,并且:
      • 检查命令的CanExecute()方法是否返回true
      • 如果是true,则调用命令的Execute()方法。
    5. (如果您使用 RelayCommand 实现,那么显然,RelayCommand 类将调用中继到您在实例化它时传递给其构造函数的特定方法。)

    所以,回答您的问题是否通过事件触发命令:

    来源表示OnClick()方法直接执行命令,而不是通过事件处理。正如您在源代码中看到的,此方法确实引发了Click 事件,但它与命令执行分开。

    尽管如此,框架如何首先调用 OnClick() 方法仍然没有得到解答;我对此一无所知。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      • 2012-05-27
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      相关资源
      最近更新 更多