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.