65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# PropertyGrid 动态显示属性

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2016年1月20日

CPOL

1分钟阅读

viewsIcon

21235

downloadIcon

626

使用 PropertyGrid 显示对象的运行时属性

引言

普通的 C# PropertyGrid 在您将其选择到 PropertyGrid 对象并显示时,足以显示对象的属性。PropertyGrid 足够智能,可以知道属性的数据类型,并以文本框或组合框的形式显示属性值。在这里,我想向您展示一个如何使用它来显示在运行时添加的属性的示例。

Using the Code

这是 MSDN 建议的使用 PropertyGrid 类的方法

  1. 定义您的对象并使用属性描述
    class  BOOK
    {
             [BrowsableAttribute(false)]
          public int book_id { get;set}
            [BrowsableAttribute(true)]
            [Description("Book’s title")]
            [DisplayName("Title")]
          public string title {get;set}
            [Description("Book’s author")]
            [DisplayName("Author")]
          public string author {get;set}
     [Description("Book’s price in USD")]
            [DisplayName("Price($)")]
          public float price {get;set}
     public BOOK() //constructor
          {
          }
          /*methods …*/
    }
  2. 将您的对象选择到 PropertyGrid 实例并显示它
    public Form1() {
       // The initial constructor code goes here.
       PropertyGrid propertyGrid1 = new PropertyGrid();
       propertyGrid1.CommandsVisibleIfAvailable = true;
       propertyGrid1.Location = new Point(10, 20);
       propertyGrid1.Size = new System.Drawing.Size(400, 300);
       propertyGrid1.TabIndex = 1;
       propertyGrid1.Text = "Property Grid";
       this.Controls.Add(propertyGrid1);
     BOOK book1 = new BOOK();
       propertyGrid1.SelectedObject = book1;
    }

该项目需要使用 C# propertyGrid 来显示/修改对象的属性,但对象属性在设计时未预定义,例如,对象的属性在目录容器中,该容器是根据不同的需求动态生成的,如下面的 BOOK 类所示

public class UserProperty; //user defined property type
/// <summary>
    /// /
    /// </summary>
    public class LocalProperty
    {
        public string Name{get;set;}
        public string Group{get;set;}  //group name
        public string DisplayName{get;set;} //display name
        public Boolean Display {get;set;} //true to show
        public Boolean Readonly {get;set;} //true is readonly
        public string  Description {get;set;}
        public object  Value {get;set}
        public List<object> Options = new List<object>();
        public LocalProperty(string name, string displayname,
                            string description, object value)
        {
            Display = true;
            Readonly = false;
            //
            Name = name;
                Group = “general”;
            DisplayName = displayname;
            Description = description;
  Value = value;
        }
        public object ValueEditor()
        {
            //return its editor
                 //null to use system provided editor
            if(_value.GetType().BaseType == typeof(UserProperty)) {
                return  (_value as UserProperty).Editor;
            }
            return null;
        }
         }
    public class LocalProperties : Dictionary<string, LocalProperty>
    {
    }
class  BOOK
{
         LocalProperties properties = new LocalProperties();
public BOOK() //constructor
      {
      }
      /*methods …*/
}
public Form1() {
   // The initial constructor code goes here.
   PropertyGrid propertyGrid1 = new PropertyGrid();
   propertyGrid1.CommandsVisibleIfAvailable = true;
   propertyGrid1.Location = new Point(10, 20);
   propertyGrid1.Size = new System.Drawing.Size(400, 300);
   propertyGrid1.TabIndex = 1;
   propertyGrid1.Text = "Property Grid";
   this.Controls.Add(propertyGrid1);
 BOOK book1 = new BOOK();
      book1.poperties[“title”] =
           new LocalProperty(“title”, “Title”, 
           “Book’s title”, true, “C# for beginner”);
      book1.properties[“price”] =
            new LocalProperty(“price”,”Price($)”, 
            “Book’s price in USD”, true,“$19”);
   propertyGrid1.SelectedObject = book1;
}

为了实现这种功能,我们需要基于一些 PropertyGrid 接口类来实现我们自己的 PropertyGrid 对象。最重要的部分是重新实现 ICustomTypeDescriptorPropertyDescriptorUITypeEditor 中提供的可重写函数。UITypeEditor 用于定义用户特定的属性编辑器,例如对话框或窗体。

 /// <summary>
        /// ////
        /// </summary>
        private class DynamicPropertyDescriptor : PropertyDescriptor
        {
            private readonly LocalPropertyGridObject _propertyGridObject;
            private readonly LocalProperty _propertyObject;
            private readonly Type _propertyType;
            public DynamicPropertyDescriptor(LocalPropertyGridObject propertyGridObject,
                string propertyName, Type propertyType, LocalProperty propertyObject)
                : base(propertyName, null)
            {
                this._propertyGridObject = propertyGridObject;
                this._propertyObject = propertyObject;
                this._propertyType = propertyType;
                //
                var attributes = new List<Attribute>();
                if (propertyObject.GetOptions().Count > 0
                    && !(_propertyObject.ValueType() == typeof(DateTime)
                          ||_propertyObject.ValueType() == typeof(System.Drawing.Color)))
                {
                    //don't use this for the Datetime and color value
                    // system will create them by default.
                    attributes.Add(new typeConverterAttribute
                    (typeof(OptionTypeConverter))); //this enable to use ComboBox
                }
                attributes.Add(new DisplayNameAttribute(_propertyObject.DisplayName==null?
                                                _propertyObject.Name: _propertyObject.DisplayName));
                attributes.Add(new CategoryAttribute(_propertyObject.Group));
                attributes.Add(new BrowsableAttribute(_propertyObject.Display));
                attributes.Add(new DescriptionAttribute(_propertyObject.Description));
                if (_propertyObject.ValueType().BaseType == typeof(UserProperty))
                {
                    attributes.Add(new EditorAttribute(typeof(UserPropertyEditor), 
                    typeof(UITypeEditor))); //this attribute enable to use user defined editor 
                                            // (dialog box)
                }
                this.AttributeArray = attributes.ToArray();
            }
}
public class LocalPropertyGridObject : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
{
        private LocalPropertyGrid _owner;
        private LocalProperties _properties;
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            var memberName = binder.Name;
            LocalProperty _p;
            if (_properties.TryGetValue(memberName, out _p))
            {
                result = _p.Value;
                return true;
            }
            result = null;
            return false;
        }
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var memberName = binder.Name;
            _properties[memberName].Value = value.ToString();
            NotifyToRefreshAllProperties();
            return true;
        }
        public PropertyDescriptorCollection GetProperties()
        {
            // of course, here must be the attributes associated
            // with each of the dynamic properties
             var properties = _properties
                .Select(pair => new DynamicPropertyDescriptor(this,
                    pair.Key, pair.Value.Value.GetType(), pair.Value));
            return new PropertyDescriptorCollection(properties.ToArray());
        }
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return this.GetProperties();
        }
}
        /// </summary>
        private class UserPropertyEditor : UITypeEditor
        {
            public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
            {
                return UITypeEditorEditStyle.Modal;
            }
public override object EditValue
(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
            {
                IWindowsFormsEditorService svc = provider.GetService
                (typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
                DynamicPropertyDescriptor d = context.PropertyDescriptor as DynamicPropertyDescriptor;
                if (d != null)
                {
                     //create the User Editor Instance and display as a model Dialog
                    LocalPropertyEditor user_editor = System.Activator.CreateInstance(
                                 d.GetPropertyObject().ValueEditor().GetType(),
                                 new object[] { value }
                                ) as LocalPropertyEditor;
                    if (svc != null && user_editor != null)
                    {
                        LocalPropertyGridObject localpropertyobject
                            = context.Instance as LocalPropertyGridObject;
                        if (localpropertyobject != null)
                        {
                            user_editor.StartPosition = FormStartPosition.Manual;
                        using (Form form = user_editor as Form)
                        {
                            if (svc.ShowDialog(form) == DialogResult.OK)
                            {
                                //foo.Bar = form.Value; // update object
                                value = user_editor.Value;
                            }
                        }
                    }
                }
                return value; // can also replace the wrapper object here
            }

用户定义的属性编辑器应从类 "LocalPropertyEditor" 继承,并带有接受属性值的构造函数和在关闭前设置 DialogResult = OK 的回调函数。

class  YourPropertyEditor: LocalPropertyEditor
{
      YourPropertyEditor(string value);
       /// <summary>
        ///
        /// </summary>
        public void OnOK(EventArgs e)
        {
            this.DialogResult = DialogResult.OK;
            this.Close();
        }
}

请查看附件中的示例以查看完整的源代码。

© . All rights reserved.