问题
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?
回答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 callingOnPropertyChangedsimply pass propert name, for exampleOnPropertyChanged("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 toBindingSource. 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.
来源:https://stackoverflow.com/questions/32913050/can-i-control-when-datagridview-reads-and-writes-from-to-its-datasource