【问题标题】:Modifying large data set that is bound to DataGridView takes a long time?修改绑定到 DataGridView 的大数据集需要很长时间?
【发布时间】:2013-01-07 04:59:31
【问题描述】:

我有一个自定义对象 (MyObject),它实现了 INotifyPropertyChanged 并具有三个属性(一个 int、一个 string 和一个 bool)。这是该对象的代码:

public class MyObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, e);
        }
    }

    private int _Index;
    public int Index
    {
        get { return _Index; }
        set
        {
            if (_Index == value)
            {
                return;
            }

            _Index = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Index"));
        }
    }

    private string _Name;
    public string Name
    {
        get { return _Name; }
        set
        {
            if (_Name == value)
            {
                return;
            }

            _Name = value;
            _Modified = true;
            OnPropertyChanged(new PropertyChangedEventArgs("Name"));
        }
    }

    private bool _Modified;
    public bool Modified
    {
        get { return _Modified; }
        set
        {
            if (_Modified == value)
            {
                return;
            }

            _Modified = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Modified"));
        }
    }

    public MyObject()
    {
    }

    public MyObject(int index, string number, bool modified)
    {
        Index = index;
        Number = number;
        Modified = modified;
    }
}

长话短说,我将这些对象的 BindingList 连接到 DataGridView,如下所示:

BindingList<MyObject> myObjectList = new BindingList<MyObject>();
BindingSource bindingSourceForMyObjects = new BindingSource();

bindingSourceForMyObjects.DataSource = myObjectList;
dataGridViewMyObjects.DataSource = bindingSourceForMyObjects;

当我的应用程序运行时,myObjectList 将填充 100,000 个对象(从文件中读取)并显示在我的 DataGridView 中。我在 DataGridView 中只有一列,它显示我的对象的字符串属性。我的用户可能想要选择 DataGridView 中的所有行并“删除”它们。我在删除术语周围使用引号,因为对象实际上并没有被删除,而是所有 100,000 个对象的字符串属性都设置为 string.Empty。现在,我使用以下代码执行该操作:

myObjectList.ToList().ForEach(c => c.Name = string.Empty);

此操作大约需要 55 秒才能完成。我也试过:

foreach (DataGridViewRow row in dataGridViewMyObjects.SelectedRows)
{
    row.Cells["NAME"].Value = string.Empty;
}

这需要的时间略短,为 49 秒,但仍然是一个很长的操作。另外,我还为 bindingSourceForMyObjects 定义了一个 ListChanged 事件处理程序,如下所示:

private void bindingSourceForMyObjects_ListChanged(object sender, ListChangedEventArgs e)
{
    if (e.ListChangedType == ListChangedType.ItemChanged)
    {
        dataGridViewMyObjects.Rows[e.NewIndex].DefaultCellStyle.BackColor = Color.Red;
        dataGridViewMyObjects.Rows[e.NewIndex].DefaultCellStyle.ForeColor = Color.White;
    }
}

如您所见,当我以编程方式更改基础对象列表时,我使用它来直观地指示 DataGridView 中已更改的行(就像我将它们全部设置为 string.Empty 时所做的那样)。当我“删除”所有对象时,这确实会增加一些开销,但即使没有这个事件处理程序,它仍然是一个冗长的操作。

最后,我的 WinForms 应用程序目前是单线程的,但我计划编写多线程代码,这样做是有意义的。这个操作可能是其中一种情况,但在我走这条路之前,还有什么我遗漏的(或者我不需要做的事情)可以提高这个操作的性能,所以它不会几乎需要一分钟才能完成?或者这只是处理 100,000 个对象的本质?

【问题讨论】:

  • 你能区分在.ToList() 中花费的时间与在.ForEach(c =&gt; ...) 中花费的时间吗?
  • c.Name 属性是否在其设置器中执行任何操作或只是一个自动属性?
  • .ToList() 需要
  • 控制台应用程序中的 for-loop 性能是否相似(即,在 gui 之外)?
  • 你能发布你的代码来实现INotifyPropertyChanged吗?你是从哪里发起活动的?我刚刚使用上面的代码做了一个快速测试,我可以在几秒钟内更新大约 250k 行。 INotifyPropertyChanged 实现可能会导致速度变慢。

标签: c# winforms multithreading datagridview


【解决方案1】:

试试这个:

dataGridView1.SuspendLayout();
        //do changes
        dataGridView1.ResumeLayout();

【讨论】:

  • 不幸的是,这对我来说并没有真正提高性能。不过,它确实比我之前所说的时间缩短了约 1.5 秒。谢谢你的建议。
【解决方案2】:

@User685869 感谢您发布扩展对象。

在完成了一些快速测试示例之后,似乎引发 PropertyChanged 事件会导致速度变慢。删除对它的调用将导致操作在一两秒内完成我的电脑上的 100k 记录。

即使没有事件处理程序也会发生这种情况。

我假设你已经实现了你需要的接口;如果你不这样做,那么删除它会解决你的问题。

我可以建议的唯一解决方案是创建一个 boolean 变量,您可以将其设置为 true 以防止在进行批量更新时引发事件并在之后切换回正常状态。这样做的缺点当然是在进行批量操作时不会引发任何事件。

例如:-

我的对象

public boolean bulk = false;
public string Name
{
    get { return _Name; }
    set
    {
        if (_Name == value)
        {
            return;
        }

        _Name = value;
        _Modified = true;
        OnPropertyChanged(new PropertyChangedEventArgs("Name"));
    }
}

父代码

_bindingList.ToList().ForEach(c =>
    {
       c.bulk = true;
       c.Name = string.Empty;
       c.bulk = false;
    });

【讨论】:

  • 谢谢!我想我可以做这样的事情。通常情况下,我只需要将 DataGridView 中所做的更改推回对象列表。有几种情况导致我通过 INotifyPropertyChanged 添加双向绑定,但我可以根据您的建议解决这些问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
  • 2014-11-10
  • 2015-04-25
  • 2020-10-03
  • 2017-06-24
相关资源
最近更新 更多