BindingList and INotifyPropertyChanged performance

15 posts, 2 answers
  1. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 02 Mar 2011 Link to this post

    Hi guys,

    I fall into a problem of performance when binding a BindingList<T> to a grid where T implements INotifyPropertyChanged interface.

    Exactly, what happens is that, having a lot of rows (10000), when I change a property of one object the FirePropertyChanged method takes some seconds and I don't understand why.

    Can someone help me about that?

    Thanks in advance.

    Stefano.
  2. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 02 Mar 2011 Link to this post

    Hello Stefano,

    And you are not doing anything else in the OnPropertyChanged of the objects?
    If not, can you please provide some more info, like the number of columns and so on, so that i can prepare a test app with this.

    Hope this helps, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga
    Telerik WinForms MVP
  3. UI for WinForms is Visual Studio 2017 Ready
  4. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 02 Mar 2011 Link to this post

    Hi Emanuel,

    thank you for your fast response!

    I never handle the PropertyChanged event in my code, and I use it only to notify the grid on data changed.

    This is the interface implementation:
    #region INotifyPropertyChanged Members
     
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
     
    protected bool CheckPropertyChanged<T>(string propertyName, T oldValue, T newValue)
    {
        if (oldValue == null && newValue == null)
        {
            return false;
        }
     
        if ((oldValue == null && newValue != null) || (oldValue != null && newValue == null) || !oldValue.Equals((T)newValue))
        {
            FirePropertyChanged(propertyName);
            return true;
        }
     
        return false;
    }
     
    public void FirePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
           this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
     
    #endregion

    and I use it on this property of my object:
    public bool IsChecked { get { return isChecked; } set { var oldvalue = isChecked; isChecked = value; CheckPropertyChanged<bool>("IsChecked", oldvalue, value); } }

    What I do is to fill a BindingList<MyObj> and with it I set grid.DataSource; at this point the grid (40k rows and 20 columns) takes 30 seconds to bind.
    Then, when I change, programmatically, IsChecked property of one of the objects in the list calling "this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName))"takes 45 seconds.

    Let me know and do not esitate to ask me any other info.

    Thanks a lot,
    Stefano.
  5. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 02 Mar 2011 Link to this post

    Hello again Stefano,

    Can you please try this more conventional approach to INotifyPropertyChanged, like the following class for example:
    Please try it if possible like this and let me know what if it's the same or not:
    public class CustomClass : INotifyPropertyChanged
    {
        private bool check;
     
        public bool Check
        {
            get { return check; }
            set
            {
                if (check != value)
                {
                    check = value;
                    OnPropertyChanged("Check");
                }
            }
        }
     
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
     
        #region INotifyPropertyChanged Members
     
        public event PropertyChangedEventHandler PropertyChanged;
     
        #endregion INotifyPropertyChanged Members
    }

    Hope this helps, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga
    Telerik WinForms MVP
  6. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 02 Mar 2011 Link to this post

    Hello Emanuel,

    I tried the solution you propose but nothing changes: executing "PropertyChanged(this, ...)" takes a lot of seconds.

    Best Regards,
    Stefano.
  7. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 02 Mar 2011 Link to this post

    Hello again Stefano,

    Sorry, i just cannot reproduce this, please try the following and let me know of the results:
    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    using Telerik.WinControls.UI;
     
    public partial class Form1 : Form
    {
        private RadGridView radGridView1;
        public Form1()
        {
            InitializeComponent();
            this.Size = new System.Drawing.Size(800, 600);
     
            this.Controls.Add(radGridView1 = new RadGridView());
            radGridView1.Dock = DockStyle.Fill;
            radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
     
            var list = new BindingList<Person>();
            for (int i = 0; i < 100000; i++)
            {
                list.Add(new Person(i, "Person"+i, i%2 == 0, i% 20 == 0, DateTime.Now.AddHours(i), DateTime.Now.AddHours(-i)));
            }
     
            radGridView1.DataSource = list;
        }
     
        private class Person : INotifyPropertyChanged
        {
            private int id;
     
            public int Id
            {
                get { return id; }
                set
                {
                    id = value;
                    OnPropertyChanged("Id");
                }
            }
            private string name;
     
            public string Name
            {
                get { return name; }
                set
                {
                    name = value;
                    OnPropertyChanged("Name");
                }
            }
            private bool alive;
     
            public bool Alive
            {
                get { return alive; }
                set
                {
                    alive = value;
                    OnPropertyChanged("Alive");
                }
            }
            private bool inDept;
     
            public bool InDept
            {
                get { return inDept; }
                set
                {
                    inDept = value;
                    OnPropertyChanged("InDept");
                }
            }
            private DateTime dateOfBirth;
     
            public DateTime DateOfBirth
            {
                get { return dateOfBirth; }
                set
                {
                    dateOfBirth = value;
                    OnPropertyChanged("DateOfBirth");
                }
            }
            private DateTime updateDate;
     
            public DateTime UpdateDate
            {
                get { return updateDate; }
                set
                {
                    updateDate = value;
                    OnPropertyChanged("UpdateDate");
                }
            }
     
            public Person(int id, string name, bool alive, bool inDept, DateTime birthDate, DateTime update)
            {
                this.Id = id; this.Name = name; this.Alive = alive; this.InDept = inDept; this.DateOfBirth = birthDate; this.UpdateDate = update;
            }
     
            #region INotifyPropertyChanged Members
     
            public event PropertyChangedEventHandler PropertyChanged;
     
            #endregion
     
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }

    Hope this helps, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga
    Telerik WinForms MVP
  8. Answer
    Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 02 Mar 2011 Link to this post

    Hi Guys,

    @Stefano, yes, I can replicate your issue. With Emanuel's sample which follows the standard pattern for INotifyPropertyChanged when running all appears to be fine. However, sorting takes around 20 seconds and then editing any column takes around 30 seconds (after sorting).

    I will see if I can come up with any suggestions for you, in the meantime if you have any questions, please let me know
    Thanks
    Richard
  9. Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 02 Mar 2011 Link to this post

    Hello,

    Ok, I know we are dealing with 100,000 rows here, which is quite a few, but editing takes a long time after sorting which doesn't happen before sorting. This may be related to this issue in the Public Issue Tracking System which is resolved in the up coming release. Note the comments in the issue, that editing was slow after sorting.

    I have also verified that removing the INotifyPropertyChanged interface, this still occurs.
    Regards,
    Richard
  10. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 02 Mar 2011 Link to this post

    Hi guys,

    I confirm the Richard answer: my grid has sorting and filtering enabled and if I disable them, the grid become faster.

    Refer to Emanuel test sample, if I need to make a massive change, for example invert the value of Alive field for all items, which the best approch?

    if I do this, it takes a lot:
    private void button1_Click(object sender, EventArgs e)
    {
         for (int i = 0; i < list.Count; i++)
            list[i].Alive = !list[i].Alive;
    }

    but, If I do this, it takes few time (on ResetBindings); is that correct?
    private void button1_Click(object sender, EventArgs e)
    {
        list.RaiseListChangedEvents = false;
        for (int i = 0; i < list.Count; i++)
            list[i].Alive = !list[i].Alive;
        list.RaiseListChangedEvents = true;
        list.ResetBindings();
    }
  11. Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 02 Mar 2011 Link to this post

    Hello,

    Yes, your second method would be quick as you are not raising the changed events as you loop over the list, and then are notifying the grid of the changes after by calling ResetBindings. You can read a full exmaple of ResetBindings at this MSDN Link

    I hope this helps but let me know if you have further questions
    Richard
  12. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 02 Mar 2011 Link to this post

    Hello Stefano,

    For this you can wrap your change in either a
    using (radGridView.DeferRefresh())
    {
    // perform required changes
    }
     
    // or
    radGridView.BeginUpdate();
    //perform required changes
    radGridView.EndUpdate();

    Hope this helps, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga
  13. Answer
    Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 02 Mar 2011 Link to this post

    Hi Emanuel,

    Sadly, the DeferRefresh has no effect on this sample. What makes the performance difference is
    list.RaiseListChangedEvents = false;
    // loop
    list.RaiseListChangedEvents = true;
    list.ResetBindings();
    though I also would have thought that using DeferRefresh should have made a difference.
    Let me know if you have any questions or comments
    Richard
  14. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 03 Mar 2011 Link to this post

    So, we are assuming that there is a problem of performance, maybe not directly related to INotifyPropertyChanged interface.

    @Richard: do you really think it could be related to that "issue" (you linked), or could be right to open a new issue?
  15. Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 03 Mar 2011 Link to this post

    Hello,

    I think it is likey that it is related, and it is highly probable that Telerik have already seen this thread, however, for qiucker support it may be worth opening a support ticket and quoting this forum thread so that Telerik are able to keep a trail of the issue. When opening a support ticket, it will be useful to include a very basic project to replicate the issue.

    If I can be of further help though, or you need me to open the ticket, please just let me know
    Thanks
    Richard
  16. codicezerouno
    codicezerouno avatar
    22 posts
    Member since:
    Feb 2009

    Posted 03 Mar 2011 Link to this post

    In the meanwhile, cause I cannot remove sorting and filtering functionality to the customer, I found a work around:

    I stop using INotify... interface and, after change a property value, I call grid.CurrentView.UpdateView() or .InvalidateRow() if you know the row.

    Stefano.
Back to Top
UI for WinForms is Visual Studio 2017 Ready