繁体   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