最近在做表单设计器,设计器上的控件都是我们自己封装的,但每个属性类别里的属性是按照属性的拼音排序的,现在想按照PropertyIndex标识进行排序(PropertyIndex的后三位是用来标识编辑器的)。
具体实现如下:
using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.ComponentModel; using HC.Test.ComponentModel; using HC.Test.Common; using System.Data; using HC.Test.Common.ComponentModel; using System.Xml.Serialization; using System.Windows.Forms; using HC.Test.Forms.ControlConfig; using System.Collections; namespace HC.Test.Designer.UI { public class ControlEditorTypeDescriptionProvider : TypeDescriptionProvider { protected TypeDescriptionProvider _baseProvider; private PropertyDescriptorCollection _propCache; protected Dictionary<Type, Type> dictEdtor; /// <summary> /// 属性Editor字典 /// Key:Attribute Value:Editor /// </summary> public Dictionary<Type, Type> EdtorDictionary { get { return dictEdtor; } } public ControlEditorTypeDescriptionProvider() : base() { dictEdtor = new Dictionary<Type, Type>(); } /// <summary> /// /// </summary> /// <param name="t">要修改属性的基类</param> public ControlEditorTypeDescriptionProvider(Type t) : this() { _baseProvider = TypeDescriptor.GetProvider(t); } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { PropertiesTypeDescriptor typeDes = new PropertiesTypeDescriptor(_baseProvider.GetTypeDescriptor(objectType, instance), this, objectType); return typeDes; } } /// <summary> /// 属性集合的描述 /// </summary> public class PropertiesTypeDescriptor : CustomTypeDescriptor { private Type objType; private DataTable dtConfig = new DataTable(); private ControlEditorTypeDescriptionProvider provider; public PropertiesTypeDescriptor(ICustomTypeDescriptor descriptor, ControlEditorTypeDescriptionProvider provider, Type objType) : base(descriptor) { if (provider == null) { throw new ArgumentNullException("provider"); } if (descriptor == null) { throw new ArgumentNullException("descriptor"); } if (objType == null) { throw new ArgumentNullException("objectType"); } this.objType = objType; this.provider = provider; } /// <summary> /// 获取属性列表 /// </summary> /// <param name="attributes"></param> /// <returns></returns> public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { try { string nowIndexCode = ""; Type editor; Type editorControl; Attribute abEvent; EventPropertyDescriptor des; bool showPageSize = true; //从dll中读取配置 Dictionary<string, ControlEditorType> dictConfig = PubFunc.GetPropertyEditor(); ArrayList orderedProperties = new ArrayList(); foreach (PropertyDescriptor prop in base.GetProperties(attributes)) { #region 控件、表单部分属性屏蔽 //屏蔽所有控件数据Category 数据 属性 if (((System.ComponentModel.MemberDescriptor)(prop)).Category == "数据" || ((System.ComponentModel.MemberDescriptor)(prop)).Category == "Data") { continue; } //屏蔽所有控件数据Category 杂项 属性 if (((System.ComponentModel.MemberDescriptor)(prop)).Category == "杂项" || ((System.ComponentModel.MemberDescriptor)(prop)).Category == "Misc") { continue; } #endregion #region 屏蔽不需要显示的属性 switch (((System.ComponentModel.MemberDescriptor)(prop)).Name) { case "DisplayStyleEnum": CommonHelpDisplayStyleEnum displayType = (CommonHelpDisplayStyleEnum)prop.GetValue(this); if (displayType == CommonHelpDisplayStyleEnum.TreeStyle) { showPageSize = false; } break; case "PageSize": if (!showPageSize) { continue; } break; } #endregion abEvent = prop.Attributes[typeof(PropertyIndexAttribute)]; if (abEvent != null && abEvent is PropertyIndexAttribute) { nowIndexCode = ((PropertyIndexAttribute)abEvent).IndexCode; #region 事件编辑器处理 if (nowIndexCode.Length > 6) { //最后三位000标识 不带编辑器 if (nowIndexCode.Substring(6, 3) == "000") { orderedProperties.Add(new PropertyOrderPair(prop.DisplayName, int.Parse(nowIndexCode), prop)); continue; } foreach (var cet in dictConfig) { if (cet.Key == nowIndexCode.Substring(6, 3)) { //根据配置文件的序列号,获取出全名HC.Test.Designer.UI.EventActionEditorControl,放入下面的函数中 editorControl = ReflectionActivator.GetType("HC.Test.Designer.UI", cet.Value.EditorName); if (editorControl == null) { orderedProperties.Add(new PropertyOrderPair(prop.DisplayName, int.Parse(nowIndexCode), prop)); break; } if (cet.Value.EditorType == "CommonTypeDropDownEditor") { editor = ReflectionActivator.GetGenericType("HC.Test.Common.Design", "HC.Test.Common.Design.GenericDropDownControlEditor`1", new Type[] { editorControl }); } else { editor = ReflectionActivator.GetGenericType("HC.Test.Common.Design", "HC.Test.Common.Design.ModalFormEditor`1", new Type[] { editorControl }); } des = new EventPropertyDescriptor(prop, editor); orderedProperties.Add(new PropertyOrderPair(prop.DisplayName, int.Parse(nowIndexCode), des)); break; } } } #endregion else { orderedProperties.Add(new PropertyOrderPair(prop.DisplayName, 0, prop)); continue; } } else { orderedProperties.Add(new PropertyOrderPair(prop.DisplayName, 0, prop)); } } //属性集合按照PropertyIndexAttribute及DisplayName排序 orderedProperties.Sort(); PropertyDescriptorCollection propsTemp = new PropertyDescriptorCollection(null); foreach (PropertyOrderPair pop in orderedProperties) { propsTemp.Add(pop.Property); } return propsTemp; //ArrayList propertyNames = new ArrayList(); //foreach (PropertyOrderPair pop in orderedProperties) //{ // propertyNames.Add(pop.Name); //} //return pdc.Sort((string[])propertyNames.ToArray(typeof(string))); } catch { throw; } } } /// <summary> /// 属性 描述(属性的属性) /// </summary> public class EventPropertyDescriptor : PropertyDescriptor { Type editor = null; PropertyDescriptor prop; public EventPropertyDescriptor(PropertyDescriptor descr, Type editor) : base(descr) { this.prop = descr; this.editor = editor; } /// <summary> /// 获取Editor; /// </summary> /// <param name="editorBaseType"></param> /// <returns></returns> public override object GetEditor(Type editorBaseType) { object obj = base.GetEditor(editorBaseType); if (obj == null) { obj = ReflectionActivator.CreateInstace(editor); } return obj; } public override bool CanResetValue(object component) { return prop.CanResetValue(component); } public override Type ComponentType { get { return prop.ComponentType; } } public override object GetValue(object component) { return prop.GetValue(component); } public override bool IsReadOnly { get { return prop.IsReadOnly; } } public override Type PropertyType { get { return prop.PropertyType; } } public override void ResetValue(object component) { prop.ResetValue(component); } public override void SetValue(object component, object value) { prop.SetValue(component, value); } public override bool ShouldSerializeValue(object component) { return prop.ShouldSerializeValue(component); } } }PropertyIndexAttribute类:
/// <summary> /// 属性信息的顺序分类等信息 /// 字段或者属性等 可序列化的信息上 /// 现在是一共9位,最后3位用来标识编辑器 最后三位000 默认不会识别编辑器 /// 中间三位用来属性排序 /// </summary> [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class PropertyIndexAttribute : Attribute { private string indexCode; /// <summary> /// 标识 /// </summary> public string IndexCode { get { return indexCode; } set { indexCode = value; } } /// <summary> /// /// </summary> /// <param name="indexCode"></param> public PropertyIndexAttribute(string indexCode) { this.indexCode = indexCode; } }排序部分:
#region Helper Class - PropertyOrderPair public class PropertyOrderPair : IComparable { private int _order; private string _name; public string Name { get { return _name; } } private PropertyDescriptor _property; public PropertyDescriptor Property { get { return _property; } } public PropertyOrderPair(string name, int order, PropertyDescriptor property) { _order = order; _name = name; _property = property; } 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 #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设置PropertyGrid控件的属性:
用法:
为每个属性添加属性:[PropertyIndex("103001000")]
比如:
[Category("掩码")] [Browsable(true)] [DisplayName("掩码类型")] //[Description("设置执行单击事件的快捷键")] [PropertyIndex("103001000")] public DevExpress.XtraEditors.Mask.MaskType MaskType { get { return this.BaseTextEdit.Properties.Mask.MaskType; } set { this.BaseTextEdit.Properties.Mask.MaskType = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("忽略空白")] [Description("对于 Simple、Regular 和 RegEx 掩码类型,MaskProperties.IgnoreMaskBlank 属性是有效的。 如果此属性值设置为 true,那么空白编辑器会失去焦点。 如果编辑器的取值仅部分完成,那么焦点不能被移出此编辑器,直至最终用户输入了完整的取值或者通过清除编辑器框而清除了取值。 如果此属性值设置为 false,那么焦点不能移出此编辑器,直至完整输入取值。 ")] [PropertyIndex("103006000")] public bool IgnoreMaskBlank { get { return this.BaseTextEdit.Properties.Mask.IgnoreMaskBlank; } set { this.BaseTextEdit.Properties.Mask.IgnoreMaskBlank = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("格式占位符")] [Description("对于 Simple、Regular 和 RegEx 掩码类型,使用由 MaskProperties.PlaceHolder 属性确定的特殊字符来呈现编辑框中的占位符。 可以使用该属性来改变默认的占位符 (“_”符)。 对于 RegEx 掩码类型,通过把 MaskProperties.ShowPlaceHolders 属性设置为 false,可以隐藏占位符。 ")] [PropertyIndex("103007000")] public char PlaceHolder { get { return this.BaseTextEdit.Properties.Mask.PlaceHolder; } set { this.BaseTextEdit.Properties.Mask.PlaceHolder = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("错误提示音")] [Description("对于所有类型的掩码,Boolean 型的 MaskProperties.BeepOnError 属性都是可用的。 把此属性设置为 true,当最终用户试图键入一个无效字符串时允许响铃。 假定使用了 Numeric 类型的掩码。 在这种情况下,最终用户每次试图键入非数字字符时,编辑器都将发出一段提示声音。")] [PropertyIndex("103005000")] public bool BeepOnError { get { return this.BaseTextEdit.Properties.Mask.BeepOnError; } set { this.BaseTextEdit.Properties.Mask.BeepOnError = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("自动填充")] [Description("对于 RegEx 掩码类型,可以启用自动完成功能。 在这种模式中,编辑器将尝试完成已经由最终用户部分输入的取值。")] [PropertyIndex("103004000")] public DevExpress.XtraEditors.Mask.AutoCompleteType AutoComplete { get { return this.BaseTextEdit.Properties.Mask.AutoComplete; } set { this.BaseTextEdit.Properties.Mask.AutoComplete = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("保存格式字符串")] [Description("对于 Simple 和 Regular 掩码类型,可以指定是否总是把显示的掩码字符 (原义字符) 包括在编辑器的取值内。 换句话说,你可以控制那些字符是否出现在由 BaseEdit.EditValue 属性返回的取值中。 要使这些字符无法被访问,则必须把 MaskProperties.SaveLiteral 属性设置为 false。 在这种情况下,如果显示的取值是“(555)123-76-34”,那么由 BaseEdit.EditValue 属性返回的取值就是“5551237634”。 ")] [PropertyIndex("103008000")] public bool SaveLiteral { get { return this.BaseTextEdit.Properties.Mask.SaveLiteral; } set { this.BaseTextEdit.Properties.Mask.SaveLiteral = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("掩码字符串")] [Description("掩码字符串标识了数据输入模板。 可以使用预定义的掩码字符串,或者构建自己的掩码表达式, 应该根据掩码类型来设置掩码字符串。")] [PropertyIndex("103002000")] public string EditMask { get { return this.BaseTextEdit.Properties.Mask.EditMask; } set { this.BaseTextEdit.Properties.Mask.EditMask = value; } } [Category("掩码")] [Browsable(true)] [DisplayName("显示格式占位符")] [Description("对于 Simple、Regular 和 RegEx 掩码类型,使用由 MaskProperties.PlaceHolder 属性确定的特殊字符来呈现编辑框中的占位符。 可以使用该属性来改变默认的占位符 (“_”符)。 对于 RegEx 掩码类型,通过把 MaskProperties.ShowPlaceHolders 属性设置为 false,可以隐藏占位符。 ")] [PropertyIndex("103003000")] public bool ShowPlaceHolders { get { return this.BaseTextEdit.Properties.Mask.ShowPlaceHolders; } set { this.BaseTextEdit.Properties.Mask.ShowPlaceHolders = value; } }效果:
如果使用网络上的sort排序代码(感觉不对,于是没有采用):
//ArrayList propertyNames = new ArrayList(); //foreach (PropertyOrderPair pop in orderedProperties) //{ // propertyNames.Add(pop.Name); //} //return pdc.Sort((string[])propertyNames.ToArray(typeof(string)));效果如下:
本文参考:
Ordering Items in the Property Grid
PropertyGrid类别排序实现,可以参考:
具体实现如下:
属性控件PropertyGrid事件:
#region 属性分组排序部分 private void propertyGrid_SelectedObjectsChanged(object sender, EventArgs e) { propertyGrid.Tag = propertyGrid.PropertySort; propertyGrid.PropertySort = PropertySort.CategorizedAlphabetical; propertyGrid.Paint += new PaintEventHandler(propertyGrid_Paint); } private void propertyGrid_Paint(object sender, PaintEventArgs e) { var categorysinfo = propertyGrid.SelectedObject.GetType().GetField("categorys", BindingFlags.NonPublic | BindingFlags.Instance); if (categorysinfo != null) { var categorys = categorysinfo.GetValue(propertyGrid.SelectedObject) as List<String>; propertyGrid.CollapseAllGridItems(); GridItemCollection currentPropEntries = typeof(PropertyGrid).GetField("currentPropEntries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(propertyGrid) as GridItemCollection; var newarray = currentPropEntries.Cast<GridItem>().OrderBy((t) => categorys.IndexOf(t.Label)).ToArray(); currentPropEntries.GetType().GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentPropEntries, newarray); propertyGrid.ExpandAllGridItems(); propertyGrid.PropertySort = (PropertySort)propertyGrid.Tag; } propertyGrid.Paint -= new PaintEventHandler(propertyGrid_Paint); }由于我用的是:
所以在反射的时候,用的是:
GridItemCollection currentPropEntries = typeof(PropertyGrid).GetField("currentPropEntries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(propertyGrid) as GridItemCollection;而非参考文章中的:
GridItemCollection currentPropEntries = propertyGrid1.GetType().GetField("currentPropEntries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(propertyGrid1) as GridItemCollection;
控件中的使用:
效果:
完成!