提高WPF DataGrid性能

voase2hg  于 2023-06-30  发布在  其他
关注(0)|答案(6)|浏览(166)

在我的.NET 3.5WPF应用程序中,我有一个WPFDataGrid,它将填充500列和50行。App的性能在滚动方面非常非常差,或者当我做DataGrid.Items.Refresh()或者在选择行时。
实际上,应用程序将需要大约20秒来更新布局。Layout_Updated()事件将在20秒后触发。
如果我将列数减少到50或更少,应用程序的响应速度将非常快。根据我的发现,性能与列数直接相关。
如何提高DataGrid的性能?

6mw9ycah

6mw9ycah1#

您可以打开一些选项来帮助您处理DataGrid对象

EnableColumnVirtualization = true
EnableRowVirtualization = true

这两个是我认为可能有帮助的主要因素。接下来尝试使绑定异步化

ItemsSource="{Binding MyStuff, IsAsync=True}"

最后,我听说设置最大高度和宽度可以帮助,即使它高于最大屏幕尺寸,但我自己没有注意到差异(声称与自动尺寸测量有关)

MaxWidth="2560"
MaxHeight="1600"

另外,永远不要将DataGrid放在ScrollViewer中,因为这样会失去虚拟化。让我知道如果这有帮助!

8ljdwjyq

8ljdwjyq2#

检查是否设置了ScrollViewer.CanContentScroll属性False。将此属性设置为false将禁用虚拟化,这将降低数据网格的性能。有关详细说明,请参阅此CanContentScroll

ajsxfq5m

ajsxfq5m3#

设置DataGrid.RowHeight值,这将产生巨大的差异。
我知道这是一个很老的问题,但我只是偶然发现了它,这是我最大的不同。我的默认身高是25。

pw9qyyiw

pw9qyyiw4#

第一步:从2分钟到10秒

这个答案(Set ScrollViewer.CanContentScroll to True)让我走上了正确的道路。但我需要将其设置为false。为了在刷新时将其设置为true,我编写了以下两个方法。

internal static void DataGridRefreshItems(DataGrid dataGridToRefresh)
{
    /// Get the scrollViewer from the datagrid
    ScrollViewer scrollViewer = WpfToolsGeneral.FindVisualChildren<ScrollViewer>(dataGridToRefresh).ElementAt(0);
    bool savedContentScrollState = scrollViewer.CanContentScroll;
    scrollViewer.CanContentScroll = true;

    dataGridToRefresh.Items.Refresh();

    /// Was set to false, restore it
    if (!savedContentScrollState)
    {
        /// This method finishes even when the update of the DataGrid is not 
        /// finished. Therefore we use this call to perform the restore of
        /// the setting after the UI work has finished.
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => SetScrollViewerCanContentScrollFalse(scrollViewer)), DispatcherPriority.ContextIdle, null);
    }
}

private static void SetScrollViewerCanContentScrollFalse(ScrollViewer scrollViewer)
{
    scrollViewer.CanContentScroll = false;
}

这是我用来获取VisualChildren的方法:

public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj != null)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
            if (child != null && child is T)
            {
                yield return (T)child;
            }

            foreach (T childOfChild in FindVisualChildren<T>(child))
            {
                yield return childOfChild;
            }
        }
    }
}

在此之后,我刷新50.000个新项目只持续10秒,不像2分钟,只消耗2 MB的RAM instad的4 GB之前。

第二步:从10秒到0.5秒

为了测试,我禁用了所有的IValueConverter,并实现了我直接绑定的属性。如果没有转换器,DataGrid会立即刷新。所以我把它留下了。

quhf5bfb

quhf5bfb6#

我发现一个折衷加载和滚动性能的最佳解决方案是为所有列的Binding设置IsAsync=True,而不是DataGrid ItemsSource Binding。示例:

<DataGridTextColumn Binding="{Binding Path=MaterialName, Mode=OneTime, IsAsync=True}" Header="Name" />

顺便说一下,当DataGrid上的虚拟化关闭时,这个解决方案工作得更好。但是,即使虚拟化打开,仍然值得一试。此外,当应用于显示图像等内容的DataGridTemplateColumn时,此解决方案非常有效。

相关问题