数据绑定的详细解释
数据绑定的详细解释
从一个Windows窗体的角度来看,“数据绑定”是一种把数据绑定到一种用户界面元素(控件)的通用机制。在Windows窗体中有两种数据绑定类型:简单绑定和复杂绑定。
简单绑定
简单绑定是将一个用户界面元素(控件)的属性绑定到一个类型(对象)实例上的某个属性的方法。例如,如果一个开发者有一个Customer类型的实例,那么他就可以把Customer的“Name”属性绑定到一个TextBox的“Text”属性上。“绑定”了这2个属性之后,对TextBox的Text属性的更改将“传播”到Customer的Name属性,而对Customer的Name属性的更改同样会“传播”到TextBox的Text属性。Windows窗体的简单数据绑定支持绑定到任何public或者internal级别的.NET Framework属性。
例子:对一个业务对象的简单数据绑定
/*******************************************************************
* 设置(使用VS的窗体设计器):
*
*添加3个 TextBox到窗体Form(textBox1, textBox2 和textBox3)
*添加下面的代码到Form.Load事件处理方法
******************************************************************/
/*******************************************************************
* 创建一个 customer 实例(使用下面的 Customer 类型)
******************************************************************/
Customer cust = newCustomer(0, "Mr. Zero", 10.0M);
/*******************************************************************
* 绑定textBox1, textBox2 和textBox3
******************************************************************/
this.textBox1.DataBindings.Add("Text", cust, "ID", true);
this.textBox2.DataBindings.Add("Text", cust, "Name", true);
this.textBox2.DataBindings.Add("Text", cust, "Rate", true);
Customer业务对象的定义:
/*******************************************************************
* 设置(使用 Visual Studio 窗体设计器):
*
*添加一个新的 C# 类文件到你的项目 并且把它命名为 “Customer.cs”
*用下面的那个Customer类取代自动生成的Customer类代码
******************************************************************/
public class Customer
{
/* 私有变量 */
private int _id;
private string _name;
private Decimal _rate;
/* 构造函数 */
public Customer()
{
this.ID = -1;
this.Name = string.Empty;
this.Rate = 0.0M;
}
public Customer(int id, string name, Decimal rate)
{
this.ID = id;
this.Name = name;
this.Rate = rate;
}
/* 公共属性 */
public int ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public Decimal Rate
{
get { return _rate; }
set { _rate = value; }
}
}
复杂数据绑定
复杂数据绑定是把一个基于列表的用户界面元素(比如ComboBox、Grid)绑定到一个数据实例列表(比如DataTable)的方法。和简单数据绑定一样,复杂数据绑定通常也是用户界面元素发生改变时传播到数据列表,数据列表发生改变时传播到用户界面元素。Windows窗体复杂数据绑定支持绑定到那些支持IList接口(或者是IEnumerable接口,如果使用的是BindingSource组件的话)的数据列表。
例子:复杂数据绑定(VS 2005)
/*******************************************************************
* 设置 (使用 Visual Studio 窗体设计器):
*
*添加一个DataGridVie到窗体Form (dataGridView1)
*添加下面的代码到Form.Load事件响应方法
******************************************************************/
/*******************************************************************
* 创建一个Customer列表.这个列表实例被命名为 blc.
* 注意: Customer 有这些属性: ID, Name and Rate
******************************************************************/
BindingList<Customer> blc = new BindingList<Customer>();
blc.Add(new Customer(0, "Mr. Zero", 10.0M));
blc.Add(new Customer(1, "Mrs. One", 15.0M));
blc.Add(new Customer(2, "Dr. Two", 20.0M));
/*******************************************************************
* 利用复杂数据绑定将DataGridView绑定到Customer列表
* 绑定(customer业务类型在上面有显示).
******************************************************************/
this.dataGridView1.DataSource = blc;
针对列表的简单数据绑定
简单数据绑定有2种形式:属性对属性的绑定(前面描述过的那种绑定);属性对“一个列表中的某一项的属性”的绑定。属性对“一个列表中的某一项的属性”的绑定形式上和属性对属性的绑定是一样的,除了数据源是一个条目(Item)列表而非单个条目(Item)(比如,是BindingList<Customer>而不是Customer)。当使用简单数据绑定绑定到一个列表时,Windows窗体数据绑定运行时(runtime)j将用户界面元素属性绑定到列表中的某一项上的一个属性。默认情况下,运行时(runtime)将绑定到列表中的第一项(例如,绑定TextBox的Text属性到Customers[0].Name),如果同一个列表被绑定到另外一个使用复杂数据绑定的控件上(正如下面的例子),Windows窗体运行时(runtime)将自动根据复杂数据绑定控件中的当前选中项同步简单数据绑定控件中的属性.,例如,一个开发者可以用简单数据绑定把一个TextBox的Text属性绑定到一个Customer列表,然后他们可以用复杂数据绑定把同一个列表绑定到一个Grid控件。当他们这么做了之后,随着他们在Grid中选择不同的条目,TextBox的Text属性就会自动的重新绑定到Grid的当前选中条目(比如,TextBox将会显示当前选中的Customer的Name)。在Windows窗体中,保持不同的绑定项目的同步性被称作“Currency Management”(字面翻译就是“流通管理”),核心的Windows窗体“Currentcy Management”引擎被一个叫做“CurrencyManager”的类型所实现。
例子:简单数据绑定到一个列表
/*******************************************************************
*设置 (使用 Visual Studio 窗体设计器)
*
*添加一个DataGridVie到窗体Form (dataGridView1)
*添加3个 TextBox到窗体Form(textBox1, textBox2 和textBox3)
*添加下面的代码到Form.Load事件响应方法
******************************************************************/
/*******************************************************************
*创建一个Customer列表.这个列表实例被命名为 blc
*注意: Customer 有这些属性: ID, Name and Rate
******************************************************************/
BindingList<Customer> blc = new BindingList<Customer>();
blc.Add(new Customer(0, "Mr. Zero", 10.0M));
blc.Add(new Customer(1, "Mrs. One", 15.0M));
blc.Add(new Customer(2, "Dr. Two", 20.0M));
/*******************************************************************
*
利用复杂数据绑定将DataGridView绑定到Customer列表
* 绑定(customer业务类型在上面有显示).
******************************************************************/
this.dataGridView1.DataSource = blc;
/*******************************************************************
* 绑定业务对象列表到TextBoxe.这里使用简单数据绑定
* 绑定到一个列表中的某一项的属性上
******************************************************************/
this.textBox1.DataBindings.Add("Text", blc, "ID", true);
this.textBox2.DataBindings.Add("Text", blc, "Name", true);
this.textBox3.DataBindings.Add("Text", blc, "Rate", true);
Windows窗体数据绑定引擎不仅可以保证属性和列表的同步,它还提供一些常用的服务来帮助简化这个处理过程。数据绑定引擎提供以下服务:
类型转换(Type Conversion)
如果需要的话,Windows窗体将执行类型转换作为绑定处理的一部分。例如,如果一个业务对象的整型(int)类型的属性(比如Customer.ID)被绑定到一个控件的字符串(string)类型的属性(比如TextBox.Text)上时,数据绑定运行时将把整型值和字符串值互相转换。Windows窗体利用TypeConverter、IFormattable和IConvertible来执行类型转换。
格式化
Windows窗体绑定支持利用.NET Framework格式化字符串来格式化目标数据。比如,当绑定一个业务对象的Decimal类型的属性(比如Order.Total)到一个控件的字符串类型的属性(比如TextBox.Text),一个格式化字符串(比如“c”)可以被指定,因此这个值可以被显示为本地化的货币显示形式(比如¥1.10),Windows窗体利用IFormattable来进行字符串格式化。
例子:格式化
/*******************************************************************
*设置 (使用 Visual Studio 窗体设计器):
*
*添加 2个TextBoxe 到 Form (textBox1 和textBox2)
*添加下面的代码到Form.Load事件响应方法
******************************************************************/
/*******************************************************************
* 数据源设置
*
* 创建一个叫做Numbers的表,并添加3列
*ID: int
*Name: string
*Cost: Decimal
******************************************************************/
DataTable _dt;
_dt = newDataTable("Numbers");
_dt.Columns.Add("ID", typeof(int));
_dt.Columns.Add("Name", typeof(string));
_dt.Columns.Add("Cost", typeof(decimal));
_dt.Rows.Add(0, "Zero", 10.0M);
_dt.Rows.Add(1, "One", 11.1M);
_dt.Rows.Add(2, "Two", 12.2M);
/*******************************************************************
* 绑定 TextBox.Text (string) 到Numbers.ID (integer)
* 绑定运行时将处理int和string之间的类型转换
******************************************************************/
this.textBox1.DataBindings.Add("Text", _dt, "ID", true);
/*******************************************************************
* 绑定 TextBox.Text (string) 到Numbers.Cost
* 绑定运行时将把值显示成货币形式(¥value.00)
*
* 注意:这里手工创建了 Binding 并将它添加到控件的DataBinding集合
* 而不是使用DataBindings.Add 的重载方法.
******************************************************************/
Binding cb = newBinding("Text", _dt, "Cost", true);
/* .NET Framework 货币格式化字符串 */
cb.FormatString = "c";
/* 添加绑定到TextBox */
this.textBox2.DataBindings.Add(cb);
错误处理
Windows窗体数据绑定整合了Windows窗体ErrorProvider控件。当使用ErrorProvider并且如果一个简单数据绑定操作失败的话,ErrorProvider控件将在用户界面元素上提供一个代表发生错误的可视反馈(一个错误图标)。ErrorProvider控件将查找在绑定期间发生的异常,另外,会在支持IDataErrorInfo接口的数据源上报告IDataErrorInfo信息
例子:错误处理
/*******************************************************************
*设置 (使用 Visual Studio 窗体设计器):
*
*添加2个 TextBoxe到Form (textBox1 和 textBox2)
*添加一个ErrorProvider到 Form (errorProvider1)
*添加下面的代码到Form.Load事件响应方法
******************************************************************/
/*******************************************************************
* 数据源设置:
*
*创建一个叫做Numbers的表,并添加3列
*ID: int
*Name: string
*Cost: Decimal
******************************************************************/
DataTable _dt;
_dt = newDataTable("Numbers");
_dt.Columns.Add("ID", typeof(int));
_dt.Columns.Add("Name", typeof(string));
_dt.Columns.Add("Cost", typeof(decimal));
_dt.Rows.Add(0, "Zero", 10.0M);
_dt.Rows.Add(1, "One", 11.1M);
_dt.Rows.Add(2, "Two", 12.2M);
/*******************************************************************
* 设置 ErrorProvider:
*
* 把它绑定到TextBox使用的同一个数据源上.
******************************************************************/
this.errorProvider1.DataSource = _dt;
/*******************************************************************
*绑定 TextBox.Text (string) 到Numbers.ID (integer)
* 绑定运行时将处理int和string之间的类型转换
******************************************************************/
this.textBox1.DataBindings.Add("Text", _dt, "ID", true);
/*******************************************************************
*绑定 TextBox.Text (string) 到Numbers.Cost
* 绑定运行时将把值显示成货币形式(¥value.00)
*
* 注意:这里手工创建了 Binding 并将它添加到控件的DataBinding集合
* 而不是使用DataBindings.Add 的重载方法
******************************************************************/
Binding cb = newBinding("Text", _dt, "Cost", true);
/* .NET Framework货币格式化字符串*/
cb.FormatString = "c";
/*添加绑定到TextBox */
this.textBox2.DataBindings.Add(cb);
Currency Management 和 BindingContext
Currency Management
Windows窗体数据绑定提供的最重要的服务之一是Currency Management。在Windows窗体数据绑定的上下文中,Currency只是为一个列表维护和同步“当前项”。在Windows窗体中,CurrencyManagement通过一个叫做“CurrencyManager”的类型提供,这个CurrencyManager是一个抽象类“BindingManagerBase”的子类。CurrencyManager提供一组事件,包括“CurrentChanged”和“PositionChanged”,控件和开发者通过这组事件都能够监视到“Currency”中的改变。此外,CurrencyManager还提供一些属性像“Position”和“Current”,来允许控件和开发者设置和取回当前项(位置)。Windows窗体数据绑定的一个关键方面是,Currency不是被像DataGrid这样的控件所维护,而是由被多个控件共享的一个CurrencyManager的一个实例来维护。
Binding Context
当使用简单列表绑定(就是上面说过的“针对列表的简单数据绑定”)的时候,简单数据绑定的控件需要同步它的数据源中的“当前项”。为了做到这点,绑定需要得到它关联的CurrencyManager的“当前”项。绑定通过它的控件的“BindingContext”属性得到它的数据源的CurrencyManager,一个控件典型的是从它的父窗体中得到它的BindingContext。“BindingContext”是一个单窗体的CurrencyManager缓存,更确切地说,BindingContext是一个BindingManagerBase实例的缓存,而BindingManagerBase时CurrencyManager的基类。
举个例子,假设一个开发者有一个Customer列表,绑定到一个DataGrid控件(DataGrid.DataSource设置到一个Customer列表)。此外,开发者还有一个简单控件,比如一个TextBox,绑定到同一个列表(TextBox利用简单列表绑定把它的Text绑定到Customer列表中的Name属性),当在DataGrid中点击一项时,DataGrid将使被点击的那想成为当前选中项。DataGrid怎么做到的呢?DataGrid首先访问它的BindingContext属性获取它的数据源(列表)的BindingManagerBase(CurrencyManager),“BindingContext”返回缓存的BindingManagerBase(如果不存在的话,会创建一个),然后DataGrid会使用BindingManagerBase的API来改变“当前”项(它通过设置CurrencyManager的Position属性来达到目的)。当一个简单数据绑定被构造出来之后,控件将得到与它的数据源关联的BindingManagerBase(CurrencyManager),它将监听BindingManagerBase上的change事件,并且在BindingManagerBase的“当前项”改变时同步更新它的绑定属性(比如Text属性)。正确的同步的关键是DataGrid和TextBox两者必须要使用相同的Curr