什么是数据绑定(译)[原创]

从一个 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 = new Customer (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 = new DataTable ( "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 = new Binding ( "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 = new DataTable ( "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 = new Binding ( "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 窗体中, Currency Management 通过一个叫做“ 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 两者必须要使用相同的 CurrencyManager(BindingManagerBase) 。如果他们使用的是不同的 CurrencyManager ,那么当 DataGrid 中的条目发生改变时简单绑定的属性将无法正确更新。
正如前面提到的,控件(和开发者)能够利用一个控件的 BindingContext 属性得到一个与数据源关联的 BindingManagerBase 。当从 BindingContext 请求一个 BindingManagerBase 的时候, BindingContext 将首先查到它的缓存看是否有被请求的 BindingManagerBase 。如果缓存中不存在这个 BindingManagerBase BindingContext 将创建并返回一个新的(并且把它加入缓存)。 BindingContext 典型情况下对于每个窗体是全局的(子控件的代理他们父窗体的 BindingContext ),因此典型情况下 BindingManagerBase(CurrencyManagers) 被一个窗体上所有的控件所共享。 BindingContext 2 种形式:
/* 获取一个给定数据源的 BindingManagerBase */
bmb = this.BindingContext[dataTable];
 
/* 获取给定数据源和数据成员的 BindingManagerBase */
bmb = this.BindingContext[dataSet, "Numbers"];
第一种形式通常用来获取一个像 ADO.NET DataTable 这样的列表的相关的 BindingManagerBase 。第二种形式用来获取含有子列表的父级数据源(比如含有子 DataTable DataSet )的 BindingManagerBase BindingContext 最令人不解的方面之一是使用不同的形式来指定同一个数据源,将导致两个不同的 BindingManagerBase 实例。迄今为止,这是控件绑定到相同的数据源但却没有同步的最常见原因(控件通过 BindingContext 来获取一个 BindingManagerBase
例子:控件同步
/* 创建含有一个 DataTable DataSet */
DataSet      dataSet = new DataSet();
DataTable    dataTable = dataSet.Tables.Add("Numbers");
 
dataTable.Columns.Add("ID", typeof(int));
dataTable.Columns.Add("Name", typeof(string));
 
dataTable.Rows.Add(0, "Zero");
dataTable.Rows.Add(1, "One");
 
/*******************************************************************
* 绑定第一个 DataGridView TextBox dataSet 中的 ”Numbers”
* 这个 DataGridView 将使用 BindingContext 得到一个数据源的 CurrencyManager
* DataGridView1 将使用以下形式的 BindingContext
*
*   bmb = BindingContext[dataSet, “Numbers”];
*
* textBox1 的文本绑定也将得到一个 BindingManagerBase, 并且使用如下的 BindingContext 形式
*
*   bmb = BindingContext[dataSet, “Number”];
*
* 因此 dataGridView1 textBox1 将共享同一个 BindingManagerBase (CurrencyManager)
*
*******************************************************************/
 
this .dataGridView1.DataSource = dataSet;
this .dataGridView1.DataMember = "Numbers";
 
this .textBox1.DataBindings.Add("Text", dataSet, "Numbers.Name", true);
 
/*******************************************************************
* 变量 “dataTable” 也指向这个 “Numbers” 虽然
* 上面的 DataGridView TextBox 利用 “DataSource” “DataMember” 形式
* 绑定到表 , 现在也可以绑定到相同的表(和数据),通过直接绑定到 “dataTable” ,正如下面所示
* 当这么做的时候 , DataGridView2 将使用以下形式的 BindingContext
*
*   bmb = BindingContext[dataTable];
*
* textBox12 的文本绑定也将使用如下的 BindingContext 形式
*
*   bmb = BindingContext[dataTable];
*
* 因此 dataGridView2 textBox2 将共享相同的 BindingManagerBase (CurrencyManager)
* 然而他们将不会和 dataGridView1 textBox1 共享相同的 CurrencyManager, 因为他们使用不同的形式
* 来获取他们的绑定 .
*******************************************************************/
 
this .dataGridView2.DataSource = dataTable;
this .textBox2.DataBindings.Add("Text", dataTable, "Name", true);
 
控制绑定操作
Windows 窗体简单绑定类型( System.Windows.Forms.Binding )允许开发者控制在用户界面元素内容发生改变时怎样和何时更新绑定的数据源属性,以及在数据源属性发生变化时怎样和何时更新用户界面元素。例如,如果一个开发者把 Customer 实例的 Name 属性绑定到一个 TextBox 控件的 Text 属性上,开发者可以指定何时把用户界面元素发生的更改“传播”到数据源。当前支持的选项是:在 TextBox 的验证 (validation) 期间(当用户将焦点移出 TextBox 时——这是默认值);当文本值发生任何改变时;以及永不更新数据源。开发者也能够控制何时把数据源的更改更新到绑定到的用户界面元素。当前支持的选项的是:当数据源属性改变时(默认值) 永不更新。注意,开发者可以组合使用“永不更新”和调用绑定 API ReadValue/WriteValue )来提供他们自己的显示的数据源与用户界面元素间的数据同步规则。
例子:显示的绑定
/*******************************************************************
* 设置 (使用 Visual Studio 窗体设计器):
*
* 添加3个TextBoxe到窗体Form (textBox1, textBox2 and textBox2)
* 添加1个 ErrorProvider 到窗体Form (errorProvider1)
* 添加一个按钮Button到窗体Form (button1)
* 添加下面的代码到Form.Load 事件处理方法
******************************************************************/
 
/*******************************************************************
* 数据源设置:
*
* 创建一个叫做 Numbers的表并添加3列
* ID:     int
* Name:   string
* Cost:   Decimal
******************************************************************/
 
DataTable _dt;
 
_dt = new DataTable ( "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)
*
* 设置DataSourceUpdateMode 为 OnPropertyChanged. 这将
* 导致一旦TextBox.Text发生改变,就会立即更新数据源属性
*
* 注意, 在这种模式下ErrorProvider将立刻显示一个错误,而不是在用户把焦点
* 移出TextBox之后才只显示一个错误
******************************************************************/
        
this .textBox1.DataBindings.Add( "Text" , _dt, "ID" , true ,
                               DataSourceUpdateMode .OnPropertyChanged);
 
/*******************************************************************
* 绑定 TextBox.Text 到 Form.Size (在这个场景下, 窗体Form 是数据源)
* 不要让控件的改变更新到数据源(窗体Form)
******************************************************************/
 
Binding sizeBinding = new Binding ( "Text" , this , "Size" , true ,
                            DataSourceUpdateMode .Never);
this .textBox2.DataBindings.Add(sizeBinding);
 
/*******************************************************************
* 设置Button.Click 显示的更新数据源(Form.Size).
*
* 使用匿名代理让代码更简洁
******************************************************************/
  
this .button1.Click += delegate ( object button, EventArgs args)
{
sizeBinding.WriteValue();
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值