关于这篇文章的标题我斟酌了很久,总觉得不管用哪个标题,都很难用不多的文字准确的描述其内容,最后还是觉得用这样一个标题吧。

先谈谈最近我们项目团队中遇到的两个需求:

1、有很多地方都需要在表格或者列表中实现多选,比如有一个人员列表,对应的数据源假设是List<EmployeeInfo>,要想实现多选,第一感觉就是在EmployeeInfo中增加Boolean Selected这样一个属性,这样固然可以解决问题,但在其他地方根本就用不到这个属性,而且如果后面还是其他类似的问题的话,EmployeeInfo这个类就会变得越来越大,越来越臃肿,于是乎就增加了下面的类:

public class SelectableInfo<T>
{public Boolean Selected { get; set; }public T Value { get; set; }
}

  

似乎这样可以解决问题,但在与UI控件绑定的时候出现了问题,将类似"Value.Name"这样的值作为PropertyName时根本不被识别。

2、在项目中某个地方需要有一个日程表,比如纵轴是人员,横轴是日期,但横轴是不固定的,日期范围是可以由用户自由选择的,于是乎设计了类似下面的数据结构:

public class ScheduleItem<TMain, TItem>
{public ScheduleItem(){this.Items = new List<TItem>();}public TMain Main { get; set; }public List<TItem> Items { get; private set; }
}

 

似乎这样的话就可以直接用一个List<ScheduleItem<EmployeeInfo, EmployeeScheduleItemInfo>>作为UI控件的数据源,动态的将"Main.Name", "Items[0].Name", "Items[1].Name", ..., "Items[n].Name"作为绑定列就能解决问题了,但跟上面的问题一样,这些PropertyName在运行时根本不被识别。

当然,对上面的两个问题,如果直接将DataTable作为数据源,当然这些问题也就都不存在了,但DataTable本身只能算是是一个弱类型对象的List,至于弱类型对象与强类型对象之间的优缺点,就不在此篇文章的讨论范围内了,也无意就此说明或者解释些什么,就事论事,只是想解决类似的问题。

我也在网上找了一下相关的解决方案,似乎只有这一篇文章http://blogs.msdn.com/b/msdnts/archive/2007/01/19/how-to-bind-a-datagridview-column-to-a-second-level-property-of-a-data-source.aspx谈到了类似的问题,并且给出了解决方案,我也仔细拜读了这篇文章,并按他的方法尝试了一番,确实能解决部分问题,但总觉得其实现方式不慎完美,其一,他是通过TypeDescriptionProviderAttribute来实现的,而在.NET FrameWork中,对Attribute的使用是有一些限制的,比如Attribute不支持泛型声明,也不支持变量,灵活性总共不高,其二,总觉得这种方式其实某种程度上可以理解为“篡改”了元数据,很难说会不会对系统其他地方的运行带来问题,比如在反射该类型时等等诸如此类的地方还是有可能会出问题的。

后来在csdn上看到有人也问到类似的问题,有一位高人提到可以用ITypedList实现,但大概一般高人都习惯性的点到为止,并没有给出完整的解决方案。只好仔细的研究了一下ITypedList的相关内容,最终总算是折腾出了一个自己还比较满意的解决方案,代码不是很复杂,也就懒得去一一说明了。

下面是我的解决方案:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text.RegularExpressions;namespace DynamicBinding
{public class PropertyBindingList<T> : BindingList<T>, ITypedList{private List<String> bindProperties;private Dictionary<String, PropertyDescriptor> propertyDescriptorDictionary;public PropertyBindingList(){this.bindProperties = new List<String>();this.innerPropertyDescriptorCollection = TypeDescriptor.GetProperties(typeof(T));this.propertyDescriptorDictionary = new Dictionary<String, PropertyDescriptor>();}public void AddBindProperty(String propertyName){if (this.bindProperties.Contains(propertyName)){throw new ArgumentException(String.Format(@"The property ""{0}"" is already exists.", propertyName), "propertyName");}this.bindProperties.Add(propertyName);}public void RemoveBindProperty(String propertyName){this.bindProperties.Remove(propertyName);}private PropertyDescriptorCollection innerPropertyDescriptorCollection;public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors){var array = new PropertyDescriptor[this.innerPropertyDescriptorCollection.Count + this.bindProperties.Count];this.innerPropertyDescriptorCollection.CopyTo(array, 0);for (var i = 0; i < this.bindProperties.Count; i++){array[this.innerPropertyDescriptorCollection.Count + i] = this.GetPropertyDescriptor(this.bindProperties[i]);}return new PropertyDescriptorCollection(array);}private PropertyDescriptor GetPropertyDescriptor(String propertyPath){if (String.IsNullOrEmpty(propertyPath)){throw new ArgumentNullException("propertyPath");}var array = propertyPath.Split('.');var first = array.First();var propertyDescriptor = this.GetPropertyDescriptor(this.innerPropertyDescriptorCollection, first);for (var i = 1; i < array.Length; i++){propertyDescriptor = this.CreatePropertyDescriptor(propertyDescriptor, array[i]);}return propertyDescriptor;}private PropertyDescriptor GetPropertyDescriptor(PropertyDescriptorCollection propertyDescriptorCollection, String name){var regex = new Regex(@"(?<name>\w+)\[(?<index>\d+)\]");var match = regex.Match(name);if (match.Success){var propertyName = match.Groups["name"].Value;var indexText = match.Groups["index"].Value;var index = Int32.Parse(indexText);var arrayPropertyDescriptor = propertyDescriptorCollection[propertyName];if (arrayPropertyDescriptor == null){throw new ArgumentOutOfRangeException(String.Format(@"Can not find property descriptor ""{0}"" in propertyDescriptorCollection.", propertyName));}var itemPropertyDescriptorName = String.Format("{0}[{1}]", arrayPropertyDescriptor.Name, index);PropertyDescriptor itemPropertyDescriptor;if (!this.propertyDescriptorDictionary.TryGetValue(itemPropertyDescriptorName, out itemPropertyDescriptor)){itemPropertyDescriptor = new InnerItemPropertyDescriptor(itemPropertyDescriptorName,arrayPropertyDescriptor,index);this.propertyDescriptorDictionary.Add(itemPropertyDescriptorName, itemPropertyDescriptor);}return itemPropertyDescriptor;}else{var result = propertyDescriptorCollection[name];if (result == null){throw new ArgumentOutOfRangeException(String.Format(@"Can not find property descriptor ""{0}"" in propertyDescriptorCollection.", name));}return result;}}private PropertyDescriptor CreatePropertyDescriptor(PropertyDescriptor parentPropertyDescriptor, String name){var regex = new Regex(@"(?<name>\w+)\[(?<index>\d+)\]");var match = regex.Match(name);if (match.Success){var propertyName = match.Groups["name"].Value;var indexText = match.Groups["index"].Value;var index = Int32.Parse(indexText);var propertyDescriptorName = parentPropertyDescriptor.Name + "." + propertyName;PropertyDescriptor arrayPropertyDescriptor;if (!this.propertyDescriptorDictionary.TryGetValue(propertyDescriptorName, out arrayPropertyDescriptor)){var properties = TypeDescriptor.GetProperties(parentPropertyDescriptor.PropertyType);var valuePropertyDescriptor = properties[propertyName];if (valuePropertyDescriptor == null){throw new ArgumentOutOfRangeException(String.Format(@"Can not find property descriptor ""{0}"" in type ""{1}"".", propertyName, parentPropertyDescriptor.PropertyType));}arrayPropertyDescriptor = new InnerPropertyDescriptor(propertyDescriptorName,parentPropertyDescriptor,valuePropertyDescriptor);this.propertyDescriptorDictionary.Add(propertyDescriptorName, arrayPropertyDescriptor);}var itemPropertyDescriptorName = String.Format("{0}[{1}]", arrayPropertyDescriptor.Name, index);PropertyDescriptor itemPropertyDescriptor;if (!this.propertyDescriptorDictionary.TryGetValue(itemPropertyDescriptorName, out itemPropertyDescriptor)){itemPropertyDescriptor = new InnerItemPropertyDescriptor(itemPropertyDescriptorName,arrayPropertyDescriptor,index);this.propertyDescriptorDictionary.Add(itemPropertyDescriptorName, itemPropertyDescriptor);}return itemPropertyDescriptor;}else{var propertyDescriptorName = parentPropertyDescriptor.Name + "." + name;PropertyDescriptor propertyDescriptor;if (!this.propertyDescriptorDictionary.TryGetValue(propertyDescriptorName, out propertyDescriptor)){var properties = TypeDescriptor.GetProperties(parentPropertyDescriptor.PropertyType);var valuePropertyDescriptor = properties[name];if (valuePropertyDescriptor == null){throw new ArgumentOutOfRangeException(String.Format(@"Can not find property descriptor ""{0}"" in type ""{1}"".", name, parentPropertyDescriptor.PropertyType));}propertyDescriptor = new InnerPropertyDescriptor(propertyDescriptorName,parentPropertyDescriptor,valuePropertyDescriptor);this.propertyDescriptorDictionary.Add(propertyDescriptorName, propertyDescriptor);}return propertyDescriptor;}}public String GetListName(PropertyDescriptor[] listAccessors){return typeof(T).Name;}private abstract class BasePropertyDescriptor : PropertyDescriptor{public BasePropertyDescriptor(String name): base(name, null){}public override bool IsReadOnly { get { return false; } }public override void ResetValue(object component) { }public override bool CanResetValue(object component) { return false; }public override bool ShouldSerializeValue(object component) { return true; }}private class InnerPropertyDescriptor : BasePropertyDescriptor{public InnerPropertyDescriptor(String name, PropertyDescriptor parentPropertyDescriptor, PropertyDescriptor valuePropertyDescriptor): base(name){this.ParentPropertyDescriptor = parentPropertyDescriptor;this.ValuePropertyDescriptor = valuePropertyDescriptor;}public PropertyDescriptor ParentPropertyDescriptor { get; private set; }public PropertyDescriptor ValuePropertyDescriptor { get; private set; }public override Type ComponentType { get { return this.ParentPropertyDescriptor.PropertyType; } }public override Type PropertyType { get { return this.ValuePropertyDescriptor.PropertyType; } }public override object GetValue(object component){var parentPropertyValue = this.ParentPropertyDescriptor.GetValue(component);if (parentPropertyValue != null){return this.ValuePropertyDescriptor.GetValue(parentPropertyValue);}return null;}public override void SetValue(object component, object value){var parentPropertyValue = this.ParentPropertyDescriptor.GetValue(component);if (parentPropertyValue != null){this.ValuePropertyDescriptor.SetValue(parentPropertyValue, value);this.OnValueChanged(component, EventArgs.Empty);}}}private class InnerItemPropertyDescriptor : BasePropertyDescriptor{public InnerItemPropertyDescriptor(String name, PropertyDescriptor parentPropertyDescriptor, Int32 index): base(name){this.ParentPropertyDescriptor = parentPropertyDescriptor;this.Index = index;}public PropertyDescriptor ParentPropertyDescriptor { get; private set; }public Int32 Index { get; private set; }public override Type ComponentType { get { return this.ParentPropertyDescriptor.PropertyType; } }public override Type PropertyType { get { return this.ParentPropertyDescriptor.PropertyType.GetElementType(); } }public override object GetValue(object component){var parentPropertyValue = this.ParentPropertyDescriptor.GetValue(component) as IList;if (parentPropertyValue != null && parentPropertyValue.Count > this.Index){return parentPropertyValue[this.Index];}return null;}public override void SetValue(object component, object value){var parentPropertyValue = this.ParentPropertyDescriptor.GetValue(component) as IList;if (parentPropertyValue != null && parentPropertyValue.Count > this.Index){parentPropertyValue[this.Index] = value;this.OnValueChanged(component, EventArgs.Empty);}}}}
}

以下是测试示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;namespace DynamicBinding
{static class Program{[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new TestForm());}}partial class TestForm{private System.ComponentModel.IContainer components = null;protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码private void InitializeComponent(){this.dataGridView1 = new System.Windows.Forms.DataGridView();((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();this.SuspendLayout();// // dataGridView1// this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;this.dataGridView1.Location = new System.Drawing.Point(0, 0);this.dataGridView1.Name = "dataGridView1";this.dataGridView1.RowTemplate.Height = 23;this.dataGridView1.Size = new System.Drawing.Size(784, 562);this.dataGridView1.TabIndex = 0;// // Form1// this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(784, 562);this.Controls.Add(this.dataGridView1);this.Name = "TestForm";this.Text = "TestForm";((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();this.ResumeLayout(false);}#endregionprivate System.Windows.Forms.DataGridView dataGridView1;}public partial class TestForm : Form{public TestForm(){InitializeComponent();}protected override void OnLoad(EventArgs e){this.dataGridView1.AutoGenerateColumns = true;var list = new PropertyBindingList<TestA>();list.AddBindProperty("List[0].BID");list.AddBindProperty("List[0].List[0].CID");list.AddBindProperty("List[0].List[0].CName");list.AddBindProperty("List[0].List[1].CID");list.AddBindProperty("List[0].List[1].CName");list.AddBindProperty("List[1].BName");list.AddBindProperty("List[1].List[0].CID");list.AddBindProperty("List[1].List[0].CName");list.AddBindProperty("List[1].List[1].CID");list.AddBindProperty("List[1].List[1].CName");list.Add(new TestA{AID = 1,AName = "A001",List = new TestB[]{ new TestB{BID = 11,BName = "B11",List = new TestC[]{new TestC{CID = 111,CName = "C111"},new TestC{CID = 112,CName = "C112"}}},                    new TestB{BID = 12,BName = "B12",List = new TestC[]{new TestC{CID = 113,CName = "C113"},new TestC{CID = 114,CName = "C114"}}},new TestB{BID = 13,BName = "B13"}}});list.Add(new TestA{AID = 1,AName = "A001"});this.dataGridView1.DataSource = list;base.OnLoad(e);}}public class TestA{public Int32 AID{get;set;}public String AName{get;set;}public TestB[] List{get;set;}}public class TestB{public Int32 BID{get;set;}public String BName{get;set;}public TestC[] List{get;set;}}public class TestC{public Int32 CID{get;set;}public String CName{get;set;}}
}

以下是测试结果:

转载于:https://www.cnblogs.com/PaulXu/p/3969956.html

通过ITypedList实现数据绑定扁平化相关推荐

  1. 前端面试题 HTML5 CSS3(盒子模型、盒子水平垂直居中、经典布局) JS(闭包、深浅克隆、数据劫持和拦截) 算法(排序、去重、数组扁平化) Vue(双向数据绑定原理、通信方式)

    前端面试题 HTML5 相关面试题 CSS3 相关面试题 盒子模型 盒子水平垂直居中的方案 经典布局方案 圣杯布局 双飞翼布局 flex布局 定位方式布局 css实现三角形 JS 相关面试题 8种数据 ...

  2. 2021年大数据常用语言Scala(二十三):函数式编程 扁平化映射 flatMap

    目录 扁平化映射 | flatMap 定义 案例 扁平化映射  flatMap 扁平化映射也是将来用得非常多的操作,也是必须要掌握的. 定义 可以把flatMap,理解为先map,然后再flatten ...

  3. 简单又好看的按钮,扁平化按钮。

    原文  http://blog.csdn.net/peijiangping1989/article/details/19333779 主题 安卓开发 今天分享一下流行的扁平化按钮.完全不需要用到图片哦 ...

  4. UI设计培训中的扁平化理念

    本文是为正在学习UI设计的同学们整理的一份资料,主要讲的是UI设计培训中的扁平化理念,扁平化的设计是抛弃一切装饰的设计,扁平化设计使得用户操作起来更加简洁.高效和舒适.简洁大方的交互界面设计自然能够引 ...

  5. 精美素材分享:16套免费的扁平化图标下载

    在这篇文章中你可以看到16套华丽的扁平化图标素材,对于设计师来说非常有价值,能够帮助他们节省大量的时间.这些精美的扁平化图标可用于多种用途,如:GUI 设计,印刷材料,WordPress 主题,演示, ...

  6. 字节老板在群里diss员工:上班时间聊游戏,工作很闲吗?员工回怼:查聊天记录,看聊天时间占工作时间百分比!网友:真·扁平化管理!...

    上班时间聊天是大多数社畜的现状,如果不小心被老板抓包怎么办? 这几天,几张截图在网上火了起来,截图显示这是一个字节跳动的游戏群,某天字节张老板突然在里面说了长长的一段话,内容如下: 看来张老板对员工上 ...

  7. keras扁平化 激活函数 避免过拟合技巧

    keras扁平化 https://keras.io/api/layers/core_layers/lambda/ 了解激活函数 https://cs231n.github.io/neural-netw ...

  8. php n维数组扁平化,js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解...

    数组的扁平化:将多维数组变成一维数组 对于一个像这样的嵌套数组:a=[1,[2,[3,4]],5,6]我们想要把它变成一个一维数组,有下面几种方法: 方法一:递归一 function parseArr ...

  9. Java树形转扁平_多层嵌套map对象转扁平化map

    将深度嵌套的map对象转换为扁平化的map对象输出. import org.apache.commons.lang3.StringUtils; import java.util.HashMap; im ...

最新文章

  1. 《元学习meta learning)》2020综述论文大全!
  2. Smart-Forms套打和不套打
  3. Vue 学习 之 7.01 学习笔记
  4. JavaScript面向对象——多继承的实现与理解
  5. less webpack 热更新_webpack---less+热更新 使用
  6. Interrupted Exception异常可能没你想的那么简单!
  7. SAP License:谈PA和PCA的区别
  8. 计算机蓝屏分析报告,如何获取电脑蓝屏后的错误报告DMP文件
  9. Java操作Oracle数据库——ARRAY TABLE类型批量数据处理区别比较
  10. linux 命令行字体发虚,解决Linux字体发虚的终极方法
  11. 民生银行香港卡的办理经历,和踩过的那些坑····
  12. 【python与数据分析】CH3 python序列结构补充——字符串
  13. 探讨IT人的创业方向
  14. Ray --内部运行机制、对象存储中对象的存储和容错
  15. WSL ubuntu xfce4桌面远程连接
  16. 大学生创新创业万学答案(三)
  17. 单播(Unicast),组播(Multicast),广播(Broadcast)
  18. 【ARM】ARM处理器概述
  19. AI版的五子棋小游戏
  20. 教你如何修改铁威马NAS的用户密码

热门文章

  1. TAdoquery的Filter
  2. $(@:_config=)的意思
  3. 【转】反病毒攻防研究第003篇:添加节区实现代码的植入
  4. 计算机网络核心知识(上)
  5. FIFO,LRU,OPT的命中、调换过程
  6. 2021总结,2022展望
  7. 以操作系统的角度述说线程与进程
  8. leetcode第一题两数相加
  9. 从思维导图学习操作系统(一)
  10. STM32F4 HAL库开发 --时钟使能和配置