简介#
在C#中提起控件绑定数据,大部分人首先想到的是WPF,其实Winform也支持控件和数据的绑定。
Winform中的数据绑定按控件类型可以分为以下几种:
- 简单控件绑定
- 列表控件绑定
- 表格控件绑定
绑定基类#
绑定数据类必须实现INotifyPropertyChanged接口,否则数据类属性的变更无法实时刷新到界面,但可以从界面刷新到类。
为了方便,我们设计一个绑定基类:
-
/
/
/
<summary
>
-
/
/
/ 数据绑定基类
-
/
/
/
<
/summary
>
-
public abstract
class BindableBase : INotifyPropertyChanged
-
{
-
public event PropertyChangedEventHandler PropertyChanged;
-
protected bool SetProperty
<T
>(
ref T field, T newValue, [CallerMemberName]
string propertyName
=
null)
-
{
-
if (!EqualityComparer
<T
>.
Default.Equals(field, newValue))
-
{
-
field
= newValue;
-
PropertyChanged?.
Invoke(this, new PropertyChangedEventArgs(propertyName));
-
return
true;
-
}
-
return
false;
-
}
-
}
需要绑定的数据类继承绑定基类即可:
-
/
/
/
<summary
>
-
/
/
/ 数据类
-
/
/
/
<
/summary
>
-
public
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
>
-
/
/
/ 数据绑定基类的扩展方法
-
/
/
/
<
/summary
>
-
public static
class BindableBaseExtension
-
{
-
-
/
/
/
<summary
>
-
/
/
/ 获取属性的描述,返回元组格式为 Item
1:描述信息 Item
2:属性名称
-
/
/
/
<
/summary
>
-
/
/
/
<param name
=
"type"
>
<
/param
>
-
/
/
/
<returns
>
<
/returns
>
-
public static Tuple
<
string,
string
>[] GetDescription(this BindableBase bindData)
-
{
-
var proAry
= bindData.GetType().GetProperties();
-
var desAry
= new Tuple
<
string,
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 name
=
"data"
>
<
/param
>
-
public 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 Dat
a() { ID
=
1,Name
=
"test"};
-
/
/常规绑定方法
-
textBox
1.DataBindings.
Add(
"Text",
data,
"ID");
-
/
/使用这种方式避免硬编码
-
textBox
2.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是一个可用来创建双向数据绑定机制的泛型集合,使用方法如下:
-
BindingList
<
Data
> list
= new BindingList
<
Data
>();
-
list.
Add(new Dat
a() { ID
=
1, Name
=
"name1" });
-
list.
Add(new Dat
a() { ID
=
2, Name
=
"name2" });
-
-
comboBox
1.DataSource
= list;
-
comboBox
1.ValueMember
=
"ID";
-
comboBox
1.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");
-
-
comboBox
1.DataSource
= dt;
-
comboBox
1.ValueMember
=
"ID";
-
comboBox
1.DisplayMember
=
"Name";
绑定BindingSource源#
BindingSource 类封装窗体的数据源,旨在简化将控件绑定到基础数据源的过程,详细内容可查看 BindingSource 组件概述。
有时候数据类型可能没有实现INotifyPropertyChanged接口,并且这个数据类型我们还修改不了,这种情况就只能使用BindingSource来将控件绑定到数据了。
假设Data类没有继承BindableBase,绑定方法如下:
-
List
<
Data
> list
= new List
<
Data
>();
-
list.
Add(new Dat
a() { ID
=
1, Name
=
"name1" });
-
list.
Add(new Dat
a() { ID
=
2, Name
=
"name2" });
-
-
BindingSource bs
= new BindingSource();
-
bs.DataSource
= list;
-
comboBox
1.DataSource
= bs;
-
comboBox
1.ValueMember
=
"ID";
-
comboBox
1.DisplayMember
=
"Name";
关键是下面的步骤,改变集合内容时手动触发变更:
-
/
/单项数据变更
-
list[
0].Name
=
"test";
-
bs.ResetItem(
0);
-
-
/
/添加数据项
-
list.
Add(new Dat
a() { ID
=
3, Name
=
"name3" });
-
bs.ResetBindings(
false);
-
/
/在BindingSource上添加或使用BindingList列表,则可以不用手动触发变更通知
-
bs.
Add(new Dat
a() { ID
=
4, Name
=
"name4" });
表格控件绑定#
绑定DataTable#
方法如下:
-
DataColumn c
1
= new DataColumn(
"ID", typeof(
string));
-
DataColumn c
2
= new DataColumn(
"名称", typeof(
string));
-
dt.
Columns.
Add(c
1);
-
dt.
Columns.
Add(c
2);
-
dt.Rows.
Add(
11,
22);
-
-
/
/禁止添加行,防止显示空白行
-
dataGridView
1.AllowUserToAddRows
=
false;
-
/
/选择是否自动创建列
-
dataGridView
1.AutoGenerateColumns
=
true;
-
-
dataGridView
1.DataSource
= dt.DefaultView;
绑定BindingList#
方法如下:
-
/
/填充数据
-
BindingList
<
Data
> dataList
= new BindingList
<
Data
>();
-
for (int i
=
0; i
<
5; i
+
+)
-
{
-
dataList.
Add(new Dat
a() { ID
= i, Name
=
"Name"
+ i.ToString() });
-
}
-
-
-
/
/禁止添加行,防止显示空白行
-
dataGridView
1.AllowUserToAddRows
=
false;
-
/
/选择是否自动创建列
-
dataGridView
1.AutoGenerateColumns
=
false;
-
/
/手动创建列
-
var desAry
= dataList[
0].GetDescription();
-
int idx
=
0;
-
foreach (var des
in desAry)
-
{
-
idx
= dataGridView
1.
Columns.
Add($
"column{idx}", des.Item
1);
/
/ 手动添加某列
-
dataGridView
1.
Columns[idx].DataPropertyName
= des.Item
2;
/
/ 设置为某列的字段
-
}
-
/
/绑定集合
-
dataGridView
1.DataSource
= dataList;
-
/
/集合变更事件
-
dataList.ListChanged
+
= DataList_ListChanged;
注:上面的GetDescription()是绑定基类的扩展方法。
BindingList提供集合的变更通知,Data通过继承绑定基类提供属性值的变更通知。
UI线程全局类#
上面所有绑定的数据源都不支持非UI线程的写入,会引起不可预知的问题,运气好的话也不会报异常出来。
为了方便多线程情况下更新数据源,设计一个UIThread类封装UI线程SynchronizationContext的Post、Send的操作,用来处理所有的UI更新操作,关于SynchronizationContext可以参考SynchronizationContext 综述。
代码如下:
-
/
/
/
<summary
>
-
/
/
/ UI线程全局类
-
/
/
/
<
/summary
>
-
public static
class UIThread
-
{
-
private static SynchronizationContext context;
-
-
-
/
/
/
<summary
>
-
/
/
/ 同步更新UI控件的属性及绑定数据源
-
/
/
/
<
/summary
>
-
/
/
/
<param name
=
"act"
>
<
/param
>
-
/
/
/
<param name
=
"state"
>
<
/param
>
-
public static void
Send(Action
<
object
> act,
object state)
-
{
-
context.
Send(obj
=
> { act(obj); }, state);
-
}
-
-
/
/
/
<summary
>
-
/
/
/ 同步更新UI控件的属性及绑定数据源
-
/
/
/
<
/summary
>
-
/
/
/
<param name
=
"act"
>
<
/param
>
-
public static void
Send(Action act)
-
{
-
context.
Send(obj
=
> { act(); },
null);
-
}
-
-
/
/
/
<summary
>
-
/
/
/ 异步更新UI控件的属性及绑定数据源
-
/
/
/
<
/summary
>
-
/
/
/
<param name
=
"act"
>
<
/param
>
-
/
/
/
<param name
=
"state"
>
<
/param
>
-
public static void Post(Action
<
object
> act,
object state)
-
{
-
context.Post(obj
=
> { act(obj); }, state);
-
}
-
-
/
/
/
<summary
>
-
/
/
/ 异步更新UI控件的属性及绑定数据源
-
/
/
/
<
/summary
>
-
/
/
/
<param name
=
"act"
>
<
/param
>
-
public static void Post(Action act)
-
{
-
context.Post(obj
=
> { act(); },
null);
-
}
-
-
-
/
/
/
<summary
>
-
/
/
/ 在UI线程中初始化,只取第一次初始化时的同步上下文
-
/
/
/
<
/summary
>
-
public static void Init()
-
{
-
if (context
=
=
null)
-
{
-
context
= SynchronizationContext.Current;
-
}
-
}
-
}
直接在主界面的构造函数里面初始化即可:
UIThread.Init();
使用方法如下:
-
Task.
Run(()
=
>
-
{
-
/
/同步更新UI
-
UIThread.
Send(()
=
> { dataList.RemoveAt(
0); });
-
});
</article>
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1541037.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!