1.引言

BindingSource组件是数据源和控件间的一座桥,同时提供了大量的API和Event供我们使用。使用这些API我们可以将Code与各种具体类型数据源进行解耦;使用这些Event我们可以洞察数据的变化。
2.简单绑定

在给gridview绑定好数据源以后,建议直接操作bindingSource来做一些处理,例如添加、删除等操作(add、romove),这样操作会直接操作bindingSource的list集合,注意:不建议使用bindingSource.datasouce =  A 这样重新绑定数据源的操作,因为这样很有可能会出现bindingSource的datasouce里面有数据,但是bindingSource的list没有数据,这时候即时重新刷新,界面上也可能会出现不显示数据的情况,(如果刷新数据无作用的话可以使用bindingSource.Accept()把状态设置为unchange状态)。建议,以后使用bindingSource进行相关操作,不建议使用bindingSource.datasouce=A这样的重新绑定数据源的操作。

DataTable myTable = myTableAdapter.GetData();//创建Table

BindingSource myBindingSource= new BindingSource();//创建BindingSource

DataGridView myGrid = new DataGridView();//创建GridView

myGrid.DataSource = myBindingSource;//将BindingSource绑定到GridView

myTable;//绑定数据到BindingSource

注:

1)绑定到DataTable,其实是绑定到DataTable提供的DataView上。每个DataTable都有一个缺省的DataView

2)DataView是绑定的实质,正如其名,它是DataTable的数据的展现。因此可以对同一个DataTable

,构建多个DataView,进而可以对这同样的数据实施不同的过滤、排序等方法,从不同侧面展示DataTable。这也体现了一定的MVC思想。

3)BindingSouce也可作为数据(其实是数据引用)的容器在不同窗体间传递,从而实现在弹出窗体中对数据的编辑

3.主细表

image

以上图所示数据为例:

1)DataSet:myDataSet

2)DataTable:ParentTable、ChildTable、GrandChildTable

3)Relation:FK_Parent_Child、FK_Child_GrandChild

//绑定父数据

parentBindingSource.DataSource = myDataSet;

parentBindingSource.DataMember = "ParentTable";

m_GrandParentGrid.DataSource = m_GrandParentBindingSource;

//绑定子数据。
    childBindingSource.DataSource = parentBindingSource;//绑定到“父BindingSource”,而不是父Table

childBindingSource.DataMember = "FK_Child_GrandChild";//绑定到“父-子Relation”

//绑定孙子数据。
    grandChildBindingSource.DataSource = childBindingSource;//绑定到“子BindingSource”

grandChildBindingSource.DataMember = "FK_Child_GrandChild";//绑定到“子-孙Relation”

这样你就可以在Form上摆上3个DataView,分布绑定到这3个BindingSouce,很容易就实现了主细表关联展现。
4.数据操纵
要操纵数据,首先需要获取当前数据项。BindingSource的Current属性返回DataRowView类型的对象(就像DataView是对 DataTable的封装一样,DataRowView是对DataRow的封装),它是对当前数据项的封装,可以通过类型转换变成你想要的对象。

custRow currentRowView =(custRow)myBindingSource.Current;//获取当前RowView

CustomersRow custRow = currentRowView.Row as CustomersRow;//类型转换为当前数据项

string company = custRow.CompanyName;//使用当前数据项

string phoneNo = custRow.Phone;

5.用BindingSource做数据容器

BindingSource还可以用作数据容器,即便它没有绑定到数据源上,它内部有一个可以容纳数据的list。
5.1Add方法

调用Add方法会在BindingSource的list中插入数据项。如果这时第一次插入数据,并且没有绑定数据,那么插入数据的类型就决定了今后此list中数据的类型。

注:

1)此时再插入其它类型对象会抛出InvalidOperationException异常

2)设置DataSource属性时会刷新list,造成Add方法添加到list中的数据丢失

5.2AddNew方法

AddNew方法返回BindingSourc所容纳数据类型的对象;如果之前没有容纳数据,则会返回Object对象。

AddNew方法会调用EndEdit方法,并将提交对当前数据的操纵;然后新数据项就成为当前项。

AddNew方法会引发AddingNew事件,可以在此事件中为数据项赋值,或者创建新数据项

private void OnAddingNew(object sender, AddingNewEventArgs e)
    {
          e.NewObject = new MyCustomObject();//
    }

6.用BindingSource对数据排序、过滤、搜索
6.1 Sort

为Sort属性赋上Sort表达式,可以对数据进行排序

myBindingSource.Sort = "ContactName ASC";//对ContanctName列按ASC进行排序

myBindingSource.Sort = "Region ASC, CompanyName DESC"//先按Region、再按CompanyName排序

6.2 Find

Find方法根据指定属性和关键字进行查找,并返回第一个匹配对象的Index
    int index = m_CustomersBindingSource.Find("CompanyName",IBM);//按CompanyName查找IBM
    if (index != -1)
    {
        myBindingSource.Position = index;//定位BindingSource
    }

6.3 Filter

为Filter属性赋上表达式,可以对数据进行过滤

m_CustomersBindingSource.Filter = "Country = 'Germany'";//过滤出Country属性为Germany的数据

7.用Event监控数据
7.1 Event

1)AddingNew

调用AddNew()方法时触发。

2)BindingComplete

当控件完成数据绑定时触发,说明控件已经从数据源中读取当前数据项的值。当BindingSource重新绑定或当前数据项改变时,会触发此事件

注:

* 当有多个控件绑定到同一数据源时,这个事件会触发多次

3)CurrrentChanged

当前数据项改变时触发此事件。触发此事件的情况如下

* Position属性改变时
        * 添加、删除数据时
        * DataSource或DataMember属性改变时

4)CurrentItemChanged

当前数据项的值改变时触发

5)DataError

通常输入无效数据时,由CurrencyManage抛出异常,从而触发此事件。

6)PositionChanged

Position属性改变时触发此事件。

7)ListChanged

数据集合改变时触发。触发此事件的情况如下

* adding, editing, deleting, 或 moving 数据项时

改变那些会影响List行为特征的属性时,如AllowEdit属性

* 替换List时(绑到新数据源)

8.限制数据修改

BindingSource不仅是数据源与控件间的“桥梁”,同时也是数据源的“看门人”。通过BindingSource,我们可以控制对数据的修改。

BinidingSource的AllowEdit, AllowNew和AllowRemove属性可以控制客户端代码和控件对数据的修改
9.复杂数据类型的Binding

对于String类型的数据,直接Binding到Text控件即可,对于复杂类型有下面几种情况

* 对于DateTime、Image等类型的数据,它们存储的格式与显示要求并不一致。
    * 有时,你并不想显示客户ID,而是希望显示客户名称
    * 数据库中的Null值

9.1 Binding类

解决以上问题的关键是要理解Binding类,了解它是如何控制数据Binding的过程。

DataTable table = customersDataSet.Customers;

//将TextBox的Text属性Binding到table的CustomerID列
    customerIDTextBox.DataBindings.Add("Text", table,"CustomerID", true);

//上面一行代码等同下面两行代码

Binding customerIDBinding = new Binding("Text", table,"CustomerID", true);
    customerIDTextBox.DataBindings.Add(customerIDBinding);

从代码可以看出,Binding是数据源(table)和控件(customerIDTextBox)间的中介人,它有以下功能

* 从数据源取数据,并按照控件要求的数据类型对此数据进行格式化(Formatting),然后传给控件
    * 从控件取数据,并按照数据源的数据类型要求对此数据进行解析(Parsing),然后返回给数据源
    * 自动对数据进行格式转换

9.2Binding类构造函数和属性

Binding构造函数有多个重载版本,下面介绍其重要的参数,这些参数同时存在于Binding对象的属性中。下面介绍中,参数名和属性名都列出来

1)formattingEnabled(属性FormattingEnabled)

o true,Binding对象自动在数据源类型和控件要求的类型间进行转换
          o false,反之

2)dataSourceUpdateMode

决定控件上数值的改变在何时提交回数据源

3)nullValue

DBNull、 null和Nullab<T>对应的值。

4)formatString

格式转换

5)formatInfo

一个实现IFormatProvider接口的对象引用,用来自定义格式转换

要了解类型如何转换的,请学习Type Conversions and Format Providers相关内容。关于上面属性的应用,请看下面介绍
9.3基于Binding类的内置机制(属性、参数)进行类型转换

通过Binding类构造时的参数,或属性设置,可以控制它进行类型转换的机制。

1)DateTime

下面先介绍一个DateTime类型的例子,使用DateTimePicker控件

//创建Binding,设置formattingEnabled为true

birthDateTimePicker.DataBindings.Add("Value",m_EmployeesBindingSource, "BirthDate", true);

//设定为使用自定义格式
    birthDateTimePicker.Format = DateTimePickerFormat.Custom;

//设定格式
    birthDateTimePicker.CustomFormat = "MM/dd/yyyy";

2)Numeric

salaryTextBox.DataBindings.Add("Text", employeesBindingSource,"Salary", true,  DataSourceUpdateMode.OnValidation,"<not specified>", "#.00");

以上代码做了以下处理

* 设定formattingEnabled为true:代表自动类型转换
        * 设定DataSourceUpdateMode为OnValidation:
        * 设定nullValue为"<not specified>":这些DBNull就显示为,"<not specified>", 同时用户录入,"<not specified>"时,数据值为DBNull
        * 设定formatString为"#.00":数值保留2位小数

9.4. 事件

下面介绍Binding的主要事件,以及如何基于这些事件进行类型转换的控制。

主要事件:

1)Format事件

发生在从数据源获取数据后,控件显示此数据之前。在这个事件里将数据源的数据类型转换为控件要求的数据类型。

2)Parse事件

与Event相反。它发生控件值改变后,数据更新回数据源之前。在这个事件里将控件的数据类型转换为数据源要求的数据类型。

这两个事件为我们控制数据提供了机制,它们都声明为ConvertEventHandler类型,

void ConvertEventHandler(object sender, ConvertEventArgs e);

有两个参数,第二个参数ConvertEventArgs e 提供了我们要formatting和parsing的数据。它有两个属性

* e.DesiredType是数值要转换的目标类型
    * e.Value是要转换的数值。我们可以替换此Value

9.5. 基于事件的类型转换
9.5.1 处理Format Event

void OnCountryFromFormat(object sender, ConvertEventArgs e)
    {
        if (e.Value == null || e.Value == DBNull.Value)
        {
             pictureBox.Image = null;
             return;
        }

//绑定的是数据源的CountryID字段,因此e.Value返回的ID号,通过此ID号取得对应数据行
        CountriesRow countryRow =    GetCountryRow((int)e.Value);

//将e.Value赋值为CountryName,从而在控件中显示名称
         e.Value = countryRow.CountryName;
        // 数据转换

ImageConverter converter = new ImageConverter();
        pictureBox.Image =    converter.ConvertFrom(countryRow.Flag) as Image;
    }

9.5.2 处理Format Event

void OnCountryFromParse(object sender, ConvertEventArgs e)
{
// Need to look up the Country information for the country name
ExchangeRatesDataSet.CountriesRow row =
GetCountryRow(e.Value.ToString());
if (row == null)
{
string error = "Country not found";
m_ErrorProvider.SetError(m_CountryFromTextBox, error);
m_CountryFromTextBox.Focus();
throw new ArgumentException(error);
}
e.Value = row.CountryID;
}
完成数据编辑

经常会遇到这种情况,你在一个控件中录入或选择一些数据,只有当年离开此控件时,关联的数据才能同步更新。这个问题是由DataRow内部机制决定的。

DataRowView类实现IEditableObject接口,支持对象的事务性编辑(当你确认完成编辑前,可以回滚数据)。我们通过BeginEdit()方法来开始数据编辑,通过EndEdit()方法提交编辑。

不要将DataRowView的EndEdit()与DataSet、DataTable、DataRow的AcceptChanges()方法混淆。 DataRow有original和current版本,同时IEditableObject的caching机制让它有transient版本,在调用 EndEdit()方法前,数据修改是不会提交到数据源。这就是前面问题的内在原因。

如果希望编辑的数据立即提交,那调用 EndEdit()函数的最佳位置就是Validated事件。Validate事件在控件录入的数据parsed,并且通过validate后触发,在这个事件中触发EndEdit()就会通知绑定到同一数据源的所有控件,从而实现数据同步更新。

private void OnCountryTextValidated(object sender, EventArgs e)
    {
              exchangeRatesBindingSource.EndEdit();
    }

当然,当前数据项改变时,也会触发EndEdit()事件
使用AutoComplete

当你希望TexbBox或ComboBox中会自动提示功能,那你应该学习一下AutoComplete功能。下面以TextBox为例介绍相关步骤

1)设定TextBox的AutoCompleteSource属性:FileSystem, HistoryList, RecentlyUsedList

2)如果希望使用自定义的列表,则设定AutoCompleteSource属性为CustomSource

3)设定AutoCompleteMode为SuggestAppend。这意味着你输入部分字符时,控件在下拉列表中提示所有相近的数据

4)如果不想使用内置的提示源,你可以自己创建一个AutoCompleteStringCollection类的列表,

5)创建这个列表后,将它赋给TextBox的AutoCompleteCustomSourc属性
DataBinding的生命周期

BindingSource的DataSourceUpdateMode属性是关键,它有以下三种可能值,下面分布以TextBox控件为例介绍此属性不同时DataBinding的生命周期

1)OnValidating(缺省值)

* DataBinding的生命周期:

TextBox.Leave, TextBox.Validating, Binding.Parse, TextBox.Validated

* 此时若将控件的CausesValidation属性设为false,那么Validating事件就不会发生

2)OnPropertyChanged

* DataBinding的生命周期:

此时,每次控件值发生改变时都会触发Binding.Parse。对TextBox控件来说,每次录入字符都会触发Binding.Parse。

3)Never

此时Parse事件不会触发,也就是说控件将成为只读的。
子父绑定

前面介绍了主细绑定,它其实是一个父子绑定。有时我们希望由子到父的关联绑定,下面我们就一起来实现这个机制。实现这个机制的关键还是Event,这个Event就是BindingSource的CurrentChanged事件

private void OnCurrentChanged(object sender, EventArgs e)
          {
             // 获取当前的子DataRow
             ExchangeRatesDataSet.ExchangeRatesRow currentRow =
                (ExchangeRatesDataSet.ExchangeRatesRow)
                ((DataRowView)m_ExchangeRatesBindingSource.Current).Row;

// 获取关联的父DataRow
             ExchangeRatesDataSet.CountriesRow fromCountryRow =
                currentRow.CountriesRowByFK_ExchangeRates_CountriesFrom;
             ExchangeRatesDataSet.CountriesRow toCountryRow =
                currentRow.CountriesRowByFK_ExchangeRates_CountriesTo;

//显示父DataRow的信息

if (fromCountryRow != null && toCountryRow != null)
             {
                m_FromCountryCombo.SelectedValue = fromCountryRow.CountryID;
                m_ToCountryCombo.SelectedValue = toCountryRow.CountryID;
             }

}
绑定到数据的多个复本

有 时,我们希望以不同角度看到同一数据,这时需要绑定到同一数据的多个复本。这里的关键是CurrencyManager类,每个 BindingSource管理着一个CurrencyManager。如果多个控件绑定到同一个BindingSource,那么只有一个 CurrencyManager,因此也就只有一个CurrentItem,这样就造成这些绑定到同一BindingSource的控件同步刷新。要解决这个问题,我们需要多个CurrencyManager,也就是说我们可以创建多个BindingSource,且绑定到同一个数据源。

9.5 处理Null类型

这里有两个概念要弄清楚,.Net内置的Null类型与代表数据库中的Null类型,以及它们的区别。

1).Net内置的Null类型

* Nullable,引用类型
        * Nuallable<T>,值类型

2).Net用来代表数据库中的Null类型

* DBNull,它有一个属性Value,可以用来判断数据是否为DBNull

if (northwindDataSet.Employees[0].Country == DBNull.Value)
             {
                     // Handle null case here
              }

对强类型数据集

if (northwindDataSet.Employees[0].IsCountryNull())
    {
    // Handle null case here
    }

1)AddNew()函数:用来添加一条数据,返回类型由绑定的DataSource决定。

1)绑定到DataSet/DataTable时,返回DataRowView对象。

注意:

a)返回的不是DataSet或DataTable或DataRow。

b)如果希望获取添加的数据,需要进行类型转换

//bs为你创建的BindingSource

DataRow row=(DataRow)((DataRowView) bs.AddNew()).Row;

c)使用TypedDataSet时,转换方法与上面类似,只是用TypedDataRow而已

//MyDataRow为你定义的TypedDataRow

MyDataRow row=(MyDataRow)((DataRowView) bs.AddNew()).Row;

C#BindingSource用法、属性、事件相关推荐

  1. vue、Affix 固钉、Affix 属性事件、vue Affix 全部固钉、vue Affix 全部属性事件

    vue.Affix 固钉.Affix 属性事件.vue Affix 全部固钉.vue Affix 全部属性事件 设计规则 何时使用 代码演示 1.基本 2.固定状态改变的回调 3.滚动容器 API 事 ...

  2. 获取对象的接口信息(方法/属性/事件)(VB6代码)

    要不是WS的超级绿豆提起这东西,我还不知道呢.... 真是个好玩意!!能列出一个对象的接口,包括方法,属性,事件.....微软直接做了啊. 记录一下,以后要用到时免得忘了: Option Explic ...

  3. vue 所有按钮属性、vue Button 所有按钮属性事件、vue a-button 所有按钮属性事件、vue 按钮所有属性事件、vue

    vue 所有按钮属性.vue Button 所有按钮属性事件.vue a-button 所有按钮属性事件.vue 按钮所有属性事件.vue 1.组件注册 1.按钮类型 2.按钮组合 3.不可用状态 4 ...

  4. vue、Cascader 级联选择、Cascader 属性事件方法、vue Cascader 所有级联选择样式、vue Cascader 级联选择全部属性事件方法

    vue.Cascader 级联选择.Cascader 属性事件方法.vue Cascader 所有级联选择样式.vue Cascader 级联选择全部属性事件方法 Cascader 级联选择 何时使用 ...

  5. vue、Dropdown 下拉菜单、Dropdown属性事件、vue Dropdown 全部下拉菜单、vue Dropdown 全部属性事件

    vue.Dropdown 下拉菜单.Dropdown属性事件.vue Dropdown 全部下拉菜单.vue Dropdown 全部属性事件 设计规则 何时使用 代码演示 1.基本 2.右键菜单 3. ...

  6. vue、Menu 导航菜单、Menu属性事件、vue Menu 全部导航菜单、vue Menu 全部属性事件

    vue.Menu 导航菜单.Menu属性事件.vue Menu 全部导航菜单.vue Menu 全部属性事件 设计规则 何时使用 代码演示 顶部导航 内嵌菜单 缩起内嵌菜单 只展开当前父级菜单 垂直菜 ...

  7. vue、Layout 布局、Layout 属性事件、vue Layout 全部布局、vue Layout 全部属性事件

    vue.Layout 布局.Layout 属性事件.vue Layout 全部布局.vue Layout 全部属性事件 设计规则 尺寸 交互 视觉 组件概述 代码演示 1.基本结构 2.自定义触发器 ...

  8. 【VUE】基础用法(属性与事件的绑定,条件渲染等)

    ✍️ 作者简介: 前端新手学习中.

  9. Vue 模板语法 插值操作 绑定属性 计算属性 事件监听 条件判断 循环遍历 阶段案例

    1 插值操作 1.1 Mustache语法 也就是双大括号 {{ }} <div id="app"> <!-- mustche语法中,不仅可以直接写变量,也可以写 ...

最新文章

  1. linux 用户java_linux之用户管理
  2. ApplicationId 与 PackageName 的区别
  3. DOS 和 Linux 常用命令的对比
  4. 神秘的subsys_initcall【转】
  5. mysql第一二章笔记_MYSQL必知必会读书笔记 第一章(基础)
  6. nlp中的经典深度学习模型(一)
  7. 杭电1466计算直线的交点数
  8. 都2021年了,不会还有人连深度学习都不了解吧(七)- 评估指标篇
  9. 网络流之dinic算法
  10. 若依微服务版 数据库设计说明书
  11. PHP第三方短信接口接入
  12. python爬去淘宝客订单_Python 应用淘宝客API接口简单获取优惠券的实现
  13. mysql题 以下1-7_mysql练习题
  14. 无线通信与生活-(1)
  15. java 锟斤 解决乱码_java eclipse 开发中文乱码锟斤拷小锟斤拷锟
  16. c++11后面引入的新特性(三)
  17. mysql状态表 历史记录设计表_常见数据库设计(2)——历史数据问题之单记录变更...
  18. php 对象教程,创建一个简单的PHP对象_PHP教程
  19. 图像变形算法:实现Photoshop液化工具箱中向前变形工具
  20. 深度理解RNN的梯度消失和LSTM为什么能解决梯度消失

热门文章

  1. tensorflow训练打游戏ai
  2. 《Adobe Premiere Pro CS5经典教程》——2.9 用Media Browser查找素材
  3. maven 安装下载与配置 代理设置 《解决下载慢问题》
  4. .Net QQ互联教程
  5. 常用String方法
  6. 前端见微知著JavaScript基础篇:你所不知道的apply, call 和 bind
  7. 网页html文档头部声明的两种常用模式
  8. 一个十分简短的Tween
  9. 如何在您的笔记本上搭建View 演示环境 -5.配置View Connection Server
  10. 三甲医院副主任博士医师,工资竟然不如程序员老公的一半!