我可以控制 DataGridView 何时读取和写入 from/to 其数据源吗?
Can I control when DataGridView reads and writes from/to its DataSource?
我绑定到 List<MyCustomType>
,当我在 MyCustomType
中的 属性 getter 上设置断点时,它们似乎被重复调用。是什么导致 DataGridView
自动重新读取数据,我可以控制它吗?
其次,我注意到当我对网格中的数据进行更改时,这些不会立即复制到数据源。在 MyCustomType
中的 属性 设置器上放置断点,它们似乎只有在我单击网格控件外部时才会被调用。如何确保在 GUI 中所做的更改立即应用到数据源?
重新读取您的属性是完全正常的,这是因为渲染。当 DataGridView
呈现单元格时,它会从属性中读取。
如果您希望属性的更改对 DataGridView
可见,您应该实现 INotifyPropertyChanged
以实现双向数据绑定。这会导致对象的更改立即在网格中可见:
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
}
- 如果您使用的是 .Net 4.5,请删除
[CallerMemberName]
并且在调用 OnPropertyChanged
时只需传递属性名称,例如 OnPropertyChanged("Name")
使用BindingList:
要使列表的更改对网格可见,例如,当您将新项目添加到数据列表时,请使用 BindingList<T>
而不是 List<T>
。
如果您使用 List<T>
,您应该将 DataSource 设置为 null 并再次设置到您的列表以使更改对网格可见。
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"});
}
- 您还可以考虑将
BindingList<T>
绑定到 BindingSource 并将网格绑定到 BindingSource
。使用设计器时更有意义。
使用CurrentCellDirtyStateChanged:
DataGridView
上的更改将自动应用到您的模型 OnValidating 但正如您还提到的,您可以使用网格的 CurrentCellDirtyStateChanged
事件将更改提交到数据源。
private void categoryDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (categoryDataGridView.IsCurrentCellDirty)
{
categoryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
- 我个人不建议对所有列都使用这种技巧,例如假设您有一个字符串 属性 验证最小字符串长度为 5,现在您如何向它输入 5 个字符,那么您在您输入 5 个字符之前,将收到 5 条验证错误消息。
慎重选择你需要的。
我绑定到 List<MyCustomType>
,当我在 MyCustomType
中的 属性 getter 上设置断点时,它们似乎被重复调用。是什么导致 DataGridView
自动重新读取数据,我可以控制它吗?
其次,我注意到当我对网格中的数据进行更改时,这些不会立即复制到数据源。在 MyCustomType
中的 属性 设置器上放置断点,它们似乎只有在我单击网格控件外部时才会被调用。如何确保在 GUI 中所做的更改立即应用到数据源?
重新读取您的属性是完全正常的,这是因为渲染。当 DataGridView
呈现单元格时,它会从属性中读取。
如果您希望属性的更改对 DataGridView
可见,您应该实现 INotifyPropertyChanged
以实现双向数据绑定。这会导致对象的更改立即在网格中可见:
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
}
- 如果您使用的是 .Net 4.5,请删除
[CallerMemberName]
并且在调用OnPropertyChanged
时只需传递属性名称,例如OnPropertyChanged("Name")
使用BindingList:
要使列表的更改对网格可见,例如,当您将新项目添加到数据列表时,请使用 BindingList<T>
而不是 List<T>
。
如果您使用 List<T>
,您应该将 DataSource 设置为 null 并再次设置到您的列表以使更改对网格可见。
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"});
}
- 您还可以考虑将
BindingList<T>
绑定到 BindingSource 并将网格绑定到BindingSource
。使用设计器时更有意义。
使用CurrentCellDirtyStateChanged:
DataGridView
上的更改将自动应用到您的模型 OnValidating 但正如您还提到的,您可以使用网格的 CurrentCellDirtyStateChanged
事件将更改提交到数据源。
private void categoryDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (categoryDataGridView.IsCurrentCellDirty)
{
categoryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
- 我个人不建议对所有列都使用这种技巧,例如假设您有一个字符串 属性 验证最小字符串长度为 5,现在您如何向它输入 5 个字符,那么您在您输入 5 个字符之前,将收到 5 条验证错误消息。
慎重选择你需要的。