通过ITypedList实现数据绑定扁平化
關(guān)于這篇文章的標(biāo)題我斟酌了很久,總覺(jué)得不管用哪個(gè)標(biāo)題,都很難用不多的文字準(zhǔn)確的描述其內(nèi)容,最后還是覺(jué)得用這樣一個(gè)標(biāo)題吧。
先談?wù)勛罱覀冺?xiàng)目團(tuán)隊(duì)中遇到的兩個(gè)需求:
1、有很多地方都需要在表格或者列表中實(shí)現(xiàn)多選,比如有一個(gè)人員列表,對(duì)應(yīng)的數(shù)據(jù)源假設(shè)是List<EmployeeInfo>,要想實(shí)現(xiàn)多選,第一感覺(jué)就是在EmployeeInfo中增加Boolean Selected這樣一個(gè)屬性,這樣固然可以解決問(wèn)題,但在其他地方根本就用不到這個(gè)屬性,而且如果后面還是其他類似的問(wèn)題的話,EmployeeInfo這個(gè)類就會(huì)變得越來(lái)越大,越來(lái)越臃腫,于是乎就增加了下面的類:
public class SelectableInfo<T> {public Boolean Selected { get; set; }public T Value { get; set; } }
似乎這樣可以解決問(wèn)題,但在與UI控件綁定的時(shí)候出現(xiàn)了問(wèn)題,將類似"Value.Name"這樣的值作為PropertyName時(shí)根本不被識(shí)別。
2、在項(xiàng)目中某個(gè)地方需要有一個(gè)日程表,比如縱軸是人員,橫軸是日期,但橫軸是不固定的,日期范圍是可以由用戶自由選擇的,于是乎設(shè)計(jì)了類似下面的數(shù)據(jù)結(jié)構(gòu):
public class ScheduleItem<TMain, TItem> {public ScheduleItem(){this.Items = new List<TItem>();}public TMain Main { get; set; }public List<TItem> Items { get; private set; } }
似乎這樣的話就可以直接用一個(gè)List<ScheduleItem<EmployeeInfo, EmployeeScheduleItemInfo>>作為UI控件的數(shù)據(jù)源,動(dòng)態(tài)的將"Main.Name", "Items[0].Name",?"Items[1].Name", ...,?"Items[n].Name"作為綁定列就能解決問(wèn)題了,但跟上面的問(wèn)題一樣,這些PropertyName在運(yùn)行時(shí)根本不被識(shí)別。
當(dāng)然,對(duì)上面的兩個(gè)問(wèn)題,如果直接將DataTable作為數(shù)據(jù)源,當(dāng)然這些問(wèn)題也就都不存在了,但DataTable本身只能算是是一個(gè)弱類型對(duì)象的List,至于弱類型對(duì)象與強(qiáng)類型對(duì)象之間的優(yōu)缺點(diǎn),就不在此篇文章的討論范圍內(nèi)了,也無(wú)意就此說(shuō)明或者解釋些什么,就事論事,只是想解決類似的問(wèn)題。
我也在網(wǎng)上找了一下相關(guān)的解決方案,似乎只有這一篇文章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談到了類似的問(wèn)題,并且給出了解決方案,我也仔細(xì)拜讀了這篇文章,并按他的方法嘗試了一番,確實(shí)能解決部分問(wèn)題,但總覺(jué)得其實(shí)現(xiàn)方式不慎完美,其一,他是通過(guò)TypeDescriptionProviderAttribute來(lái)實(shí)現(xiàn)的,而在.NET FrameWork中,對(duì)Attribute的使用是有一些限制的,比如Attribute不支持泛型聲明,也不支持變量,靈活性總共不高,其二,總覺(jué)得這種方式其實(shí)某種程度上可以理解為“篡改”了元數(shù)據(jù),很難說(shuō)會(huì)不會(huì)對(duì)系統(tǒng)其他地方的運(yùn)行帶來(lái)問(wèn)題,比如在反射該類型時(shí)等等諸如此類的地方還是有可能會(huì)出問(wèn)題的。
后來(lái)在csdn上看到有人也問(wèn)到類似的問(wèn)題,有一位高人提到可以用ITypedList實(shí)現(xiàn),但大概一般高人都習(xí)慣性的點(diǎn)到為止,并沒(méi)有給出完整的解決方案。只好仔細(xì)的研究了一下ITypedList的相關(guān)內(nèi)容,最終總算是折騰出了一個(gè)自己還比較滿意的解決方案,代碼不是很復(fù)雜,也就懶得去一一說(shuō)明了。
下面是我的解決方案:
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);}}}} }以下是測(cè)試示例:
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 窗體設(shè)計(jì)器生成的代碼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;}} }以下是測(cè)試結(jié)果:
轉(zhuǎn)載于:https://www.cnblogs.com/PaulXu/p/3969956.html
總結(jié)
以上是生活随笔為你收集整理的通过ITypedList实现数据绑定扁平化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于Hadoop生态技术构建阿里搜索离线
- 下一篇: “努力就会成功”