通过ITypedList实现数据绑定扁平化
關于這篇文章的標題我斟酌了很久,總覺得不管用哪個標題,都很難用不多的文字準確的描述其內容,最后還是覺得用這樣一個標題吧。
先談談最近我們項目團隊中遇到的兩個需求:
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实现数据绑定扁平化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Hadoop生态技术构建阿里搜索离线
- 下一篇: “努力就会成功”