2011-07-13 28 views
3

我想從小型「塊」或頁面中的數據庫中讀取大量的行並向用戶報告進度;即,如果我在加載每個塊時加載了100個「塊」報告進度。什麼是從TPL任務報告WPF視圖進度的適當方式?

我在C#4.0中使用TPL從數據庫中讀取這些塊,然後將完整結果集交給可以使用它的另一個任務。我覺得TPL可以讓我更好地控制任務取消和交付,比BackgroundWorker等更好,但似乎沒有內置的方式來報告任務的進度。

下面是我已經實現的向WPF進度條報告進度的解決方案,我想確保這是適當的,並且沒有更好的方法,我應該採取。

我開始通過創建一個簡單的接口,以表示改變進度:

public interface INotifyProgressChanged 
{ 
    int Maximum { get; set; } 
    int Progress { get; set; } 
    bool IsIndeterminate { get; set; } 
} 

這些屬性可以被綁定到一個ProgressBar在WPF視圖和接口是由背襯視圖模型,其是負責發起實現數據加載和最終報告的總體進展情況(簡化這個例子):

public class ContactsViewModel : INotifyProgressChanged 
{ 
    private IContactRepository repository; 

    ... 

    private void LoadContacts() 
    { 
    Task.Factory.StartNew(() => this.contactRepository.LoadWithProgress(this)) 
     .ContinueWith(o => this.UseResult(o)); 
    } 
} 

你會發現,我傳遞的視圖模型到庫法作爲INotifyProgressChanged而這正是我はnt,以確保我沒有做錯什麼。

這裏我的思考過程是,爲了報告進度,實際執行工作(這是存儲庫方法)的方法需要訪問INotifyProgressChanged接口,以報告最終更新視圖的進度。下面就讓我們來看看庫法(縮短這個例子):

public class ContactRepository : IContactRepository 
{ 
    ... 

    public IEnumberable<Contact> LoadWithProgress(INotifyProgressChanged indicator) 
    { 
    var resultSet = new List<Contact>(); 
    var query = ... // This is a LINQ to Entities query 

    // Set the maximum to the number of "pages" that will be iterated 
    indicator.Maximum = this.GetNumberOfPages(query.Count(), this.pageSize); 

    for (int i = 0; i < indicator.Maximum; i++) 
    { 
     resultSet.AddRange(query.Skip(i * this.pageSize).Take(this.pageSize)); 
     indicator.Progress += 1; // As each "chunk" is loaded, progress is updated 
    } 

    // The complete list is returned after all "chunks" are loaded 
    return resultSet.AsReadOnly(); 
    } 
} 

那就是如何庫最終報告通過視圖模型的觀看進度。這是正確的方法嗎?我是否正確使用TPL,違反任何主要規則等?這個解決方案正在發揮作用,正在按照預期報告進展情況,我只是想確保我不會讓自己失敗。

回答

5

我建議您避免從後臺線程更新數據綁定屬性。

爲了解決這個問題,你可以有你的後臺任務post a UI task to do its update,或(甚至更好),使用Task-Based Asynchronous Pattern Overview文檔中描述的IProgress<T>/Progress<T>系統。

IProgress<T>方法很好,因爲它將您的後臺任務與ViewModel更新分開。但是,它有some drawbacks(在後臺任務和更新之間共享數據;以及處理來自更新的異常);我希望在官方的Async CTP發佈之前解決這些問題。

1

我不認爲你應該直接從後臺線程直接更新ViewModel。我編寫了很多Silverlight應用程序,並且我喜歡使用MVVMLight工具包來實現MVVM模式。

在MVVM中,有時您需要讓ViewModel「影響」視圖,因爲ViewModel沒有對視圖的引用,所以您無法直接執行視圖。在這些場景中,MVVMLight有一個Messenger類,它允許我們在視圖中「偵聽」消息並從ViewModel「通知」。

我相信你應該在你的場景中使用Messenger類。

這裏是一個示例代碼的鏈接:http://chriskoenig.net/2010/07/05/mvvm-light-messaging/

+0

我第二此。您可以將NotificationMessage 消息發送到UI以進行更新。 –

6

這樣做是從​​傳遞TaskScheduler實例要確保WPF調度線程上執行ContinueWith的「規定」的方式。

例如:

public void DoSomeLongRunningOperation() 
{ 
    // this is called from the WPF dispatcher thread 

    Task.Factory.StartNew(() => 
    { 
     // this will execute on a thread pool thread 
    }) 
    .ContinueWith(t => 
    { 
     // this will execute back on the WPF dispatcher thread 
    }, 
    TaskScheduler.FromCurrentSynchronizationContext()); 
}