衡水做淘宝网站,装网要多少钱,autohome汽车之家官网,网页视频提取软件app这篇文章并非讨论 WPF Datagrid 的性能数据#xff0c;而只是简单介绍一下为了使其性能良好#xff0c;你需要注意哪些方面。我不太想使用性能分析器来展示实际数据#xff0c;而是尽可能地使用了 Stopwatch 类。这篇文章不会深入探讨处理海量数据的技术#xff0c;例如分页… 这篇文章并非讨论 WPF Datagrid 的性能数据而只是简单介绍一下为了使其性能良好你需要注意哪些方面。我不太想使用性能分析器来展示实际数据而是尽可能地使用了 Stopwatch 类。这篇文章不会深入探讨处理海量数据的技术例如分页或如何实现分页而是专注于如何让 Datagrid 处理大数据。
这是生成我想要加载到 Datagrid 中的数据的 C# 类。
public class DataItem { public long Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public long Age { get; set; } public string City { get; set; } public string Designation { get; set; } public string Department { get; set; } } public static class DataGenerator { private static int _next 1; public static IEnumerable GetData(int count) { for (var i 0; i count; i) { string nextRandomString NextRandomString(30); yield return new DataItem { Age rand.Next(100), City nextRandomString, Department nextRandomString, Designation nextRandomString, FirstName nextRandomString, LastName nextRandomString, Id _next }; } } private static readonly Random rand new Random(); private static string NextRandomString(int size) { var bytes new byte[size]; rand.NextBytes(bytes); return Encoding.UTF8.GetString(bytes); } }
ViewModel 定义如下所示 public class MainWindowViewModel : INotifyPropertyChanged { private void Notify(string propName) { if (PropertyChanged ! null) PropertyChanged(this, new PropertyChangedEventArgs(propName)); } public event PropertyChangedEventHandler PropertyChanged; private Dispatcher _current; public MainWindowViewModel() { _current Dispatcher.CurrentDispatcher; DataSize 50; EnableGrid true; _data new ObservableCollection(); } private int _dataSize; public int DataSize { get { return _dataSize; } set { LoadData(value - _dataSize); _dataSize value; Notify(DataSize); } } private ObservableCollection _data; public ObservableCollection Data { get { return _data; } set { _data value; Notify(Data); } } private bool _enableGrid; public bool EnableGrid { get { return _enableGrid; } set { _enableGrid value; Notify(EnableGrid); } } private void LoadData(int more) { Action act () { EnableGrid false; if (more 0) { foreach (var item in DataGenerator.GetData(more)) _data.Add(item); } else { int itemsToRemove -1 * more; for (var i 0; i itemsToRemove; i) _data.RemoveAt(_data.Count - i - 1); } EnableGrid true; }; //act.BeginInvoke(null, null); _current.BeginInvoke(act, DispatcherPriority.ApplicationIdle); } }
如您所见随着 DataSize 的改变数据也会被加载。目前我使用滑块来调整加载大小。这一切都非常简单而且有趣的事情从 XAML 开始。
为了将此“数据”应用到我的WPF数据网格我将这个ViewModel实例应用到我的类的DataContext中。请参阅下面的窗口代码
public partial class MainWindow : Window { private MainWindowViewModel vm; public MainWindow() { InitializeComponent(); vm new MainWindowViewModel(); this.Loaded (s, e) DataContext vm; } } 让我们从以下 XAML 开始
stackpanel slider maximum100 minimum50 value{Binding DataSize} / label grid.row1 content{Binding DataSize} datagrid grid.row2 isenabled{Binding EnableGrid} itemssource{Binding Data} /datagrid /stackpanel
现在构建应用程序并运行。结果如下所示 如上所示我加载了 100 个项目却看不到滚动条。让我们将滑块的 Maximum 属性从 100 改为 1000然后重新运行应用程序。一次性将滑块拖到 1000。所以即使加载了 1000 个项目网格的响应也不太好。 让我们看一下内存使用情况 对于一个只加载了 1000 条数据的应用程序来说这已经相当繁重了。那么究竟是什么占用了这么多内存呢你可以连接内存分析器或使用 Windbg 查看内存内容但由于我已经知道导致这个问题的原因所以就不赘述了。 这个问题是由于 DataGrid 被放置在 StackPanel 中。垂直堆叠时StackPanel 基本上会为其子项分配所需的所有空间。这使得 DataGrid 创建 1000 行每行每列所需的所有 UI 元素并进行渲染。DataGrid 的虚拟化功能在这里没有发挥作用。 让我们做一个简单的修改将 DataGrid 放入网格中。其 XAML 代码如下所示
Grid Grid.RowDefinitions RowDefinition Height30/ RowDefinition Height30/ RowDefinition Height*/ /Grid.RowDefinitions Slider Value{Binding DataSize} Minimum50 Maximum1000/ Label Content{Binding DataSize} Grid.Row1/ DataGrid ItemsSource{Binding Data} Grid.Row2 IsEnabled{Binding EnableGrid} /DataGrid /Grid
当我运行应用程序时你会注意到当我加载 1000 个项目时同一个应用程序的性能除了我刚才提到的 XAML 代码之外没有任何代码更改比以前好了很多。而且我还看到了漂亮的滚动条。 让我们看一下内存使用情况 哇差别简直是十倍可以参考WPF虚拟化的文章https://blog.csdn.net/hefeng_aspnet/article/details/147305605
那么我在这里还要谈论什么呢 如果你注意到 ViewModel 的代码你应该会看到我在加载数据时禁用了网格并在完成后重新启用它。我还没有真正测试过这项技术是否有用但我在 HTML 页面中使用过这项技术当时列表框中的大量项目都需要被选中这项技术非常有用。 在我展示的所有截图中网格都是排序的。因此当数据发生变化时网格必须继续对数据进行排序并根据您选择的排序方式进行显示。我认为这会造成很大的开销。如果可行的话在更改数据之前请考虑移除数据网格的排序功能并且这样做不会对最终用户造成影响。我还没有测试过这一点但分组功能应该也应该如此大多数情况下分组功能无法简单地移除。 只需将 DataGrid 加载到任何其他面板例如 Grid而不是 StackPanel 中您就能看到很大的区别。只要您将网格的可视区域保持在较小的范围内WPF DataGrid 的性能就很好。 下面显示的是我的网格加载了近一百万个数据项。与加载的数据量相比占用空间相当小。这意味着要么是WPF控件占用大量内存要么是WPF UI虚拟化带来了好处。
排序对 DataGrid 的影响 由于没有对数据网格进行排序将 100 万个项目加载到我的集合中花了将近 20 秒。 启用排序后加载一半的数据项本身就花了 2 分钟多加载全部数据项则花了 5 分钟多我甚至因为太麻烦而关掉了应用程序。这很重要因为应用程序会一直忙于处理数据变化时必须进行的排序从而占用大量 CPU 资源。因此由于我直接将其放入可观察集合中因此每次添加数据项都可能触发排序。 相反考虑在后端进行排序而不是使用数据网格。 如果虚拟化得到正确利用尽管网格绑定到 100 万个项目我仍然可以滚动应用程序。
在数据网格上使用 BeginInit() 和 EndInit()。 修改了 ViewModel 的 LoadData() 方法使其在开始加载数据时调用 BeginInit()并在加载完成后调用 EndInit()。这确实很有帮助。加载 100 万个项目网格上未进行任何排序仅花费了大约 8 秒之前需要 18 秒。可惜的是没有花足够的时间使用分析器来显示实际数据。
窗口更改后的后台代码如下所示
public partial class MainWindow : Window { private MainWindowViewModel vm; public MainWindow() { InitializeComponent(); vm new MainWindowViewModel(); this.Loaded (s, e) DataContext vm; vm.DataChangeStarted () dg.BeginInit(); vm.DataChangeCompleted () dg.EndInit(); } } 我还必须将 DataChangeStarted 和 DataChangeCompleted 操作添加到 ViewModel 类中。ViewModel 类的更改部分如下所示
public event Action DataChangeStarted ; public event Action DataChangeCompleted; private void LoadData(int more) { Action act () { //Before the data starts change, call the method. if (DataChangeStarted ! null) DataChangeStarted(); var sw Stopwatch.StartNew(); EnableGrid false; if (more 0) { foreach (var item in DataGenerator.GetData(more)) _data.Add(item); } else { int itemsToRemove -1 * more; for (var i 0; i itemsToRemove; i) _data.RemoveAt(_data.Count - i - 1); } EnableGrid true; sw.Stop(); Debug.WriteLine(sw.ElapsedMilliseconds); if (DataChangeCompleted ! null) DataChangeCompleted(); }; //act.BeginInvoke(null, null); _current.BeginInvoke(act, DispatcherPriority.ApplicationIdle); }
您可以尝试一下并亲自观察性能差异。 如果在数据网格上进行排序即使使用了上述技巧性能仍然会受到影响。排序的开销抵消了调用 BeginInit 和 EndInit 所获得的性能提升。拥有 100 万条记录可能不太现实。
如果您喜欢此文章请收藏、点赞、评论谢谢祝您快乐每一天。