2

I'm binding to a List<MyCustomType> and when I put a breakpoint on property getters in MyCustomType, they are seemingly being called repeatedly. What causes the DataGridView to automatically re-read the data and can I control this?

Secondly, I note that when I make changes to the data in the grid, these are not immediately replicated to the DataSource. Putting breakpoints on property setters in MyCustomType, they only seem to be called when I click outside the grid control. How can I make sure changes made in the GUI are immediately applied to the data source?

Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

1 Answers1

1

Re-reading from your properties is completely normal, it's because of rendering. When DataGridView renders cells, it reads from properties.

Supporting INotifyPropertyChanged:

If you want to changes on properties be visible to DataGridView, you should implement INotifyPropertyChanged to have two-way data-binding. This causes the changes in your objects immediately be visible in grid:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Category : INotifyPropertyChanged
{
    #region Properties
    private int _Id;
    public int Id
    {
        get
        {
            return _Id;
        }
        set
        {
            if (_Id == value)
                return;
            _Id = value;
            OnPropertyChanged();
        }
    }

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

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}
  • If you are using .Net 4.5, remove [CallerMemberName] and when calling OnPropertyChanged simply pass propert name, for example OnPropertyChanged("Name")

Using BindingList:

To make changes to a list visible to the grid, for example when you add a new item to your list of data, use BindingList<T> instead of List<T>.

If you use List<T>, you should set the DataSource to null and again to your list to make changes visible to the grid.

BindingList<Category> source = new BindingList<Category>();

private void Form_Load(object sender, EventArgs e)
{
    //Load Data to BindingList
    new List<Category>()
    {
        new Category(){Id=1, Name= "Category 1"},
        new Category(){Id=2, Name= "Category 2"},
    }.ForEach(x=>list.Add(x));

    this.categoryDataGridView.DataSource = list;
}

private void toolStripButton1_Click(object sender, EventArgs e)
{
    //Add data to BindingList 
    //Data will be visible to grid immediately
    list.Add(new Category(){Id=3, Name= "Category 3"});
}
  • Also you can consider binding the BindingList<T> to a BindingSource and the the grid to BindingSource. It makes more sense when using designer.

Using CurrentCellDirtyStateChanged:

Changes on DataGridView will automatically apply on your models OnValidating but as you also mentioned, you can use the CurrentCellDirtyStateChanged event of the grid to commit changes to the data source.

private void categoryDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (categoryDataGridView.IsCurrentCellDirty)
    {
        categoryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}
  • I personally don't recommend such trick for all columns, for example suppose you have a string property that validates for minimum string length of 5, now how can you enter 5 character to it, then you will receive 5 validation error messages until you enter 5 characters.

Choose what you need carefully.

Jeff B
  • 8,572
  • 17
  • 61
  • 140
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks - but can I control when validation is done so my model is updated immediately? I am finding if I toggle a check-box, model is only updated when I click on a different cell for instance. I need what is visible to the user to _always_ represent the model the moment the user changes something. – Mr. Boy Oct 05 '15 at 10:27
  • I think this question my be the answer to #2 but I haven't tested yet. http://stackoverflow.com/questions/6468263/datagridviewcheckboxcolumn-how-to-update-bound-datasource-on-property-changed-i – Mr. Boy Oct 05 '15 at 11:38
  • Hi, I'm getting a compile error that `[CallerMemberName]` isn't known... do I need a specific `using` reference or version of .NET? – Mr. Boy Oct 05 '15 at 15:07
  • @Mr.Boy `using System.Runtime.CompilerServices;` and .Net 4.5 – Reza Aghaei Oct 05 '15 at 15:14
  • @Mr.Boy if you are using .Net 4.0, simply remove that attribute and pass property name to method, for example `OnPropertyChanged("Name");` – Reza Aghaei Oct 05 '15 at 15:15
  • Yeah, I was going to try that :) The problem I see is that `this.PropertyChanged` is always `null` - which says nobody is registered to this event? Is that because I use `List` instead of `BindingList` still? – Mr. Boy Oct 05 '15 at 15:50
  • @Mr.Boy `PropertyChanged` will be used internally in data binding mechanisms. – Reza Aghaei Oct 05 '15 at 15:53
  • I tested, and changing to use `BindingList` made it all work now :) Thanks a lot. – Mr. Boy Oct 05 '15 at 16:04
  • @JeffBridgman thank you for editing typos and making the answer better :) hope you and other users find it more helpful now:) – Reza Aghaei Oct 12 '15 at 22:38