【问题标题】:Relaycommand ICommand.CanExecute not firingRelaycommand ICommand.CanExecute 未触发
【发布时间】:2017-02-24 23:01:13
【问题描述】:

我有以下问题:

我有一个带有执行 canexecute 方法的中继命令,但每次我调用 raisecanexecutechanged();它在 relaycommand 中调用 raisecanexecutechanged,为其设置一个新的委托,然后返回视图模型。

相同的设置适用于另一个视图模型。我检查了 1000 次有什么不同,但我什么也没找到。

如果您能帮助我,我将不胜感激。

    public RelayCommand UpdateAMSCommand { get; private set; }

    public AMSSettingsViewModel(IEventAggregator eventAggregator)
    {
        UpdateAMSCommand = new RelayCommand(OnUpdateAMS, CanUpdateAms);
        CustomAMSOffices.ListChanged += listChanged;
        CustomAMSContacts.ListChanged += listChanged;
    }

    private void listChanged(object sender, ListChangedEventArgs e)
    {
        if (sender != null)
        {
            if (sender is BindingList<CustomAMSOffice>)
            {
                BindingList<CustomAMSOffice> temp =  (BindingList<CustomAMSOffice>)sender;

                if (temp.Count > _amsOfficesItemsCounter)
                {
                    _amsOfficesItemsCounter = temp.Count;

                    for (int i = 0; i < temp.Count; i++)
                    {
                        temp[i].ErrorsChanged += RaiseCanExecuteChanged;
                    }
                }   
            }
            else if (sender is BindingList<CustomAMSContact>)
            {
                BindingList<CustomAMSContact> temp = (BindingList<CustomAMSContact>)sender;

                if (temp.Count > _amsContactsItemsCounter)
                {
                    _amsContactsItemsCounter = temp.Count;

                    for (int i = 0; i < temp.Count; i++)
                    {
                        temp[i].ErrorsChanged += RaiseCanExecuteChanged;
                    }
                }
            }
        }

        UpdateAMSCommand.RaiseCanExecuteChanged();
    }

    private void RaiseCanExecuteChanged(object sender, DataErrorsChangedEventArgs e)
    {
        UpdateAMSCommand.RaiseCanExecuteChanged();
    }

    private bool CanUpdateAms()
    {
        foreach (var cao in CustomAMSOffices)
        {
            if (!cao.Check() || cao.HasErrors)
            {
                return false;
            }
        }

        foreach (var cac in CustomAMSContacts)
        {
            if (!cac.Check() || cac.HasErrors)
            {
                return false;
            }
        }
        return true;
    }

编辑: 我使用的中继命令:https://github.com/briannoyes/WPFMVVM-StarterCode/blob/master/ZzaDashboard/ZzaDashboard/RelayCommand.cs

【问题讨论】:

  • 从你当前的代码很难说。你在使用Thread/Task 吗?尝试最小化/最大化您的窗口,看看这是否会带来一些变化?
  • 我只使用线程/任务在我的数据库和我的虚拟机之间进行通信。我已经试过了。
  • 您可以尝试的一件事是:await 或在CanExecute 方法中检查您的任务状态。
  • 在调试时我没有进入 canexecute 方法,这是我的问题,我为什么要等待呢?
  • ...然后显示您的RelayCommand 代码。

标签: c# wpf mvvm relaycommand canexecute


【解决方案1】:

好的,我只是复制粘贴一些我正在使用的代码,以便您可以将它们弹出到您的项目中并使用。

首先,RelayCommand() 类。我从this msdn page 中提取了这段代码:

public class RelayCommand : ICommand
{
    #region Fields
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;
    #endregion

    #region Constructors
    public RelayCommand(Action<object> execute) : this(execute, null) { }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion

    #region ICommand Members
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

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

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
    #endregion
}

现在我们的ModelView.cs 类需要从INotifyPropertyChanged 继承,并且将拥有我们的RaisePropertyChanged()。现在我通常把它做成一个它自己的文件,并让我所有的 ModelViews 都从它继承,这样代码就更简洁了,但你可以随心所欲。

我是这样设置的:

BaseViewModel.cs:

public class BaseViewModel : INotifyPropertyChanged
{
    internal void RaisePropertyChanged(string prop)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
    public event PropertyChangedEventHandler PropertyChanged;

    // Any other code we want all model views to have
}

现在我们的 MainViewModel.cs 将从 BaseViewModel 继承,添加我们的事件处理程序,然后运行它!

示例:ServerViewModel.cs

public class ServerViewModel : BaseViewModel
{
    public RelayCommand BroadcastMessageCommand { get; set; }

    private string _broadcastmessage;
    public string broadcastmessage
    {
        get { return _broadcastmessage; }
        set { _broadcastmessage = value; RaisePropertyChanged("broadcastmessage"); }
    }

    Server server;

    public ServerViewModel()
    {
        server = new Server();
        server.run();
        BroadcastMessageCommand = new RelayCommand(BroadcastMessage, CanBroadcast);
    }

    private bool CanBroadcast(object param)
    {
        if (string.IsNullOrWhiteSpace(broadcastmessage))
            return false;
        if (!server.running)
            return false;
        return true;
    }

    public void BroadcastMessage(object param)
    {
        server.BroadcastMessage(broadcastmessage);
        broadcastmessage = "";
        RaisePropertyChanged("broadcastmessage");
    }
}

现在 MainView.xaml 中与Command="{Binding broadcastmessage}" 绑定的任何内容都会相应更新。在我的情况下,我将此绑定到一个按钮,如果消息为空,或者我们未连接到服务器,则该按钮将被禁用。

希望这些代码示例足以让您朝着正确的方向前进!如果您对此有任何疑问,请告诉我。

【讨论】:

  • 非常感谢您的回答,但正如我在问题中所写,我在另一个视图模型中有完全相同的代码设置并且有效,我只是不知道为什么这不起作用,没有任何问题中继命令类或某事
  • @Revooo96 这似乎是一个基本步骤,但您是否检查以确保将 XAML 上的 datacontext 与您的模型视图绑定?
  • 是的,它的绑定正确,它正确地显示了我列表中的数据,只是没有提高我的中继命令的可执行性,我不知道为什么
  • 你的OnUpdateAMS()方法在哪里?
  • 我没有把它放在代码中因为它不可能是问题的根源,因为该方法永远不会被调用,那是我的问题
【解决方案2】:

让我们尽可能地简化代码,直到我们让它正常工作,然后我们会慢慢添加代码,直到找到导致问题的代码。

因此,让我们将其简化为准系统,看看我们是否取得了任何成功。试试这个代码:

public RelayCommand UpdateAMSCommand { get; private set; }

public AMSSettingsViewModel(IEventAggregator eventAggregator)
{
    UpdateAMSCommand = new RelayCommand(OnUpdateAMS, CanUpdateAms);
    CustomAMSOffices.ListChanged += listChanged;
    CustomAMSContacts.ListChanged += listChanged;
}

private void listChanged(object sender, ListChangedEventArgs e)
{
    UpdateAMSCommand.RaiseCanExecuteChanged();
}

private void RaiseCanExecuteChanged(object sender, DataErrorsChangedEventArgs e)
{
    UpdateAMSCommand.RaiseCanExecuteChanged();
}

// This will simply flip from true to false every time it is called.
private bool _canupdate = false;
private bool CanUpdateAms()
{
    _canupdate = !_canupdate;
    return _canupdate;
}

编辑:我不知道为什么它不起作用。

【讨论】:

  • "我认为这两行是不正确的" -- 你说的版本不正确一模一样 你说的版本不正确.它们都将编译为相同的 IL。你在说什么?
  • “你说的不正确的版本和你说的不正确的版本完全一样。” - 嗯?它们对我来说运行和编译都很好。不过我不知道他是如何进行所有设置的,所以我在抛出想法。
  • 我的意思是,您在这里“抛弃”的想法毫无意义。您建议 OP 将工作代码更改为表达完全相同代码的其他方式,好像这会有所作为。当然,“它们都可以为 [你] 运行和编译”。它们完全相同。你为什么声称他们不是? IE。一个你标记为“可能错误”,而另一个你标记为“可能正确”。那是不可能的。它们要么都是“可能错误”,要么都是“可能正确”,因为它们完全相同。
  • 酷,我删除了这个建议。没有意识到通过提出想法来帮助另一个开发人员是如此重要。抱歉,我没有检查我的 SO 建议的 IL 输出。我提供了示例代码、调试建议以及对我能找到的唯一代码差异的快速编辑。谢谢你让我知道这没有什么区别,但你可能会失去态度。我们都在这里学习和帮助他人,所以你不需要批评我犯了错误。
  • “你在说什么?”,“你在这里'扔掉'的想法毫无意义。”,“..*好像*那会让”,“它们完全一样。你为什么声称它们不是?”,“那不可能。”国际海事组织,你是一个“无所不知”的人,嘲笑我不知道这两个语句在功能上是相同的。我不羞于犯错,我不是专家,也不自称是专家。我什至说“可能”,因为我不确定。我并没有声称知道出了什么问题,我只是提出可能有助于缩小问题范围的想法。
猜你喜欢
  • 1970-01-01
  • 2016-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 2012-12-09
相关资源
最近更新 更多