簡體   English   中英

使用后台工作程序動態更新DataGrid

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM