在我的
.NET 3.5
WPF
应用程序中,我有一个 WPF
DataGrid
,它将填充 500 列和 50 行。
应用程序的性能在滚动时非常非常差,或者当我执行DataGrid.Items.Refresh()
或选择行时。
实际上应用程序将需要大约 20 秒来更新布局。
Layout_Updated()
事件将在 20 秒后触发。
如果我将列数减少到 50 或更少,应用程序的响应速度将会非常快。根据我的发现,性能与列数直接相关。
如何提高
DataGrid
性能?
您可以打开一些选项来帮助您处理 DataGrid 对象
EnableColumnVirtualization = true
EnableRowVirtualization = true
这两个是我认为可能有帮助的主要内容。接下来尝试使您的绑定异步
ItemsSource="{Binding MyStuff, IsAsync=True}"
最后,我听说设置最大高度和宽度可以有所帮助,即使它高于最大屏幕尺寸,但我自己没有注意到差异(声称与自动尺寸测量有关)
MaxWidth="2560"
MaxHeight="1600"
也永远不要将
DataGrid
放入 ScrollViewer
中,因为你基本上会失去虚拟化。让我知道这是否有帮助!
检查您是否有属性
ScrollViewer.CanContentScroll
设置False
。
将此属性设置为 false 会禁用虚拟化,从而降低数据网格的性能。有关更多说明,请参阅此CanContentScroll
设置
DataGrid.RowHeight
值,这将产生巨大的差异。
我知道这是一个非常老的问题,但我刚刚遇到它,这是我最大的不同。我的默认高度是 25。
这个答案(将 ScrollViewer.CanContentScroll 设置为 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,而不是之前的 4 GB。
为了测试,我禁用了所有
IValueConverter
并实现了直接绑定的属性。如果没有转换器,DataGrid 会立即刷新。所以我就留下了。
也许可以尝试这个,而不是一次加载所有 50 行
http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization
我发现对加载和滚动性能最好的折衷方案是为所有列的绑定设置
IsAsync=True
,而不是 DataGrid ItemsSource 绑定。示例:
<DataGridTextColumn Binding="{Binding Path=MaterialName, Mode=OneTime, IsAsync=True}" Header="Name" />
顺便说一句,当 DataGrid 上的虚拟化关闭时,此解决方案效果更好。不过,即使开启了虚拟化,仍然值得一试。此外,当应用于显示图像等的 DataGridTemplateColumn 时,此解决方案非常有效。
我在 ScrollViewer 中有一个 DataGrid,允许
DataGrid
增长到无限高度。将 DataGrid 的 Height
或 MaxHeight
设置为合理的值可以解决我的大部分性能问题。