WPF DataGrid分组导致可怕的内存消耗

时间:2018-03-11 19:06:58

标签: c# wpf

我无法得到DataGrid分组的错误。

我有一个像这样的最小应用程序:

<Window x:Class="dggrouping_test.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:dggrouping_test"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid x:Name="dataGrid">
        <DataGrid.GroupStyle>
            <x:Static Member="GroupStyle.Default"/>
        </DataGrid.GroupStyle>
    </DataGrid>
</Grid>

和XAML吧:

    <div id="particles-js"></div>
 <div class="text">
    <h1>Particles Background</h1>
 </div>

当我编译并运行它时,应用程序在3000行上消耗大约550 MB的内存(!!!)。加载需要几秒钟。如果我只是评论dataView.GroupDescriptions.Add(...)并再次运行 - 内存使用量下降到85 MB。因此,在最小的示例中,每3000行消耗450 MB。我做错了什么?

2 个答案:

答案 0 :(得分:1)

还有一个答案。

<强>设计

3000行少量。我的拇指是100个字段/信息的东西是你应该向用户显示的限制。每行需要的字段越多,你应该显示的行数就越少(低至5-10)他甚至无法处理的任何内容。

在查询期间尽可能多地进行过滤。如果您的DBMS支持分页,也可以使用它。检索所有内容是常见的错误,然后在GUI中进行过滤。 3000看起来很明显。

UI Virtualsiation

XAML在设计时考虑了很多UI Virtualsition Support。问题是,只有当它被打开并且你暴露正确的东西才能工作

绑定垃圾邮件

UI更新成本很高。如果每个用户触发的事件只执行一次,则不会产生任何影响。但如果它是从任何形式的循环运行它会成为一个问题。我为这个问题写了一些示例代码:

using System;
using System.Windows.Forms;

namespace UIWriteOverhead
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        int[] getNumbers(int upperLimit)
        {
            int[] ReturnValue = new int[upperLimit];

            for (int i = 0; i < ReturnValue.Length; i++)
                ReturnValue[i] = i;

            return ReturnValue;
        }

        void printWithBuffer(int[] Values)
        {
            textBox1.Text = "";
            string buffer = "";

            foreach (int Number in Values)
                buffer += Number.ToString() + Environment.NewLine;
            textBox1.Text = buffer;
        }

        void printDirectly(int[] Values){
            textBox1.Text = "";

            foreach (int Number in Values)
                textBox1.Text += Number.ToString() + Environment.NewLine;
        }

        private void btnPrintBuffer_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(10000);
            MessageBox.Show("Printing with buffer");
            printWithBuffer(temp);
            MessageBox.Show("Printing done");
        }

        private void btnPrintDirect_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Generating Numbers");
            int[] temp = getNumbers(1000);
            MessageBox.Show("Printing directly");
            printDirectly(temp);
            MessageBox.Show("Printing done");
        }
    }
} 

现在很少有东西会导致UI更新和过多的更改通知一样多。在完成这样的处理工作时,为UI移除一个Collection可能是有益的,一旦完成就重新绑定它。

一般模式

XAML和WPF / UWP的设计考虑了MVVM模式。虽然您可以使用其他方法,但您错过了大约90%的功率,并且每隔一步就会遇到问题。您的代码对我来说看起来不像是一个合适的MVVM模式(Window中的初始化代码将其丢弃;因为缺少使用CollectionView进行排序/过滤部分)。

如果您遵循该模式,那么您所遇到的问题将永远不会存在。无论哪种情况,如果您打算在XAML中做任何认真的工作,都值得学习。几年前我写了MVVM介绍:https://social.msdn.microsoft.com/Forums/vstudio/en-US/b1a8bf14-4acd-4d77-9df8-bdb95b02dbe2/lets-talk-about-mvvm?forum=wpf

内存问题?

虽然速度问题是真实的,但内存问题并非毫无疑问。 mix up the measurement很容易。

很多人都误解了垃圾收集器在设计上非常懒惰。如果它只在应用程序关闭时收集一次,那么这是理想的情况。因此,存储器使用可以在没有任何问题的情况下增加。您可以通过调用GC.Collect()进行测试来检查是否存在内存泄漏。但是在生命应用程序中它不应该出现(选择适当的GC策略)。 https://social.msdn.microsoft.com/Forums/en-US/286d8c7f-87ca-46b9-9608-2b559d7dc79f/garbage-collection-pros-and-limits?forum=csharpgeneral

答案 1 :(得分:1)

解决了VirtualizingPanel.IsVirtualizingWhenGrouping附加财产的问题。它适用于.NET 4.5及更高版本。

<DataGrid x:Name="dataGrid" VirtualizingPanel.IsVirtualizingWhenGrouping="True">
    <!-...-->
</DataGrid>