C# WPF数据网格分组导致可怕的内存消耗

C# WPF数据网格分组导致可怕的内存消耗,c#,wpf,C#,Wpf,我就是搞不懂数据网格分组有什么不对 我有这样一个最小的应用程序: public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); List<Rec> data = new List<Rec>(); var rnd = new Random(); for (int i = 0

我就是搞不懂数据网格分组有什么不对

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

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        List<Rec> data = new List<Rec>();
        var rnd = new Random();
        for (int i = 0; i < 3000; i++)
        {
            data.Add(new Rec() { Group = string.Format("Group{0}", rnd.Next(1,3)), Name = string.Format("Item{0}", rnd.Next(1,50)), Age = rnd.Next(10,100) });
        }

        var dataView = new ListCollectionView(data);
        dataView.GroupDescriptions.Add(new PropertyGroupDescription("Group"));

        dataGrid.ItemsSource = dataView;
    }
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
列表数据=新列表();
var rnd=新随机数();
对于(int i=0;i<3000;i++)
{
data.Add(new Rec(){Group=string.Format(“Group{0}”,rnd.Next(1,3)),Name=string.Format(“Item{0}”,rnd.Next(1,50)),Age=rnd.Next(10100)});
}
var dataView=新的ListCollectionView(数据);
添加(新属性GroupDescription(“组”);
dataGrid.ItemsSource=dataView;
}
}
以及它的XAML:

<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>


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

答案不止一个

设计

3000行不是一个小数目。我的经验是,100个字段/条信息是您应该向用户显示的限制。每行需要的字段越多,您应该显示的行就越少(减少到5-10行),而他甚至无法处理更多的内容

在查询过程中尽可能多地进行筛选。如果您的DBMS支持分页,也可以使用它。检索所有,然后在GUI中进行过滤是一个常见的错误。3000看起来很简单

UI虚拟化

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

绑定垃圾邮件

用户界面更新代价高昂。对于每个用户触发的事件,仅执行一次并不重要。但是,如果从任何形式的循环运行,它都会成为一个问题。我为此问题编写了一些示例代码:

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");
        }
    }
} 
使用系统;
使用System.Windows.Forms;
命名空间UIWriteOverhead
{
公共部分类Form1:Form
{
公共表格1()
{
初始化组件();
}
int[]getNumbers(int上限)
{
int[]ReturnValue=新int[上限];
for(int i=0;i
现在几乎没有什么事情能像过多的更改通知那样导致UI更新。在执行类似于这样的处理工作时删除UI的集合,以便在完成后重新绑定它,这可能是有益的

一般模式

XAML和WPF/UWP的设计考虑了MVVM模式。虽然您可以使用其他方法,但您会错过大约90%的功能,每隔一步就会遇到问题。在我看来,您的代码不是一个合适的MVVM模式(窗口中的初始化代码泄露了它;排序/筛选部分也没有使用CollectionView)

如果你遵循这种模式,你所遇到的问题很有可能永远不会存在。无论哪种情况,如果您计划在XAML中进行任何严肃的工作,都值得学习。几年前,我写了一篇MVVM简介:

内存问题?

虽然速度问题是真实存在的,但内存问题并非必然存在。这很容易理解

很多人误解了垃圾收集器在设计上是相当懒惰的。如果它在应用程序关闭时只收集一次,那么这是理想的情况。因此,内存使用量可以增加而不存在任何问题。您可以通过调用GC.Collect()进行测试来检查是否存在内存泄漏。但是在life应用程序中,它不应该出现(而是选择一个合适的GC策略)

解决了附加属性的问题。它在.NET4.5及更高版本上工作

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


分组阻止了行虚拟化,这会导致为每一行创建UI元素,甚至是那些偏离屏幕的元素。这是一个很好的提示。谢谢在谷歌上搜索时,我发现了一个virtualzingpanel.isvirtualzingwengroup附加属性,它解决了一个问题。