PropertyGrid控制元件 分類(Category)及屬性(Property)排序

衣舞晨風發表於2015-11-05

最近在做表單設計器,設計器上的控制元件都是我們自己封裝的,但每個屬性類別裡的屬性是按照屬性的拼音排序的,現在想按照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)));
效果如下:



本文參考:

PropertyGrid排序

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;


控制元件中的使用:



效果:


完成!


相關文章