2015-10-06 68 views
1

場景(WPF桌面應用,.NET 4.6)螺紋親和力:與ReactiveUI 6

我有顯示某些 「任務」 列表框。 目標是啓動一個異步過程,它將迭代所有任務,執行其中的每個任務。

這是一個長時間運行的過程,因此期望的行爲是禁用大多數命令而不鎖定UI,因此用戶仍然可以取消它。 它應該標記每個任務(待機,運行,完成),以便可以動態更新UI,向最終用戶提供反饋(使用基於「狀態」枚舉的樣式)。

的問題

當執行指令(ExecuteTasks)得到消息:

這種類型的CollectionView的不支持從一個線程從發送器線程不同,以它的SourceCollection變化。

我的問題是:如何使用ReactiveUI解決此問題?我相信答案是圍繞調度程序的某個地方,但目前爲止我還無法弄清楚。

下面是代碼的樣子:

public ReactiveCommand<object> AddTask { get; } 
public ReactiveCommand<object> ExecuteTasks { get; } 
public IPlugin SelectedTask 
{ 
    get { return selectedTask; } 
    set { this.RaiseAndSetIfChanged(ref selectedTask, value); } 
} 
public ReactiveList<IPlugin> Tasks 
{ 
    get { return tasks; } 
} 
public ReactiveList<PluginFactoryGroup> TaskFactories 
{ 
    get 
    { 
     return taskFactories; 
    } 
} 

public AppViewModel(IExecutionContext context, IEnumerable<PluginFactoryGroup> taskFactories) 
{ 

    this.context = context; 

    // initialize lists 
    tasks = new ReactiveList<IPlugin>() { ChangeTrackingEnabled = true }; 
    this.taskFactories = new ReactiveList<PluginFactoryGroup>(taskFactories); 

    // create observables to determine whether or not commands can be executed 
    var canEdit = /*...*/ 
    var canExecute = /*...*/ 

    // initialize commands 
    AddTask = ReactiveCommand.Create(canEdit); 
    AddTask.Subscribe(_ => { 
     if (SelectedFactory != null) 
     { 
      var t = SelectedFactory.Create(this.context); 
      Tasks.Add(t); 
      SelectedTask = t; 
     } 
    }); 

    ExecuteTasks = ReactiveCommand.CreateAsyncTask(canExecute, _ => 
    { 
     return Task.Run(() => 
     { 
      object result = null; 
      foreach (var item in Tasks) 
      { 
       item.Clear(); 
       item.Validate(); 
      } 

      if (Tasks.Any(e => e.Status == TaskStatus.Error)) 
      { 
       Tasks.Reset(); 
       return result; 
      } 

      foreach (var item in Tasks) 
      { 
       item.Status = XrmTools.Plugins.TaskStatus.Running; 
       item.Execute(); 
       item.Status = XrmTools.Plugins.TaskStatus.Completed; 
      } 
      return result; 
     }); 
    }); 

} 

回答

2

所有的UI更新(ReactiveList添加/刪除,或IPlugin屬性更改)需要UI線程發生。在你的情況下,假設​​是要在後臺發生的冗長操作,你應該使用異步/的await代替Task.Run,​​例如:你的代碼應該是這樣的:

ExecuteTasks = ReactiveCommand.CreateAsyncTask(canExecute, async _ => 
{ 
    object result = null; 
    foreach (var item in Tasks) 
    { 
     item.Clear(); 
     item.Validate(); 
    } 

    if (Tasks.Any(e => e.Status == TaskStatus.Error)) 
    { 
     Tasks.Reset(); 
     return result; 
    } 

    foreach (var item in Tasks) 
    { 
     item.Status = XrmTools.Plugins.TaskStatus.Running; 
     await ExecuteAsync(item); 
     item.Status = XrmTools.Plugins.TaskStatus.Completed; 
    } 
    return result; 
}); 

Task ExecuteAsync(IPlugin item) 
{ 
    return Task.Run(() => item.Execute()); 
} 

看一看在這reference如果你需要更多的靈感。

+0

太棒了。錯誤消失了,它沒有像預期的那樣阻塞UI,非常感謝。有一件事,狀態的改變 - 「item.status =/* new_status * /'不是用信號通知用戶界面。該集合正在改變,但用戶界面不是。我的工作方式是在每次更改(兩次)後添加'Tasks.Reset()',這看起來不正確。 – Ebrito