CommandManager.InvalidateRequerySuggested在MVVM-Light中不会导致CanExecute重新查询

11

我正在使用MVVM-Light的RelayCommand

private ICommand myRevertCmd;
public ICommand Revert
    {
        get
        {
            if (myRevertCmd == null)
            {
                myRevertCmd = new RelayCommand(RevertExecute, CanRevertExecute);
            }

            return myRevertCmd;
        }
    }

    private void RevertExecute()
    {
        searchType = SearchType.Revert;
        SearchStart();
    }

    private bool CanRevertExecute()
    {
        return isRevertEnabled;
    }

我有一些代码更改了isRevertEnabled的值,但相关按钮没有改变。经过一番搜索,我发现可以使用trigger强制重新评估按钮状态。

// force the GUI to re-evaluate the state of the buttons
CommandManager.InvalidateRequerySuggested();

但是这并不起作用。有人有什么建议吗?


3
目前,RelayCommand 的一种实现在额外的命名空间 GalaSoft.MvvmLight.CommandWpf 中,专门解决了这个问题! - heltonbiker
1
我尝试了下面所有的答案,但都没有解决问题。但是使用GalaSoft.MvvmLight.CommandWpf.RelayCommand却解决了我的问题。谢谢@heltonbiker。 - Paw Baltzersen
7个回答

43

仅提供另一个可能的解决方案,在我的情况下,我需要使用 Application.Current.Dispatcher.Invoke 在UI线程上调用CommandManager.InvalidateRequerySuggested


7
感谢提示“在UI线程上”,给您点个赞。我在后台线程调用它时没有任何反应。 - Flat Eric
2
希望我能再次为此投票。我想知道为什么MVVMLight默认情况下没有做这个。 - Benjol
1
您将此代码放在ViewModel中还是视图的后台代码中?抱歉,我不知道如何在UI线程上调用它。 - Emil
我尝试了许多其他选项/建议。这是第一个对我有效的。耶。 - steve
@Emil:我在视图模型中使用了一个后台任务,但你可能正在另一个类中使用后台线程。...但是为了使其有用,它需要从后台代码中工作...对我来说它可以工作。我通过任务代码中的调度程序调用InvalidateRequerySuggested。 - steve
显示剩余2条评论

7
有很多建议可以参考(在这里这里这里)。我使用了一个简单但不太美观的解决方法。在我的BackgroundWorker完成事件中,我只需为我的命令调用OnPropertyChanged("MyICommand")即可。

3
这可能是一个“不太美丽的权宜之计”,但是OnPropertyChanged(Command)对我非常有用。感谢这个想法,我自己从来没有想到过。 - Zamotic
如果UI不是“繁重”的,您可以调用OnPropertyChanged(null)来更新_每个_属性,包括命令(这将重新评估CanExecute命令)。 - heltonbiker

4
根据Josh Smith的文章'允许CommandManager查询您的ICommand对象'。问题在于该命令是非路由命令。
我已经制作了MVVM-Light RelayCommand的新实现,如下所示:
// original
//public event EventHandler CanExecuteChanged;


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

public void RaiseCanExecuteChanged()
{
    CommandManager.InvalidateRequerySuggested();
    //            var handler = CanExecuteChanged;
    //            if (handler != null)
    //            {
    //                handler(this, EventArgs.Empty);
    //            }
}

2
目前,另一个命名空间GalaSoft.MvvmLight.CommandWpf中有一个RelayCommand的实现,专门解决了这个问题! - heltonbiker

0
我会将isRevertEnabled标志转换为属性,并从那里调用OnPropertyChanged方法(您需要实现INotifyPropertyChanged接口)。在更改标志的地方,您需要使用该属性,例如IsRevertEnabled = true。
private bool isRevertEnabled;

public bool IsRevertEnabled
{
    get
    {
        return isRevertEnabled;
    }
    set
    {
        if (isRevertEnabled != value)
        {
            isRevertEnabled = value;
            OnPropertyChanged("IsRevertEnabled");
        }
    }
}

private bool CanRevertExecute()     
{         
    return IsRevertEnabled;     
}

0

@heltonbiker已经在评论中指出了这一点,但解决方法是更改命名空间。

如果您查看RelayCommand的源代码,您会看到以下备注:

// Remarks:
//     If you are using this class in WPF4.5 or above, you need to use the GalaSoft.MvvmLight.CommandWpf
//     namespace (instead of GalaSoft.MvvmLight.Command). This will enable (or restore)
//     the CommandManager class which handles automatic enabling/disabling of controls
//     based on the CanExecute delegate.

0
我猜你可以在 "isRevertEnabled" 的 INPC(或依赖属性)设置方法中调用“Revert.RaiseCanExecuteChanged()”方法。这将强制执行“CanRevertExecute”谓词。

这会产生编译错误:“事件System.Windows.Input.ICommand.CanExecuteChanged只能出现在+=或-=的左侧”。 - Andrea

0

只需调用Revert.RaiseCanExecuteChanged();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接