WPF 数据网格和多线程

3

我有一个DataGrid,使用DataContext填充DataView。我尝试在单独的线程中执行此操作,但UI仍然会冻结。我想在填充DataGrid时防止UI冻结。

这是我迄今为止编写的代码:

private void btnOK_Click(object sender, RoutedEventArgs e)
    {
        GetFieldsBLL getFieldsBLL = new GetFieldsBLL();
        DataView dv = getFieldsBLL.GetWholeView(ViewName);
        Task task = new Task(() => ucDataExtracViewControl.PopulateGrid(dv));
        task.Start();
    }

public void PopulateGrid(DataView dv)
    {
        dgView.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate{

            dgView.Columns.Clear();
            dgView.AutoGenerateColumns = false;
            foreach (DataColumn column in dv.Table.Columns)
            {
                var gridColumn = new DataGridTextColumn()
                {
                    Header = column.ColumnName,
                    Binding = new Binding("[" + column.ColumnName + "]")
                };

                dgView.Columns.Add(gridColumn);
            }
            dgView.DataContext = dv;

            DataView = dv;
        }));
    }

提前致谢!

编辑: 我重新创建列的原因是因为一些列名中含有句点。例如,“Job No.”在使用绑定时不会产生任何数据。在此阅读更多信息:为什么DataTable列名中含有句点会使它们不适用于WPF的DataGrid控件? 不可能对数据库进行更改。


主UI线程创建UI对象,是唯一可以访问UI对象的线程。您可以在后台线程上调用GetFieldsBLL()。如果不需要编辑,则使用ListView GridView,因为它更轻便和更快速。 - paparazzo
通常标记答案为已接受是一种礼貌。否则,人们下次可能不会再帮助你。 - jjrdk
5个回答

4
我曾经与WPF DataGrid 有很多的合作,其中渲染是一个问题。最终的渲染时间取决于您所显示的列数和行数。当DataGrid进行渲染时,它必须绘制每一项,这意味着加载并测量内容的大小。
我发现您可以通过设置固定列宽和行高来获得一些良好的速度改进。当与下面的DelayedDataGridTextColumn结合使用时,几乎没有UI线程的阻塞,因为每个单元格都是单独渲染的,从而允许其他具有更高优先级的UI线程事情发生。
public class DelayedDataGridTextColumn : DataGridTextColumn
{
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var textBlock = new TextBlock();
        textBlock.SetValue(FrameworkElement.StyleProperty, ElementStyle);

        Dispatcher.BeginInvoke(
            DispatcherPriority.Loaded,
            new Action<TextBlock>(x => x.SetBinding(TextBlock.TextProperty, Binding)),
        textBlock);
        return textBlock;
    }
}

请注意,您可以调整DispatcherPriority以适应所需的渲染速度。优先级越低,获得的遮幕效果越多。优先级越高,在渲染时处理的其他项目就越少。


谢谢您的快速回答!设置固定列宽和行高确实使其渲染更快。我查看了您的代码,但我不明白“c”和“x”代表什么。您能否详细说明一下? - user1471467
我删除了c,它是我的代码中的自定义命名空间别名。抱歉。x只是传递到lambda委托的参数。它代表单元格的TextBlock内容,这也是作为arg参数传递给BeginInvoke方法的内容。 - jjrdk

1

将您的DispatcherPriority更改为Background。

dgView.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
// your code
};

0

我已经解决了这个问题。C# 代码并不是 UI 冻结的原因,而是 DataGrid 上记录的渲染。 在数据网格中设置 EnableRowVirtualization="True" EnableColumnVirtualization="True" ScrollViewer.CanContentScroll="True"

实际上,当数据量很大时,DataGrid 会导致 UI 冻结。因此启用虚拟化可以解决此问题。


0

你实际上不需要在单独的线程中运行它,因为你首先要做的是将其调度回UI线程

问题是为什么要自己重新创建列? 这是否真的比将AutoGenerateColumns设置为true产生不同的结果?

我认为如果你能正确使用WPF的绑定功能,那么你可以改进这个问题,但是代码太少了,很难提出好的建议。


我重新创建列的原因是因为有些列名称中含有句点。例如,“工作编号”在使用绑定时不会产生任何数据。了解更多信息,请访问此处:https://dev59.com/8nA85IYBdhLWcg3wF_m0 对数据库进行更改不是一个选项。 - user1471467

0

您可以使用BackgroundWorker类来执行后台进程,具体步骤如下:

将以下任务分配给DoWork回调函数

  1. 调用getFieldsBLL.GetWholeView(ViewName);
  2. 按照您想要的方式填充DataView

将UI更新代码(dgView.DataContext = dv;)移动到RunWorkerCompleted回调函数中。

注意:您需要为DataView创建一个类变量,以便它可以在DoWork和RunWorkerCompleted回调函数中使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接