逻辑上,数据绑定是“对象属性”和“控件属性”之间的关联,这种关联是由数据绑定引擎完成的。
当对象属性和控件属性其中之一发生变化时,就会自动更新另外一方的数据。c#
定义一个数据源RaceCarDriver.cs,实现接口System.ComponentModel.INotifyPropertyChanged。架构
using System.ComponentModel;namespace BasicBindingEngine{ class RaceCarDriver : INotifyPropertyChanged { //实现接口INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } string name; int wins; public RaceCarDriver(string name, int wins) { this.name = name; this.wins = wins; } public string Name { get { return name; } set { name = value; //提供被修改的属性名称 OnPropertyChanged("Name"); } } public int Wins { get { return wins; } set { wins = value; OnPropertyChanged("Wins"); } } }}
窗体代码,实现数据绑定。函数
using System;using System.ComponentModel;using System.Windows.Forms;namespace BasicBindingEngine{ public partial class Form1 : Form { RaceCarDriver raceCarDriver = new RaceCarDriver("M Schumacher", 500); public Form1() { InitializeComponent(); //将初始的RaceCarDriver状态复制到文本框控件 this.nameText.Text = this.raceCarDriver.Name; this.winsText.Text = this.raceCarDriver.Wins.ToString(); //数据绑定,双向检测 //检测文本框控件的修改,更新数据源数据 this.nameText.TextChanged += NameText_TextChanged; this.winsText.TextChanged += WinsText_TextChanged; //检测RaceCarDriver对象的修改,更新文本框数据显示 this.raceCarDriver.PropertyChanged += RaceCarDriver_PropertyChanged; } private void WinsText_TextChanged(object sender, EventArgs e) { this.raceCarDriver.Wins = int.Parse(this.winsText.Text); } private void NameText_TextChanged(object sender, EventArgs e) { this.raceCarDriver.Name = this.nameText.Text; } //直接修改RaceCarDriver的wins数据 private void AddWinBtn_Click(object sender, EventArgs e) { ++this.raceCarDriver.Wins; } private void RaceCarDriver_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case "Name": this.nameText.Text = this.raceCarDriver.Name; break; case "Wins": this.winsText.Text = this.raceCarDriver.Wins.ToString(); break; } } }}
Windows Forms 数据绑定引擎的基本生成块就是Binding对象,它将控件数据绑定到对象属性上。 this
简单绑定:将控件属性绑定到对象属性的操做叫作简单绑定。 spa
项数据源:数据源的数据是存在于单个项中。例如RaceCarDriver这样的绑定对象。code
绑定关系类System.Windows.Forms.Binding
,经过此能够省却写属性检测代码。orm
public Binding(string propertyName, object dataSource, string dataMember); propertyName: 要绑定的控件属性的名称。 dataSource: 一个 System.Object,它表明数据源。 dataMember: 要绑定到的属性或列表。
窗体代码,经过Binding实现数据绑定。控件的DataBindings添加绑定关系。对象
using System;using System.Windows.Forms;namespace BasicBindingEngine{ public partial class Form1 : Form { RaceCarDriver raceCarDriver = new RaceCarDriver("M Schumacher", 500); public Form1() { InitializeComponent(); //将Name和Wins属性绑定到Name和Wins文本框上 Binding nameBinding = new Binding("Text", this.raceCarDriver, "Name", true); this.nameText.DataBindings.Add(nameBinding); Binding winsBinding = new Binding("Text", this.raceCarDriver, "Wins", true); this.winsText.DataBindings.Add(winsBinding); } //直接修改RaceCarDriver的wins数据 private void AddWinBtn_Click(object sender, EventArgs e) { ++this.raceCarDriver.Wins; } }}
添加绑定时会发生两件事情:1.数据绑定引擎自动使用对象属性的值来填充控件的属性。2.任何须需的数据转换都自动完成。blog
初始化以后,数据绑定引擎负责控件属性和数据源对象属性之间的同步。为了确保数据的变化能够从对象复制到控件上,绑定引擎会查找INotifyPropertyChanged.PropertyChanged事件。接口
只有在对象触发INotifyPropertyChanged.PropertyChanged事件而且公共属性是可读写(实现了get,set访问器)的状况下,简单绑定才是双向的。
列表数据源:是在绑定操做中做为数据来使用的同构对象的集合。对象是必须实现' IList '接口的类。
绑定管理器:负责管理特定数据源的绑定集合,有两个方面的做用:属性管理器和当前项管理器。两个管理器都是由抽象基类BindingManagerBase的具体实现。
属性管理器:是PropertyManager类的一个实例,而且是为项数据源建立的。
当前项管理器:是CurrencyManager类的一个实例,而且是为列表数据源来建立的。可用于跟踪当前对象的位置。
using System;using System.Collections.Generic;using System.Windows.Forms;namespace SimpleBindingAndListDataSource{ public partial class SBALDS : Form { //建立强类型数据列表数据源 List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>(); public SBALDS() { InitializeComponent(); //用数据项填充列表数据源 this.raceCarDrivers.Add(new RaceCarDriver("M.Schumacher", 500)); this.raceCarDrivers.Add(new RaceCarDriver("A.Senna", 1000)); this.raceCarDrivers.Add(new RaceCarDriver("A.Prost", 400)); //将Name和Wins属性绑定到Name和Wins文本框上 this.nameTextBox.DataBindings.Add("Text", this.raceCarDrivers, "Name"); this.winsTextBox.DataBindings.Add("Text", this.raceCarDrivers, "Wins"); RefreshShowMangerPositon(); } BindingManagerBase BindingManager { //访问RaceCarDriver列表数据源的绑定管理器 get { return this.BindingContext[this.raceCarDrivers]; } } private void MoveFirstBtn_Click(object sender, EventArgs e) { this.BindingManager.Position = 0; RefreshShowMangerPositon(); } private void MovePreviousBtn_Click(object sender, EventArgs e) { --this.BindingManager.Position; RefreshShowMangerPositon(); } private void MoveNextBtn_Click(object sender, EventArgs e) { ++this.BindingManager.Position; RefreshShowMangerPositon(); } private void MoveLastBtn_Click(object sender, EventArgs e) { this.BindingManager.Position = this.BindingManager.Count - 1; RefreshShowMangerPositon(); } void RefreshShowMangerPositon() { int count = this.BindingManager.Count; int position = this.BindingManager.Position + 1; this.bindingNavigatorPositionItem.Text = position.ToString(); this.bindingNavigatorCountItem.Text = string.Format("/{0}", count); this.bindingNavigatorMoveFirstItem.Enabled = (position > 1); this.bindingNavigatorMovePreviousItem.Enabled = (position > 1); this.bindingNavigatorMoveNextItem.Enabled = (position < count); this.bindingNavigatorMoveLastItem.Enabled = (position < count); } }}
使用这种方式时,简单绑定和列表数据源中在添加绑定时没有实现事件event PropertyChangedEventHandler PropertyChanged
,致使在修改数据源属性时不能更新控件属性。使用IBindingList
接口解决这种问题。
复杂绑定:将控件的一个属性绑定到整个列表数据源中。复杂绑定中的复杂是指控件自身内置的对操做和输出列表数据的附加支持。
复杂控件使用DataSource
属性访问列表数据源,使用DisplayMember
设置须要显示的列表数据源属性
...引用namespace ComplexBindingListDataSources{ public partial class Form1 : Form { //建立强类型列表数据源 List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>(); public Form1() { InitializeComponent(); this.raceCarDrivers.Add(new RaceCarDriver("M.Schuamacher", 500)); this.raceCarDrivers.Add(new RaceCarDriver("A.Senna", 1000)); this.raceCarDrivers.Add(new RaceCarDriver("A.Prost", 400)); //将Name和Wins属性简单绑定到Name和Wins文本框 //当用户输入数据时保持对象属性的同步 this.nameTextBox.DataBindings.Add("Text", this.raceCarDrivers, "Name"); this.winTextBox.DataBindings.Add("Text", this.raceCarDrivers, "Wins"); //复杂绑定列表框到RaceCarDriver列表数据源 this.raceCarDriverListBox.DataSource = this.raceCarDrivers; //指定列表数据源中的项出如今列表框中的属性 this.raceCarDriverListBox.DisplayMember = "Name"; this.raceCarDriversDataGridView.DataSource = this.raceCarDrivers; RefreshShowMangerPositon(); } .... 导航相关代码 .... }}
在绑定中,对列表数据源的修改不会当即显示在界面上。这就须要实现一个通讯协议让控件可以知道列表数据源的变化。经过实现接口IBindingList
来实现通知。
IBindingList:是关于数据绑定架构的约定,扩展了IList接口,增长了为列表数据源提供的数据绑定特定的功能。实现添加、更新、删除列表数据源项。支持列表修改通知功能,经过订阅ListChanged事件。
System.ComponentModel.BindingList
:是IBindingList接口的泛型实现。实现了列表管理功能(AllowEdit、AllowNew、AllowRemove、AddNew)和 实现IBindingList的变动通知功能的子集(SupportsChangeNotification、ListChanged)。
将List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>();
改写为BindingList<RaceCarDriver> raceCarDrivers = new BindingList<RaceCarDriver>();
就实现了列表数据源变动时通知功能。
class RaceCarDriver : INotifyPropertyChanged{ //要让DataGridView能够添加新行,就须要这个构造函数 public RaceCarDriver() { } ....}
经过BindingSource类可将现存的IList升级到IBindingList。
//建立强类型数据列表数据源List<RaceCarDriver> raceCarDrivers = new List<RaceCarDriver>();BindingSource raceCarDriversBS = new BindingSource();public SBALDS(){ InitializeComponent(); //用数据项填充列表数据源 this.raceCarDrivers.Add(new RaceCarDriver("M.Schumacher", 500)); this.raceCarDrivers.Add(new RaceCarDriver("A.Senna", 1000)); this.raceCarDrivers.Add(new RaceCarDriver("A.Prost", 400)); //给List<RaceCarDriver>加上列表管理和变动通知功能 raceCarDriversBS.DataSource = this.raceCarDrivers; //将数据源变为this.raceCarDriversBS this.nameTextBox.DataBindings.Add("Text", this.raceCarDriversBS, "Name"); this.winsTextBox.DataBindings.Add("Text", this.raceCarDriversBS, "Wins"); ...}