使用 C# PropertyGrid 动态显示属性





4.00/5 (2投票s)
使用 PropertyGrid 显示对象的运行时属性
引言
普通的 C# PropertyGrid
在您将其选择到 PropertyGrid
对象并显示时,足以显示对象的属性。PropertyGrid
足够智能,可以知道属性的数据类型,并以文本框或组合框的形式显示属性值。在这里,我想向您展示一个如何使用它来显示在运行时添加的属性的示例。
Using the Code
这是 MSDN 建议的使用 PropertyGrid
类的方法
- 定义您的对象并使用属性描述
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 …*/ }
- 将您的对象选择到
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
对象。最重要的部分是重新实现 ICustomTypeDescriptor
、PropertyDescriptor
和 UITypeEditor
中提供的可重写函数。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();
}
}
请查看附件中的示例以查看完整的源代码。