显示进度和/或检测DataGrid渲染的完成情况

时间:2014-08-29 14:59:13

标签: c# wpf datagrid

将大型数据集分配给DataGrid后似乎有延迟。在分配到ItemsSource或DataSource完成后,此延迟发生,并且是非阻塞的。特别是,在分配大量数据(大约200,000个元素)作为DataGrid的源之后,在项目出现在屏幕上之前有3-4秒的延迟。我确信这是在赋值后发生的并且是非阻塞的,因为赋值后的所有代码都会立即执行。

DataGrid中是否有任何可能有助于减少我遇到的延迟的设置,更重要的是,有什么方法可以知道该过程何时完成且项目在屏幕上可见,以便我可以通知流程的用户以及何时完成?

我尝试过优化的内容:

  • 交换ItemsSource和DataSource以访问数据 - 没有明显的改进
  • 启用列/行虚拟化 - 没有明显的改进
  • 禁用列自动调整大小 - 小改进

此外,DataGrid仅包含在用于定位的网格中,没有包含ScrollViewer或任何此类性质的网格,我使用this之类的东西将数据绑定到DataGrid。

我尝试过通知的内容:

  • 订阅DataGrid的LayoutUpdated事件 - 经常触发,不知道如何判断哪个是我想要的。

我愿意接受进一步优化的建议,但确实需要找到一种方法来了解信息在屏幕上何时可见。延迟是可以接受的,并且可能随着数据集的大小增加,但我需要一种方法来让用户知情。

XAML:

<Window x:Class="WPF_NPS.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="480" Width="640">
    <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
       <DataGrid Grid.Row="1" AutoGenerateColumns="True" Name="DataGrid" ItemsSource="{Binding Data}" VerticalContentAlignment="Stretch" 
                  HorizontalAlignment="Stretch" CanUserReorderColumns="False" CanUserSortColumns="False" IsReadOnly="True"
                  ColumnWidth="*" />
    </Grid>
</Window>

C#可以在&#34;我尝试优化&#34;

的链接下找到

2 个答案:

答案 0 :(得分:0)

您可以使用BusyIndicator中包含的免费Extended WPF Toolkit™ Community Edition控件:

您只需要设置IsBusy属性,它将如下所示,使用异步Task,如下所示,启用它,执行操作,禁用它。

enter image description here


您应该使用IProgress<T>,因为它是运行后台任务的“官方”方式,正如您在下面看到的那样,它不如BackgroundWorker那么繁琐。

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            // simulate some long work
            IProgress<double> progress = new Progress<double>(handler);
            Action action = () =>
            {
                int length = 10;
                for (int i = 0; i < length; i++)
                {
                    Thread.Sleep(3000);
                    double d = 1.0d/length*(i + 1);
                    progress.Report(d);
                }
            };

            await Task.Run(action);
        }

        private void handler(double value)
        {
            ProgressBar.Value = value*100;
        }
    }
}

最后一件事,

最好使用DataContext,而不是手动分配ItemsSource

在您的XAML上,只需按如下方式定义数据网格:

<DataGrid ItemsSource="{Binding}" />

然后在您的代码上使用它:

public partial class MainWindow : Window
{
    public MyObject MyObject { get; private set; }

    public MainWindow()
    {
        InitializeComponent();

        MyObject = new MyObject();

        this.DataContext = MyObject;
    }

    private void DoSomething()
    {
        MyObject.DoSomething();
    }
}

尽量避免直接操纵数据网格,这样只会让你的生活更轻松。


要明确问题

我不知道一个机制告诉你DataGrid进程在哪里,但是通过使用上面出现的控件,这肯定是一个很好的用户体验,他被告知他有等待,期间。

此外,您可以提供取消按钮,但我不确定这是否适合您的情况。

答案 1 :(得分:0)

虽然我从来没有找到要挂钩的事件,所以当完成渲染时我会收到通知,但我确实从各种评论和答案中进行了大量优化。这主要是通过放弃使用反射更快的实现来完成的。

首先,代替为我的DataGrid源使用自定义类,它提供了一个包含多个公共属性的自定义类的简单数组。这些属性是根据Excel互操作读取的数据设置的(LINQ to Excel替代方案被提出结果比我自己的解决方案慢)。这与数据网格绑定,并且对于100k行,加载时间减少到几秒钟,总共大约200k-500k单元。

在构建项目数组并将其绑定到DataGrid之后仍然可以找到该延迟,但延迟比以前更容易接受。

最后,因为我需要能够从文件中读取而不知道我想要哪些列,直到用户在运行时指定,我构建了一个系统来生成动态填充的类。我这样做是通过构建一个包含此类类代码的字符串,然后使用C#的运行时编译功能来获得接近硬编码类的效率。

最后,为了避免必须使用反射为这些自定义对象中的属性赋值,我构建了一个简单的接口,指定了一个方法,该方法从电子表格中获取通用数据行并在内部初始化属性。

这是界面:

namespace MyNamespace {
    // Used by classes built by an ExcelQueryableFactory to permit 
    // initialization without knowledge of the underlying properties.
    public interface IExcelQueryable  {
        void BuildFrom(object[] data);
    }
}

以下是为其中一个自定义类生成的代码示例:

namespace MyNamespace {
    public class CustomClass0 : IExcelQueryable {
        public double myNumber { get; set; }
        public string myString { get; set; }
        public void BuildFrom(object[] data) {
            myNumber = (double)data[0];
            myString = (string)data[1];
        }
    }
}

它绝对不是我写过的最好的代码,但我想我还是会分享它,因为其他人出现了类似的问题。