C# 如何在MVVM中强制更新UI?

C# 如何在MVVM中强制更新UI?,c#,wpf,mvvm-light,dispatcher,C#,Wpf,Mvvm Light,Dispatcher,好吧,让我试着解释一下 我有一个可以触发某种扫描过程的应用程序。这个扫描过程会启动(只是一个数字)10个后台工作人员来做一些事情。5秒钟后(再次,仅仅是一个数字),我想 杀死所有后台工作人员(为此我使用CancelAsync) 用我从他们那里得到的数据做一些计算 更新UI以显示数据,并启用一些按钮(这些按钮通过名为PerformUpdateCommand的ViewModel命令绑定,它们具有isperformUpdateLowed的CanExecute属性。isperformUpdateLow

好吧,让我试着解释一下

我有一个可以触发某种扫描过程的应用程序。这个扫描过程会启动(只是一个数字)10个后台工作人员来做一些事情。5秒钟后(再次,仅仅是一个数字),我想

  • 杀死所有后台工作人员(为此我使用CancelAsync)
  • 用我从他们那里得到的数据做一些计算
  • 更新UI以显示数据,并启用一些按钮(这些按钮通过名为
    PerformUpdateCommand
    ViewModel
    命令绑定,它们具有
    isperformUpdateLowed
    CanExecute
    属性。
    isperformUpdateLowed
    依赖属性在我的任务完成时设置
我想我会做什么:

  • 触发后台工作程序后,以5秒的间隔启动调度程序计时器
  • 当调度程序发出“滴答声”时,我计算数据并将属性
    IsPerformUpdateAllowed
    设置为true或false
这基本上是按预期工作的(UI保持响应性,…),只是一个小问题:UI没有被更新(按钮未启用)。当我将窗口置于后台并返回前台时,该命令即被启用,
IsPerformUpdateAllowed
属性也被设置为true。此外,当我按下按钮(处于禁用状态)后,它将被启用

因此,尽管我正确设置了dependency属性,但UI不会对此更改做出反应

有人知道为什么吗?有趣的是,我还将UI中的一些文本设置为标签-这些文本被正确更新。只是告诉命令
CanExecute
的属性没有触发UI更新

定时器初始化代码

        _scanTimer = new DispatcherTimer();
        _scanTimer.Interval = new TimeSpan(0, 0, 0, 3);
        _scanTimer.Tick += delegate
        {
            // After the timer has elapsed (some time passed), cancel all scans and update the result
            _scanTimer.Stop();
            UpdateScanResults();
            CancelNormalScans(false);
        };
        _scanTimer.Start();
编码命令如何绑定到WPF元素(按钮实际上是一个超链接):

代码实际上依赖于两个依赖属性IsScannedDeviceAvailable而不是IsUpdateInProgress。这两个属性都是依赖属性


更新:我刚读到CanExecute属性的绑定只有一次。如果你想重新验证它,你需要在命令上调用RaiseCanExecuteChanged。这是可行的,但有点麻烦,因为现在每当两个属性中的一个发生更改时,我都需要手动调用它。实际上我想自动处理它。一个关于如何更容易地实现这一点,你有什么想法吗?难道没有一些方法可以在CanExecute和属性之间进行某种单向绑定吗?

在你的VM调用上,无论何时设置IsCannedDeviceAvailable/IsUpdateInProgress,RaiseCanceChanged都可以。或者我个人最喜欢的方法是创建你自己的
ICommand
实现,因为它很漂亮反正很简单

public class FooCommand : ICommand
{
    private bool _canExecute;
    private Action _delegate;
    public event EventHandler CanExecuteChanged;
    public new bool CanExecute
    {
        get
        {
            return _canExecute;
        }
        set
        {
            _canExecute = value;
            if(CanExecuteChanged != null)
                CanExecuteChanged();
        }
    }

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

    bool ICommand.CanExecute()
    {
        return CanExecute;
    }

    public FooCommand(Action action)
    {
        _delegate = action;
    }
}

如果根据属性触发
命令.CanExecute()
中的更改,也可以调用
命令管理器.invalidateRequestSuggested()
强制执行
CanExecute()
待重新评估。

您的iPerformUpdateAllowedDependencyProperty依赖于什么?您的ICommands没有引发CanExercuteChanged事件。@Aron:有没有比手动调用RaiseCanceTechChanged更好的方法?@TomL。这是最好的方法。对于视图模型操作,可以假定您知道应用程序何时准备好进行command输入。谢谢,这应该可以。但是,我实际上想用另一种方式对整个过程进行编码。一旦一个命令的所有条件都满足,它应该自动启用。这不是很有效,因为它必须计算所有命令。
    public RelayCommand ReadSettingsCommand
    {
        get
        {
            return _readSettingsCommand
                ?? (_readSettingsCommand = new RelayCommand(ExecuteReadSettings, () => IsScannedDeviceAvailable && !IsUpdateInProgress));
        }
    }
public class FooCommand : ICommand
{
    private bool _canExecute;
    private Action _delegate;
    public event EventHandler CanExecuteChanged;
    public new bool CanExecute
    {
        get
        {
            return _canExecute;
        }
        set
        {
            _canExecute = value;
            if(CanExecuteChanged != null)
                CanExecuteChanged();
        }
    }

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

    bool ICommand.CanExecute()
    {
        return CanExecute;
    }

    public FooCommand(Action action)
    {
        _delegate = action;
    }
}