广州培训 网站开发,怎么创业做电商,iis6 建设网站浏览,企业网站源码带支付简介# 在C#中提起控件绑定数据#xff0c;大部分人首先想到的是WPF#xff0c;其实Winform也支持控件和数据的绑定。 Winform中的数据绑定按控件类型可以分为以下几种#xff1a; 简单控件绑定列表控件绑定表格控件绑定 绑定基类# 绑定数据类必须实现INotifyPropertyChanged… 简介# 在C#中提起控件绑定数据大部分人首先想到的是WPF其实Winform也支持控件和数据的绑定。 Winform中的数据绑定按控件类型可以分为以下几种 简单控件绑定列表控件绑定表格控件绑定 绑定基类# 绑定数据类必须实现INotifyPropertyChanged接口否则数据类属性的变更无法实时刷新到界面但可以从界面刷新到类。 为了方便我们设计一个绑定基类 /// summary/// 数据绑定基类/// /summarypublic abstract class BindableBase : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;protected bool SetPropertyT(ref T field, T newValue, [CallerMemberName] string propertyName null){ if (!EqualityComparerT.Default.Equals(field, newValue)){field newValue;PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); return true;} return false;}} 需要绑定的数据类继承绑定基类即可 /// summary/// 数据类/// /summarypublic class Data : BindableBase{private int id 0;private string name string.Empty; public int ID { get id; set SetProperty(ref id, value); }public string Name { get name; set SetProperty(ref name, value); }} 功能扩展# 主要为绑定基类扩展了以下两个功能 获取属性的Description特性内容从指定类加载属性值对象直接赋值是赋值的引用控件绑定的数据源还是之前的对象 这两个功能不属于绑定基类的必要功能但可以为绑定提供方便所以单独放在扩展方法类里面。 代码如下 /// summary/// 数据绑定基类的扩展方法/// /summarypublic static class BindableBaseExtension{ /// summary /// 获取属性的描述返回元组格式为 Item1:描述信息 Item2:属性名称 /// /summary /// param nametype/param /// returns/returnspublic static Tuplestring, string[] GetDescription(this BindableBase bindData){var proAry bindData.GetType().GetProperties();var desAry new Tuplestring, string[proAry.Length]; string desStr; for (int i 0; i proAry.Length; i){var attrs (DescriptionAttribute[])proAry[i].GetCustomAttributes(typeof(DescriptionAttribute), false);desStr proAry[i].Name;foreach (DescriptionAttribute attr in attrs){desStr attr.Description;}desAry[i] Tuple.Create(desStr, proAry[i].Name);} return desAry;} /// summary /// 加载同类型指定对象的属性值如果当前属性值或目标属性值为null则不执行赋值操作 /// /summary /// param namedata/parampublic static void Load(this BindableBase source, BindableBase dest){ if (source null || dest null){ //不执行操作 return;} Type type source.GetType(); if (type ! dest.GetType()){throw new ArgumentNullException(参数类型不一致);}var proAry type.GetProperties(); for (int i 0; i proAry.Length; i){var proType proAry[i].PropertyType; if (proType.IsSubclassOf(typeof(BindableBase))){ //检测到内部嵌套的绑定基类建议不处理直接跳过这种情况应该单独处理内嵌对象的数据加载 //var childData (BindableBase)(proAry[i].GetValue(source)); //childData.Load((BindableBase)(proAry[i].GetValue(dest)));} else{proAry[i].SetValue(source, proAry[i].GetValue(dest));}}}} 简单控件绑定# 简单属性绑定是指某对象属性值和某控件属性值之间的简单绑定需要了解以下内容 Control.DataBindings 属性代表控件的数据绑定的集合。Binding 类代表某对象属性值和某控件属性值之间的简单绑定。 使用方法如下 Data data new Data() { ID1,Nametest};//常规绑定方法 textBox1.DataBindings.Add(Text, data, ID);//使用这种方式避免硬编码textBox2.DataBindings.Add(Text, data, nameof(data.Name)); 注这种绑定会自动处理字符串到数据的类型转换转换失败会自动恢复原值。 列表控件绑定# 列表控件绑定主要用于 ListBox 与 ComboBox 控件它们都属于 ListControl 类的派生类。ListControl 类为 ListBox 类和 ComboBox 类提供一个共同的成员实现方法。 注CheckedListBox 类派生于 ListBox 类不再单独说明。 使用列表控件绑定前需要了解以下内容 ListControl.DataSource 属性获取或设置此 ListControl 的数据源值为实现 IList 或 IListSource 接口的对象如 DataSet 或 Array。 ListControl.DisplayMember 属性获取或设置要为此 ListControl 显示的属性指定 DataSource 属性指定的集合中包含的对象属性的名称默认值为空字符串()。 ListControl.ValueMember 属性获取或设置属性的路径它将用作 ListControl 中的项的实际值表示 DataSource 属性值的单个属性名称或解析为最终数据绑定对象的属性名、单个属性名或句点分隔的属性名层次结构, 默认值为空字符串()。 注最终的选中值只能通过ListControl.SelectedValue 属性获取目前还没找到可以绑定到数据的方法。 绑定BindingList集合# BindingList是一个可用来创建双向数据绑定机制的泛型集合使用方法如下 BindingListData list new BindingListData();list.Add(new Data() { ID 1, Name name1 });list.Add(new Data() { ID 2, Name name2 }); comboBox1.DataSource list;comboBox1.ValueMember ID;comboBox1.DisplayMember Name; 注如果使用List泛型集合则不支持双向绑定。同理如果Data没有继承绑定基类则属性值的变更也不会实时更新到界面。 绑定DataTable表格# DataTable支持双向绑定使用方法如下 DataTable dt new DataTable();DataColumn[] dcAry new DataColumn[] { new DataColumn(ID),new DataColumn(Name) };dt.Columns.AddRange(dcAry);dt.Rows.Add(1, name1Dt);dt.Rows.Add(2, name2Dt); comboBox1.DataSource dt;comboBox1.ValueMember ID;comboBox1.DisplayMember Name; 绑定BindingSource源# BindingSource 类封装窗体的数据源旨在简化将控件绑定到基础数据源的过程详细内容可查看 BindingSource 组件概述。 有时候数据类型可能没有实现INotifyPropertyChanged接口并且这个数据类型我们还修改不了这种情况就只能使用BindingSource来将控件绑定到数据了。 假设Data类没有继承BindableBase绑定方法如下 ListData list new ListData(); list.Add(new Data() { ID 1, Name name1 });list.Add(new Data() { ID 2, Name name2 }); BindingSource bs new BindingSource();bs.DataSource list;comboBox1.DataSource bs;comboBox1.ValueMember ID;comboBox1.DisplayMember Name; 关键是下面的步骤改变集合内容时手动触发变更 //单项数据变更list[0].Name test;bs.ResetItem(0); //添加数据项list.Add(new Data() { ID 3, Name name3 });bs.ResetBindings(false);//在BindingSource上添加或使用BindingList列表则可以不用手动触发变更通知bs.Add(new Data() { ID 4, Name name4 }); 表格控件绑定# 绑定DataTable# 方法如下 DataColumn c1 new DataColumn(ID, typeof(string));DataColumn c2 new DataColumn(名称, typeof(string));dt.Columns.Add(c1);dt.Columns.Add(c2);dt.Rows.Add(11, 22); //禁止添加行防止显示空白行dataGridView1.AllowUserToAddRows false;//选择是否自动创建列dataGridView1.AutoGenerateColumns true; dataGridView1.DataSource dt.DefaultView; 绑定BindingList# 方法如下 //填充数据BindingListData dataList new BindingListData(); for (int i 0; i 5; i){dataList.Add(new Data() { ID i, Name Name i.ToString() });} //禁止添加行防止显示空白行dataGridView1.AllowUserToAddRows false; //选择是否自动创建列dataGridView1.AutoGenerateColumns false; //手动创建列var desAry dataList[0].GetDescription();int idx 0;foreach (var des in desAry){idx dataGridView1.Columns.Add($column{idx}, des.Item1); // 手动添加某列dataGridView1.Columns[idx].DataPropertyName des.Item2; // 设置为某列的字段} //绑定集合dataGridView1.DataSource dataList; //集合变更事件dataList.ListChanged DataList_ListChanged; 注上面的GetDescription()是绑定基类的扩展方法。 BindingList提供集合的变更通知Data通过继承绑定基类提供属性值的变更通知。 UI线程全局类# 上面所有绑定的数据源都不支持非UI线程的写入会引起不可预知的问题运气好的话也不会报异常出来。 为了方便多线程情况下更新数据源设计一个UIThread类封装UI线程SynchronizationContext的Post、Send的操作用来处理所有的UI更新操作关于SynchronizationContext可以参考SynchronizationContext 综述。 代码如下 /// summary/// UI线程全局类/// /summarypublic static class UIThread{private static SynchronizationContext context; /// summary /// 同步更新UI控件的属性及绑定数据源 /// /summary /// param nameact/param /// param namestate/parampublic static void Send(Actionobject act, object state) {context.Send(obj { act(obj); }, state);} /// summary /// 同步更新UI控件的属性及绑定数据源 /// /summary /// param nameact/parampublic static void Send(Action act){context.Send(obj { act(); }, null);} /// summary /// 异步更新UI控件的属性及绑定数据源 /// /summary /// param nameact/param /// param namestate/parampublic static void Post(Actionobject act, object state){context.Post(obj { act(obj); }, state);} /// summary /// 异步更新UI控件的属性及绑定数据源 /// /summary /// param nameact/parampublic static void Post(Action act){context.Post(obj { act(); }, null);} /// summary /// 在UI线程中初始化只取第一次初始化时的同步上下文 /// /summarypublic static void Init(){ if (context null) { context SynchronizationContext.Current; }}} 直接在主界面的构造函数里面初始化即可 UIThread.Init();使用方法如下 Task.Run(() { //同步更新UIUIThread.Send(() { dataList.RemoveAt(0); });}); /article