C# 提高WPF数据网格性能

C# 提高WPF数据网格性能,c#,wpf,datagrid,wpfdatagrid,C#,Wpf,Datagrid,Wpfdatagrid,在我的.net3.5WPF应用程序中,我有一个WPFDataGrid,它将填充500列和50行。 应用程序的性能在滚动时非常差,或者在执行DataGrid.Items.Refresh()时,或者在选择行时非常差 实际上,应用程序将需要大约20秒来更新布局布局更新()事件将在20秒后触发 如果我将列数减少到50或更少,应用程序的响应速度将非常快。根据我的发现,性能与列数直接相关 如何提高数据网格的性能?也许可以尝试一下,而不是一次加载所有50行 您可以打开一些选项来帮助您处理数据网格对象 Enab

在我的
.net3.5
WPF
应用程序中,我有一个
WPF
DataGrid
,它将填充500列和50行。 应用程序的性能在滚动时非常差,或者在执行
DataGrid.Items.Refresh()时,或者在选择行时非常差

实际上,应用程序将需要大约20秒来更新布局<代码>布局更新()
事件将在20秒后触发

如果我将列数减少到50或更少,应用程序的响应速度将非常快。根据我的发现,性能与列数直接相关


如何提高数据网格的性能?

也许可以尝试一下,而不是一次加载所有50行


您可以打开一些选项来帮助您处理数据网格对象

EnableColumnVirtualization = true
EnableRowVirtualization = true
这两个是我认为可能有帮助的主要因素。下一步尝试使绑定异步

ItemsSource="{Binding MyStuff, IsAsync=True}"
最后,我听说设置最大高度和宽度即使高于最大屏幕尺寸也有帮助,但我自己没有注意到差异(声称与自动尺寸测量有关)


另外,永远不要将
数据网格
放在
滚动查看器
中,因为这样会基本上失去虚拟化。让我知道这是否有帮助

检查是否有属性
ScrollViewer.CanContentScroll
设置
False

将此属性设置为false将以某种方式禁用虚拟化,这将降低数据网格的性能。有关更多说明,请参阅此

设置
DataGrid.RowHeight
值,这将产生巨大的差异

我知道这是一个非常古老的问题,但我刚刚遇到了它,这是我这边最大的不同。我的默认身高是25。

第一步:2分钟到10秒 这个答案()使我走上了正确的轨道。但我需要将其设置为
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;
}
内部静态无效DataGridRefreshItems(DataGrid dataGridToRefresh)
{
///从datagrid获取scrollViewer
ScrollViewer ScrollViewer=WpfToolsGeneral.FindVisualChildren(dataGridToRefresh).ElementAt(0);
bool savedContentScrollState=scrollViewer.CanContentScroll;
scrollViewer.CanContentScroll=true;
dataGridToRefresh.Items.Refresh();
///已设置为false,请还原它
如果(!savedContentScrollState)
{
///即使没有更新DataGrid,此方法也会完成
///已完成。因此,我们使用此调用来执行
///UI工作完成后的设置。
Dispatcher.CurrentDispatcher.BeginInvoke(新操作(()=>SetScrollViewerCanContentScrollFalse(scrollViewer)),DispatcherPriority.ContextIdle,null);
}
}
私有静态无效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;
            }
        }
    }
}
公共静态IEnumerable FindVisualChildren(DependencyObject depObj),其中T:DependencyObject
{
if(depObj!=null)
{
for(int i=0;i
在此之后,我对50000个新项目的刷新只持续10秒,不像之前的2分钟那样,并且只消耗了2MB内存(4GB)

步骤2:10秒到0.5秒
为了进行测试,我禁用了所有
IValueConverter
,并实现了直接绑定的属性。如果没有转换器,DataGrid会立即刷新。所以我留下了它。

一个包含10列以上的网格可能不是一个好主意。但是请考虑<代码>自定义分页< /代码> @ BoMeMe真的吗?见过外汇网格吗?@WPFK你有没有考虑过使用性能优化的第三方网格?@TomTom-你能告诉我一些第三方网格吗。我不知道外汇是什么,但我不认为任何理智的人都会浏览500列数据。谢谢你的建议。但是我的要求是一次有500列。你说滚动,所以我认为你不能一次显示所有的行(用500列怎么办)数据虚拟化的思想是加载一定数量的数据来填充网格,然后在滚动时删除和替换行。我还可以将列设置为固定宽度。@testpattern数据网格自行滚动,你只是不想把它放在你自己的ScrollViewer的DataGrid外部,否则你会失去控件的虚拟化。我想知道为什么其他线程从未讨论过这一点,即启用行和列虚拟化。非常感谢。绝对值得一试!事实上,他们为我提高了很多性能。感谢@Alan,设置MaxHeight取得了显著的改进。非常感谢你。同时设置MaxWidth也有轻微的改进。在找到这个解决方案之前,我都快疯了。非常感谢。
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;
            }
        }
    }
}