WPF DataGrid 需要 >1 秒才能加载“少量”数据 (40x40)

问题描述 投票:0回答:1

我正在努力为 WPF 中合理大小的 DataGrid 提供合理的加载时间。

我的例子非常简单,粘贴在下面。它只是在按下按钮时加载 40x40 数字的网格,并且 UI 冻结 1 秒。我已经运行了 VS 分析器,加载布局需要 1.16 秒,该布局是由可视化树中的 4147 个元素构建的。

我对 DataGrid 需要时间加载没有任何问题,但我确实认为窗口在加载时完全冻结是一个问题。有人可以帮助我加载 DataGrid 同时保持我的窗口响应吗?

非常感谢!

主窗口.xaml

<Window x:Class="Coliru.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:c="clr-namespace:flap_ui_core.Components;assembly=flap_ui_core"
        xmlns:local="clr-namespace:Coliru"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="Create DataGrid" Padding="5,2" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <ContentControl x:Name="myContentControl"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Coliru
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public class ViewModel
        {
            public class RowType
            {
                public ObservableCollection<double> Columns
                {
                    get => _Columns;
                }

                private ObservableCollection<double> _Columns;

                public RowType(ObservableCollection<double> columns)
                {
                    _Columns = columns;
                }
            }

            public static readonly int numcol = 40;
            public static readonly int numrow = 40;
            public ObservableCollection<RowType> Rows { get => _Rows; }
            private ObservableCollection<RowType> _Rows;


            public ViewModel()
            {
                 Random autoRand = new Random();
                _Rows = new ObservableCollection<RowType>();

                for (int icol = 0; icol < numcol;++icol)
                {
                    var rows = new ObservableCollection<double>();
                    for (int irow = 0; irow < numrow; ++irow)
                    {
                        rows.Add(autoRand.NextDouble());
                    }

                    _Rows.Add(new(rows));
                }

            }

        }

        DataGrid? dataGrid;
        ViewModel viewModel;
        public MainWindow()
        {
            InitializeComponent();

            viewModel = new ViewModel();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            dataGrid = new DataGrid();
            dataGrid.Initialized += DataGrid_Initialized;

            myContentControl.Content = dataGrid;
        }

        private void DataGrid_Initialized(object? sender, EventArgs e)
        {
            dataGrid.ItemsSource = viewModel.Rows;
            dataGrid.AutoGenerateColumns = false;
            for (int i = 0; i < ViewModel.numcol; ++i)
            {
                var col = new DataGridTextColumn();
                var binding = new Binding();
                binding.Path = new PropertyPath("Columns[" + i + "]");
                col.Binding = binding;
                col.Header = "Column " + i;
                col.Width = 80;
                dataGrid.Columns.Add(col);
            }
        }
    }
}

我尝试了很多方法,这只是问题的简化版本。我也尝试过 MVVM。

c# .net wpf datagrid
1个回答
0
投票

我尝试运行您发布的代码,但我无法能够重现 1 秒的长延迟。

我发现的唯一真正的问题不会真正上升到“问题”的水平,除非涉及多次按钮单击:就目前情况而言,每次单击按钮时,您都会再次订阅dataGrid.Initialized事件,而这

不是
“超出范围”的事情。如果是第二次单击按钮,则 DataGrid_Initialized 将运行两次。如果这是第 100 次按钮单击,
DataGrid_Initialized
现在将运行 100 次。
这是一个更清晰的点击处理程序,可以断开事件连接,以确保您只拥有所需的事件。

private void Button_Click(object sender, RoutedEventArgs e) { Stopwatch stopwatch = Stopwatch.StartNew(); try { if (myContentControl.Content is DataGrid existing) { existing.ItemsSource = null; } dataGrid = new DataGrid(); dataGrid.Initialized += DataGrid_Initialized; myContentControl.Content = dataGrid; } finally { dataGrid.Initialized -= DataGrid_Initialized; } stopwatch.Stop(); MessageBox.Show($@"Initialization took {stopwatch.Elapsed:ss\:ffff}"); }

经过修订的最终代码 
Button_Click

和细微的简化。

正在向我们展示一切?没有

Debug 日志记录或类似的东西吗?这是我尝试优化任何可能需要时间的代码。不过,我想澄清的是,我没有发现任何确凿证据。

repro public partial class MainWindow : Window { public MainWindow() => InitializeComponent(); new ViewModel DataContext => (ViewModel)base.DataContext; DataGrid? dataGrid; private void Button_Click(object sender, RoutedEventArgs e) { Stopwatch stopwatch = Stopwatch.StartNew(); Dispatcher.BeginInvoke(() => { try { if (myContentControl.Content is DataGrid existing) { existing.ItemsSource = null; } dataGrid = new DataGrid(); dataGrid.Initialized += DataGrid_Initialized; myContentControl.Content = dataGrid; } finally { dataGrid.Initialized -= DataGrid_Initialized; } stopwatch.Stop(); MessageBox.Show($@"Initialization took {stopwatch.Elapsed:ss\:ffff}"); }); } private void DataGrid_Initialized(object? sender, EventArgs e) { for (int i = 0; i < ViewModel.NUMCOL; ++i) { var col = new DataGridTextColumn(); col.Binding = new Binding { Path = new PropertyPath("Columns[" + i + "]"), }; col.Header = "Column " + i; col.Width = 80; dataGrid.Columns.Add(col); } dataGrid.AutoGenerateColumns = false; dataGrid.ItemsSource = DataContext.Rows; } } public class ViewModel { public class RowType { public ObservableCollection<double> Columns { get; } public RowType(ObservableCollection<double> columns) { Columns = columns; } } public const int NUMCOL = 40; public const int NUMROW = 40; public ObservableCollection<RowType> Rows { get; } = new ObservableCollection<RowType>(); public ViewModel() { Random autoRand = new Random(); for (int icol = 0; icol < NUMCOL; ++icol) { var rows = new ObservableCollection<double>(); for (int irow = 0; irow < NUMROW; ++irow) { rows.Add(autoRand.NextDouble()); } Rows.Add(new(rows)); } } }

Xaml
<Window x:Class="Coliru.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Coliru"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Width="500" Height="300">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Grid>
        <Button Content="Create DataGrid" Padding="5,2" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <ContentControl x:Name="myContentControl"/>
    </Grid>
</Window>

© www.soinside.com 2019 - 2024. All rights reserved.