你的位置:首页 > ASP.net教程

[ASP.net教程][WinForm] DataGridView绑定DataTable,ComboBox列绑定Dictionary


一  需求介绍

一般像枚举类型的数据,我们在数据库里存储着诸如(1、2、3、4…)或者(“001”、“002”、“003”…)此类,但是界面上我们想要显示的是具体的文本内容,以便用户理解使用。所以在从数据库中加载出来的数据DataTable绑定到DataGridView上时,就需要其中一些枚举列采用下拉框,并绑定对应的枚举数据源。

二  具体实现

首先,如果 DataGridView 的 AutoGenerateColumns 为 true 时,在绑定 DataTable 到 DataGridView 上时,会自动生成绑定数据中的各列,且默认均为普通的 DataGridViewTextBoxColumn 。所以如果要设置 DataGridView 中的下拉列,首先要把 AutoGenerateColumns 设置为 false

 1 /// <summary> 2 /// Sample 3 /// DataGridView绑定DataTable,ComboBox列绑定enum枚举 4 /// </summary> 5 public MainForm() 6 { 7   InitializeComponent(); 8  9   DataTable dataTable = CreateDataTable(); 10   dataGridView.AutoGenerateColumns = true;// 默认设置 11   dataGridView.DataSource = dataTable; 12   // SetGridView(dataTable); 13 } 14  15 /// <summary> 16 /// 构建数据源 17 /// 可以从数据库读取出来构建DataTable数据源 18 /// </summary> 19 /// <returns>DataTable数据表</returns> 20 public DataTable CreateDataTable() 21 { 22   DataTable dt = new DataTable(); 23   dt.Columns.Add(new DataColumn("Id", typeof(int))); 24   dt.Columns.Add(new DataColumn("Name", typeof(string))); 25   dt.Columns.Add(new DataColumn("EnumCol1", typeof(int))); 26   dt.Columns.Add(new DataColumn("EnumCol2", typeof(string))); 27   Random r = new Random(); 28   for (int i = 0; i < 50; i++) 29   { 30     DataRow dr = dt.NewRow(); 31     dr[0] = r.Next(); 32     dr[1] = "Name = " + r.Next(); 33     dr[2] = r.Next(1, 5); 34     dr[3] = "EnumCol_" + r.Next(1, 5); 35     dt.Rows.Add(dr); 36   } 37  38   return dt; 39 }

View Code

效果图(AutoGenerateColumns = true):
image

    在将 AutoGenerateColumns 属性设置为 false 后,就要手动生成各列了。其中 EnumCol1 列要绑定一个枚举类型,EnumCol2 要绑定一个字典类型(Dictionary<string, string>)。

 1 /// <summary> 2 /// 枚举1 3 /// </summary> 4 private enum EnumCol1 5 { 6   A = 1, 7   B = 2, 8   C = 3, 9   D = 4, 10   E = 5 11 } 12 /// <summary> 13 /// 枚举转字典 14 /// </summary> 15 /// <typeparam name="T"></typeparam> 16 /// <param name="enumType"></param> 17 /// <returns></returns> 18 private Dictionary<int, string> EnumToDictionary(Type enumType) 19 { 20   Dictionary<int, string> result = new Dictionary<int, string>(); 21   foreach (int key in Enum.GetValues(enumType)) 22   { 23     string value = Enum.GetName(enumType, key); 24     result.Add(key, value); 25   } 26  27   return result; 28 }

通过上面的代码中 EnumToDictionary 方法把枚举类型转换为字典类型,这样就可以统一处理数据绑定了。

 1 /// <summary> 2 /// 设置DataGridView视图 3 /// </summary> 4 /// <param name="dt">数据源表</param> 5 public void SetGridView(DataTable dt) 6 { 7   // 在放弃绑定时自动生成列,并且还未初始化DataGridView的列时 8   // 手动生成各列 9   if (!dataGridView.AutoGenerateColumns && dataGridView.Columns.Count == 0) 10   { 11     Dictionary<int, string> dic = EnumToDictionary(typeof(EnumCol1)); 12     foreach (DataColumn dc in dt.Columns) 13     { 14       string colName = dc.ColumnName; 15       DataGridViewColumn dgvc = null; 16       if (colName.StartsWith("EnumCol"))// 生成下拉列 17       { 18         dgvc = new DataGridViewComboBoxColumn(); 19         BindingSource bs = new BindingSource(); 20         if (colName.EndsWith("1"))// 绑定枚举1 21         { 22           bs.DataSource = dic; 23           dgvc.ValueType = typeof(int); 24         } 25         else if (colName.EndsWith("2"))// 绑定枚举2 26         { 27           bs.DataSource = new Dictionary<string, string>() 28           { 29             {"EnumCol_1", "AA"}, 30             {"EnumCol_2", "BB"}, 31             {"EnumCol_3", "CC"}, 32             {"EnumCol_4", "DD"}, 33             {"EnumCol_5", "EE"} 34           }; 35           dgvc.ValueType = typeof(string); 36         } 37         ((DataGridViewComboBoxColumn)dgvc).DisplayMember = "Value"; 38         ((DataGridViewComboBoxColumn)dgvc).ValueMember = "Key"; 39         ((DataGridViewComboBoxColumn)dgvc).DataSource = bs; 40         ((DataGridViewComboBoxColumn)dgvc).DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing; 41         ((DataGridViewComboBoxColumn)dgvc).FlatStyle = FlatStyle.Flat; 42       } 43       else// 生成普通列 44       { 45         dgvc = new DataGridViewTextBoxColumn(); 46       } 47       dgvc.Name = colName; 48       dgvc.DataPropertyName = colName;// 保证该列的值绑定到DataTable中colName列上 49       dataGridView.Columns.Add(dgvc); 50     } 51   } 52 } 53 

在列类型为 DataGridViewComboBoxColumn,且要绑定字典数据源(Dictionary)时,需要通过 BindingSource 。即:

BindingSource bs = new BindingSource();bs.DataSource = dic;((DataGridViewComboBoxColumn)dgvc).DataSource = bs;

需要特别注意的是==>

48行代码:

dgvc.DataPropertyName = colName;  表示该下拉列的值绑定对应于数据源 DataTable 中 colName 列数据;

37行、38行代码:

((DataGridViewComboBoxColumn)dgvc).DisplayMember = "Value";

((DataGridViewComboBoxColumn)dgvc).ValueMember = "Key";

DisplayMember 是表示下拉框显示的值绑定于 dgvc 的数据源的某个位置(字典的Value);

ValueMember 是表示下拉框实际值绑定于 dgvc 的数据源的某个位置(字典的Key);

23行、35行代码:

dgvc.ValueType = typeof(int);

dgvc.ValueType = typeof(string);

这两句则是指定该列绑定数据源里该列的类型。此时,必须保证数据源 DataTable 里该列的值类型,必须和该列的 ValueMember 属性绑定的数据源类型一致。以文中例子,即 datagridview 的列 EnumCol1 和列 EnumCol2 的数据类型必须和所绑定的 Dictionary 的 Key 类型一致(因为 dgvc 的 ValueMember 属性绑定于 Key)。

效果图:

image

三  追加几句

1、SQLite 数据库中的 INTEGER 和 INT 类型区别需要通过“Tools –> Options –> Type Mappings”来确定。

image

但是实际上,其中 INT 类型是与C#的 Int32 对应,如果将 INTEGER 类型的列绑定到 Dictionary<int, string>上,是会报如下错误的:

image

2、本文以 DataGridViewComboBoxColumn 列绑定 Dictionary 字典数据源为例说明具体绑定细节,当然该类型列也可以绑定于 DataTable 等其他类型数据,只要设置好绑定细节就可以了。

3、至于楼主在网上查找相关资源时,有不少人(All Most)提到要在绑定 DataGridViewComboBoxColumn 的数据源之后再绑定 datagridview 的数据源就可以解决上面那个异常,而且这种答案粘贴的满网络都是,可是楼主当初尝试了无论哪个在前哪个在后都是不行,害的楼主怀疑了老半天自己的智商,⊙︿⊙!最后还是用GoogleFQ查到一个英文论坛里一段英文回复才晓得哪里才是这个异常的关键!怎么说呢,这种未经验证的解决方案在中文网络里传的到处都是,而且一看就知道是那种粘贴复制过来的,也没有其他注解说明。

有句话说的不无道理,程序员本来就挺辛苦的,经常加班熬夜啥的,我们程序员自己又何必为难程序员呢?你给别人挖一个坑,别人也给你挖一个坑,好吧,我们这个行业就在这里玩跳坑,爬坑的弱智游戏吧,然后让其他行业嘲笑鄙视我们吧!何必呢!还是希望在发布或者转发一个解决方案时,能先验证它的正确性,再加以自己的注释说明,至少要对得起这份职业,对得起其他的程序员,少给别人挖坑,别人也给你少挖坑,这样何愁啥bug解决不了,何愁多费那么多无用功去解决一个别人已经解决好的问题呢?

四  资源下载

样例工程: http://files.cnblogs.com/files/memento/DataGridView_ComboBoxColumnSample.zip