PropertyGrid—属性类别排序
属性默认按照字母顺序排序,有时,我们想要按自定义的顺序排序
这个工具类可以把每个属性类别里的属性排序,但是不能把属性类别排序。
为属性类添加属性:[TypeConverter(typeof(PropertySorter))]
为每个属性添加属性:[PropertyOrder(10)]
private void Form_Load(object sender, EventArgs e) { propertyGrid1.SelectedObject = new Person(); } [TypeConverter(typeof(PropertySorter))] [DefaultProperty("Name")] public class Person { protected const string PERSONAL_CAT = "Personal Details"; private string _name = "Bob"; private DateTime _birthday = new DateTime(1975,1,1); [Category(PERSONAL_CAT), PropertyOrder(10)] public string Name { get {return _name;} set {_name = value;} } [Category(PERSONAL_CAT), PropertyOrder(11)] public DateTime Birthday { get {return _birthday;} set {_birthday = value;} } [Category(PERSONAL_CAT), PropertyOrder(12)] public int Age { get { TimeSpan age = DateTime.Now - _birthday; return (int)age.TotalDays / 365; } } }
工具类
// // (C) Paul Tingey 2004 // using System; using System.Collections; using System.ComponentModel; namespace OrderedPropertyGrid { public class PropertySorter : ExpandableObjectConverter { #region Methods public override bool GetPropertiesSupported(ITypeDescriptorContext context) { return true; } public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { // // This override returns a list of properties in order // PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(value, attributes); ArrayList orderedProperties = new ArrayList(); foreach (PropertyDescriptor pd in pdc) { Attribute attribute = pd.Attributes[typeof(PropertyOrderAttribute)]; if (attribute != null) { // // If the attribute is found, then create an pair object to hold it // PropertyOrderAttribute poa = (PropertyOrderAttribute)attribute; orderedProperties.Add(new PropertyOrderPair(pd.Name,poa.Order)); } else { // // If no order attribute is specifed then given it an order of 0 // orderedProperties.Add(new PropertyOrderPair(pd.Name,0)); } } // // Perform the actual order using the value PropertyOrderPair classes // implementation of IComparable to sort // orderedProperties.Sort(); // // Build a string list of the ordered names // ArrayList propertyNames = new ArrayList(); foreach (PropertyOrderPair pop in orderedProperties) { propertyNames.Add(pop.Name); } // // Pass in the ordered list for the PropertyDescriptorCollection to sort by // return pdc.Sort((string[])propertyNames.ToArray(typeof(string))); } #endregion } #region Helper Class - PropertyOrderAttribute [AttributeUsage(AttributeTargets.Property)] public class PropertyOrderAttribute : Attribute { // // Simple attribute to allow the order of a property to be specified // private int _order; public PropertyOrderAttribute(int order) { _order = order; } public int Order { get { return _order; } } } #endregion #region Helper Class - PropertyOrderPair public class PropertyOrderPair : IComparable { private int _order; private string _name; public string Name { get { return _name; } } public PropertyOrderPair(string name, int order) { _order = order; _name = name; } public int CompareTo(object obj) { // // Sort the pair objects by ordering by order value // Equal values get the same rank // int otherOrder = ((PropertyOrderPair)obj)._order; if (otherOrder == _order) { // // If order not specified, sort by name // string otherName = ((PropertyOrderPair)obj)._name; return string.Compare(_name,otherName); } else if (otherOrder > _order) { return -1; } return 1; } } #endregion }
属性排序方式
属性的排序是基于容器类的.sort();实现的。因为控件通过TypeConverter.GetProperties();方法获得PropertyDescriptorCollection类型的对象。并根据此对象的元素设定SelectedObject的表现方式等。故实现属性类的排序首先需要获得对象的集合,然后使其按指定方式排序。因为sort()方法接受string[]类型的参数作为排序依据,其相对于属性的排序是比对其Name属性(或DisplayName属性),而我们需要在保证本地化的前提下完成排序,所以我们要在抛开其Name属性(或者DisplayName)的前提下实现排序(理论上我们能获得属性property的name属性,即方法名,但是笔者在实践中设定字符串数组中依次填入name作为排序依据,未能成功,非本地化的情况下可以实现,现在仍未找到原因,猜测其可能会以DisplayName替代Name返回???)。基于以上分析与原因,我们需要给每个属性Property添加一个属性Attribute可以作为排序的依据。到此,存在一个问题。如何根据新的属性(代称为order)对Property进行排序。较为优雅的方法是实现IComparable()接口。
事例代码如下:(部分代码来源于网络,感谢先辈)
[AttributeUsage(AttributeTargets.Property)] public class PropertyOrderAttribute : Attribute//自定义Attribute类,向property提供 ```````````````````````````````````````````````````//order属性 { private int order; public PropertyOrderAttribute(int order) { this.order = order; } public int Order { get { return order; } } } class TestPropertyDescriptor : PropertyDescriptor,IComparable//继承PropertyDescriptor类并实现IComparable接口 { private PropertyDescriptor basePropertyDescriptor; private int order; ... //构造函数 public TestPropertyDescriptor(PropertyDescriptor basePropertyDescriptor): base(basePropertyDescriptor) { this.basePropertyDescriptor = basePropertyDescriptor; order = GetOrder(basePropertyDescriptor.Attributes); } //获得property的order属性 private int GetOrder(AttributeCollection ac) { foreach (Attribute a in ac) { if (a is PropertyOrderAttribute) return ((PropertyOrderAttribute)a).Order; } return 0; } ... #region "IComparable" public int CompareTo(object tpd)//实现接口,使此类的对象可以依据order进行比较、排序 { TestPropertyDescriptor other = (TestPropertyDescriptor)tpd; if (order == other.order) return string.Compare(Name, other.Name); else return (order > other.order) ? 1 : -1; } #endregion } class ICustomTDClass1: Class1 , ICustomTypeDescriptor//Class1为需要对其属性进行排序的自定义类。 { ... public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { PropertyDescriptorCollection tmpPDC = TypeDescriptor.GetProperties(typeof(ICustomTDClass1), attributes); PropertyDescriptorCollection result = new PropertyDescriptorCollection(null); ArrayList orderPdList = new ArrayList(); foreach (PropertyDescriptor pd in tmpPDC) { TestPropertyDescriptor tpd = new TestPropertyDescriptor(pd); result.Add(tpd); orderPdList.Add(tpd); } orderPdList.Sort();//根据order排序 ArrayList propertyNames = new ArrayList(); foreach (TestPropertyDescriptor propertyAttributes in orderPdList)//获得排序后的DisplayName数组 { propertyNames.Add(propertyAttributes.DisplayName); } return result.Sort((string[])propertyNames.ToArray(typeof(string)));//根据数组对结果排序,注意这里不能直接return `````````````````````````````````````````````````````````````````````````````````````````````//result.Sort(),因为在result里存着的是PropertyDescriptor类`````````````````````````````````````````````````````````````````````````````````````````````//型的对象,而不是我们定义的TestPropertyDescriptor类`````````````````````````````````````````````````````````````````````````````````````````````//型。至此,排序功能圆满完成。 } ... }
参考