Winform数据绑定原理(一)——使用BIndingSource
一、控件的赋值
我们的目标是实现这样的效果:Label 控件会随着 TextBox 控件内容的修改而更新显示

1.传统方式实现
// 传统事件驱动代码
private void textBox1_TextChanged(object sender, EventArgs e)
{
person.Name = textBox1.Text;
label1.Text = $"Hello, {person.Name}";
}
在传统的WinForms开发中,开发者需要手动同步界面控件与后台数据源的状态,这里用户在TextBox中输入内容时,需要编写TextChanged事件处理代码,将新值手动更新到数据对象;反之,若数据对象的属性被代码修改,也需要手动更新对应的控件。这种方式存在以下问题:
- 代码冗余:每个控件都需要编写事件监听和数据同步逻辑,当界面变得复杂时,事件处理代码会大量增加,UI 与业务逻辑耦合严重
- 维护困难:当界面或数据模型变动时,需修改多处代码。
- 易出错:手动同步可能遗漏某些场景
2.绑定模式
public Form1()
{
InitializeComponent();
var person = new Person(); // 创建 ViewModel 实例
// 绑定 TextBox.Text 到 ViewModel.Name(双向绑定)
textBox1.DataBindings.Add(
"Text", // 控件的属性
person, // 数据源(ViewModel)
"Name", // 数据源的属性
false, // 是否启用格式化
DataSourceUpdateMode.OnPropertyChanged // 更新模式
);
// 绑定 Label.Text 到 ViewModel.Title(单向绑定)
label1.DataBindings.Add(
"Text",
person,
"Title",
false,
DataSourceUpdateMode.Never // 数据源更新时不回写
);
}
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
}
}
public string Title => $"Hello {Name}";
public event PropertyChangedEventHandler PropertyChanged;
}
数据绑定的核心思想是通过数据绑定将视图与逻辑解耦,实现双向自动同步:
- 数据源变更时,自动更新界面
- 界面操作(如用户输入)自动更新数据源。
3.绑定说明
我们来看这行代码:textBox1.DataBindings.Add("Text", person, "Name", false, DataSourceUpdateMode.OnPropertyChanged); 前三个参数表示将 textBox1 控件的 Text 属性绑定到 personVM 对象的 Name 属性。这样,当两边对象的属性发生更改时,都会通知另一方。当我们通过控件(UI 界面)修改 textBox1 的值时,就会触发 person.Name 属性的 set 方法,而 set 方法中又调用了 Title 的 PropertyChanged 事件。由于 label1 绑定了 Title,所以 Label 也会随之更新。
代码解释:Control属性DataBindings是 从Control父类继承的属性 是一个 ControlBindingsCollection,其中包含该控件的 Binding 对象。Add方法内部则是new了一个Binding 。
二、BindingSource 数据源
1.简单使用
在上述例子中,保持其他代码不变,将 Form 构造函数中的代码修改如下:
InitializeComponent();
var person = new PersonViewModel();
BindingSource source = new BindingSource();
source.DataSource = person;
textBox1.DataBindings.Add("Text", source, "Name", false, DataSourceUpdateMode.OnPropertyChanged);
label1.DataBindings.Add("Text", source, "Title", false, DataSourceUpdateMode.Never);
person.Name = "test";
再操作UI界面,会得到相同的效果。那么,BindingSource作用是什么呢? 为什么要用它呢?
2.复杂例子
我们先来看一个稍微复杂的例子

在这个例子中,我们可能会觉得不知道如何使用Binding了,因为这里不再是一个属性对应一个值,而是一个集合。不信的话,你可以尝试使用以下代码:dataGridView1.DataBindings.Add("DataSource", listPerson, "Name", false, DataSourceUpdateMode.OnPropertyChanged) 你会发现没有任何响应,甚至会报错。
下面是正确的简单实现:
List<Person> listPerson;
public BindingSourceComp01()
{
InitializeComponent();
listPerson = new List<Person>()
{
new Person() { ID=1,Name="关羽"},
new Person() { ID=2,Name="张飞"},
new Person() { ID=3,Name="马超"}
};
dataGridView1.DataSource = listPerson;
}
private void button1_Click(object sender, EventArgs e)
{
var name = textBox1.Text;
listPerson.Add(new Person() { ID = listPerson.Count + 1, Name = name });
// 没有绑定,需要手动刷新
dataGridView1.DataSource = null;
dataGridView1.DataSource = listPerson;
}
这时候,BindingSource 就可以发挥作用了,只需要维护创建的数据源对象即可,代码会变得简洁:
BindingSource bindingSource;
public BindingSourceComp01()
{
InitializeComponent();
List<Person> listPerson = new List<Person>()
{
new Person() { ID=1,Name="关羽"},
new Person() { ID=2,Name="张飞"},
new Person() { ID=3,Name="马超"}
};
// 将数据集合设置到 BindingSource
bindingSource = new BindingSource() { DataSource = listPerson };
// 将 BindingSource 作为 DataGridView 的数据源
dataGridView1.DataSource = bindingSource;
}
private void button1_Click(object sender, EventArgs e)
{
var name = textBox1.Text;
// 注意这里要用BindingSource 不能再用普通集合操作了
bindingSource.Add(new Person()
{
ID = bindingSource.Count + 1, // 这里id不是很严谨,因为可能有删除操作,所以这里只是简单的加1
Name = name
});
}
3.解释 BindingSource
BindingSource组件在Windows Forms中主要用于简化数据绑定并增强控件与数据源之间的交互,其核心功能及特点如下:
- 核心作用
- 提供间接绑定层 ,将控件与数据源解耦。通过将控件绑定到BindingSource,再由BindingSource连接实际数据源(如数据库、对象集合等),实现数据变更的自动同步
- 同步与导航
- 多个控件绑定同一BindingSource时,自动同步当前项及数据变更,避免手动更新
- 内置
CurrencyManager管理数据导航(如记录切换),简化分页或数据遍历操作
三、综合例子
带一个新增和删除的效果(暂时不考虑额外窗体编辑)

代码如下
private void InitDataGridView()
{
bindingSource = new BindingSource() { DataSource = Student.GetList() };
dgvStudent.DataSource = bindingSource;
}
private void btnAdd_Click(object sender, EventArgs e)
{
bindingSource.Add(new Student
{
Id = 10,
Name = "New Student",
Age = 20,
Classes = 4,
Address = "New Address"
});
}
private void btnDel_Click(object sender, EventArgs e)
{
// 通过bindingSource.Current获取当前选中的对象
if (bindingSource.Current is Student item)
{
var result = MessageBox.Show("是否删除?", "删除", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result.Equals(DialogResult.OK))
bindingSource.Remove(item);
}
}

浙公网安备 33010602011771号