2013-11-04 66 views
1

好吧,让我试着解释一下。如何强制更新MVVM中的UI?

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

  • 杀死所有的后台工作人员(我使用CancelAsync为)
  • 做一些计算与我从所有这些
  • 的获得了数据
  • 更新UI,使得显示的数据,并实现一些按钮(这些按钮通过ViewModel命令约束称为PerformUpdateCommand和他们的IsPerformUpdateAllowed一个CanExecute属性。当我的任务完成IsPerformUpdateAllowed依赖属性设置。

我想我会做:

  • 触发背景工人后,使用5秒的间隔定时器调度。
  • DispatcherTimer“ticks”我计算数据并将属性IsPerformUpdateAllowed设置为true或false。

这只是基本上按预期工作(用户界面保持响应,...)只是一个小问题:用户界面没有被更新(按钮未启用)。只要我将窗口放入背景并返回到前景中,该命令即会启用,并且IsPerformUpdateAllowed属性也将设置为true。另外,按下按钮后(禁用状态),它将被启用。

所以,虽然我正确设置了依赖项属性,但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元件(按钮实际上是一个超链接):

  <Label Grid.Row="1" Grid.Column="1"> 
       <Hyperlink Command="{Binding ReadSettingsCommand}"> 
        <TextBlock Text="{Binding Source={StaticResource Loc}, Path=Labels.ReadSettings}"></TextBlock> 
       </Hyperlink> 
      </Label> 

下面是命令

public RelayCommand ReadSettingsCommand 
    { 
     get 
     { 
      return _readSettingsCommand 
       ?? (_readSettingsCommand = new RelayCommand(ExecuteReadSettings,() => IsScannedDeviceAvailable && !IsUpdateInProgress)); 
     } 
    } 

的代码是代码实际上依赖于两个依赖项属性IsScannedDeviceAvailable AND NOT IsUpdateInProgress。两者都是依赖属性。

更新:我刚刚读到绑定到CanExecute属性只是一次。如果你想让它重新生效,你需要在命令上调用RaiseCanExecuteChanged。这可以起作用,但它有点麻烦,因为现在我需要在两个属性中的一个发生更改时手动调用它。其实我想让它自动处理。关于如何更轻松地完成这些任何想法? CanExecute和属性之间是否存在某种单向绑定的方法?

+0

什么是您的IsPerformUpdateAllowed DependencyProperty? – markmnl

+0

您的ICommands不会引发CanExercuteChanged事件。 – Aron

+0

@Aron:有没有比手动调用RaiseCanExecuteChanged更好的方法? –

回答

3

每当您设置了IsScannedDeviceAvailable/IsUpdateInProgress时,您的VM都会调用RaiseCanExecuteChanged。或者我个人最喜欢创建自己的实现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; 
    } 
} 
+0

谢谢,这应该工作。然而,我实际上想以相反的方式编码整个事物。一旦命令的所有条件都满足,它应该自动启用。 –

0

如果你正在引发基础上的属性Command.CanExecute()的变化,你也可以拨打CommandManager.InvalidateRequerySuggested()迫使CanExecute()进行重新评估。

+0

这将不是非常有效,因为它将不得不评估所有的命令。 –