[英]Dynamically updating a DataGrid using a background worker
我有一個自定義類的ObservableCollection
,其中包含一個字符串和一個int:
public class SearchFile
{
public string path { set; get; }
public int occurrences { set; get; }
}
我想在dataGrid
顯示集合。 集合具有在更新時通知的方法,到目前為止,僅需將其鏈接到DataGrid.ItemsSource
(對嗎?)。 這是網格XAML(帶有dataGrid1.ItemsSource = files;
在C#代碼后面):
<DataGrid AutoGenerateColumns="False" Height="260" Name="dataGrid1" VerticalAlignment="Stretch" IsReadOnly="True" ItemsSource="{Binding}" >
<DataGrid.Columns>
<DataGridTextColumn Header="path" Binding="{Binding path}" />
<DataGridTextColumn Header="#" Binding="{Binding occurrences}" />
</DataGrid.Columns>
</DataGrid>
現在事情變得更復雜了。 我首先想顯示的path
與默認數值• occurrence
的零。 於是,我想通過每SearchFile
並與計算值更新occurrence
。 這是輔助函數:
public static void AddOccurrences(this ObservableCollection<SearchFile> collection, string path, int occurrences)
{
for(int i = 0; i < collection.Count; i++)
{
if(collection[i].path == path)
{
collection[i].occurrences = occurrences;
break;
}
}
}
這是占位符worker函數:
public static bool searchFile(string path, out int occurences)
{
Thread.Sleep(1000);
occurences = 1;
return true; //for other things; ignore here
}
我正在使用BackgroundWorker
作為后台線程。 這是如何做:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> allFiles = new List<string>();
//allFiles = some basic directory searching
this.Dispatcher.Invoke(new Action(delegate
{
searchProgressBar.Maximum = allFiles.Count;
files.Clear(); // remove the previous file list to build new one from scratch
}));
/* Create a new list of files with the default occurrences value. */
foreach(var file in allFiles)
{
SearchFile sf = new SearchFile() { path=file, occurrences=0 };
this.Dispatcher.Invoke(new Action(delegate
{
files.Add(sf);
}));
}
/* Add the occurrences. */
foreach(var file in allFiles)
{
++progress; // advance the progress bar
this.Dispatcher.Invoke(new Action(delegate
{
searchProgressBar.Value = progress;
}));
int occurences;
bool result = FileSearcher.searchFile(file, out occurences);
files.AddOccurrences(file, occurences);
}
}
現在,當我運行它時,有兩個問題。 首先,更新進度條的值將引發The calling thread cannot access this object because a different thread owns it.
例外。 為什么? 它在調度程序中,因此應該可以正常工作。 其次, foreach
循環在bool result =...
行上中斷。 我將其注釋掉,並嘗試將int occurences = 1
設置為int occurences = 1
,然后循環進行,但是發生了一些奇怪的事情:每當我調用該方法時,它要么全為零,要么為全零,要么為一個介於中間的狀態,onez在a之后看似隨機的零)。
為什么?
Dispatcher並不像聽起來那樣簡單。 使用分派器意味着代碼將在創建包含對象的同一線程上執行,但這不一定是UI線程。 確保在屏幕上顯示的實際UI元素中定義了worker_DoWork方法(這是消除由后台線程創建的元素的簡便方法)。
真的,看看您的代碼,我不確定您為什么要使用后台工作者。 由於不斷向UI線程調度,因此這看起來實際上會更慢。 相反,我認為您最好的選擇是將長時間運行的部分放在任務中,並在單個回調上更新UI。 例如,在您的代碼中,看起來對於UI線程來說實際上太慢的唯一事情可能是FileSearcher調用。 這很容易放入后台任務,該任務返回找到的結果數。
至於FileSearcher上的問題,您的方法定義不匹配。 您發布的方法僅采用路徑和out int,但調用時將傳遞4個參數。 沒有看到過載,實際上是在打電話,很難猜測出了什么問題。
編輯:讓我備份多一點。 您的根本問題是WPF不支持綁定到從后台線程修改的集合。 這就是在那里進行所有分派和其他復雜代碼的原因。 我最初的建議(長期工作的一項任務)是解決該問題的一種方法。 但您可以使用ObservableList類做得更好。 這使您可以從后台線程更新集合,並自動通知UI,而無需使用調度程序。 這可能會使大多數復雜的線程問題消失。 我也建議您通讀前三篇文章以供參考,那里也有很多很好的信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.