我见过的所有PropertyGrid示例都允许用户编辑单个对象,PropertyGrid通过反射进行扫描。我希望用户能够编辑例如ini文件或普通旧词典,每个键值对一行。这可能吗?
答案 0 :(得分:5)
是。几年前,我写了some code以在IDictionary
中显示PropertyGrid
。
答案 1 :(得分:3)
这是一个从Roger链接到的代码派生的完整示例。我更新了它以便
Dictionary<GridProperty,object>
代替IDictionary GridProperty
指定名称,类别,描述等。(注意,这篇文章已经改变了原来的设计。)谢谢罗杰!
public partial class PropertyEditor : Form
{
public PropertyEditor()
{
InitializeComponent();
var dict = new Dictionary<GridProperty, object>();
dict["Food"] = "Poutine";
dict["Ball"] = "Football";
dict[new GridProperty("1. Greeting", "Words", "The first word to say")] = "Hello";
dict[new GridProperty("2. Subject", "Words", "The second word to say")] = "Dogs";
dict[new GridProperty("3. Verb", "Words", "The third word to say")] = "Like";
dict[new GridProperty("4. Object", "Words", "The fourth word to say")] = "Burritos";
dict[new GridProperty("Integer", "Types", "")] = 42;
dict[new GridProperty("Double", "Types", "")] = 42.5;
dict[new GridProperty("Color", "Types", "")] = Color.ForestGreen;
propertyGrid1.SelectedObject = new DictionaryPropertyGridAdapter(dict, "Stuff");
}
}
/// <summary>
/// Holds information about a property in a Dictionary-based PropertyGrid
/// </summary>
public class GridProperty
{
public GridProperty(string name)
{ Name = name; }
public GridProperty(string name, string category)
{ Name = name; Category = category; }
public GridProperty(string name, string category, string description)
{ Name = name; Category = category; Description = description; }
public string Name { get; private set; }
public string Category { get; private set; }
public string Description { get; set; }
public bool IsReadOnly { get; set; }
public object DefaultValue { get; set; } // shown if value is null
public static implicit operator GridProperty(string name) { return new GridProperty(name); }
}
/// <summary>An object that wraps a dictionary so that it can be used as the
/// SelectedObject property of a standard PropertyGrid control.</summary>
/// <example>
/// propertyGrid.SelectedObject = new DictionaryPropertyGridAdapter(dict, "");
/// </example>
public class DictionaryPropertyGridAdapter : ICustomTypeDescriptor
{
internal IDictionary<GridProperty, object> _dictionary;
internal string _defaultCategory;
public DictionaryPropertyGridAdapter(Dictionary<GridProperty, object> dict, string defaultCategory)
{
_dictionary = dict;
_defaultCategory = defaultCategory;
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var props = new PropertyDescriptor[_dictionary.Count];
int i = 0;
foreach (var prop in _dictionary)
props[i++] = new GridPropertyDescriptor(prop.Key, this);
return new PropertyDescriptorCollection(props);
}
#region Boilerplate
#region Never called
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
return GetProperties(null);
}
#endregion
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _dictionary;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
#endregion
class GridPropertyDescriptor : PropertyDescriptor
{
GridProperty _prop;
DictionaryPropertyGridAdapter _parent;
internal GridPropertyDescriptor(GridProperty prop, DictionaryPropertyGridAdapter parent)
: base(prop.Name, null)
{
_prop = prop;
_parent = parent;
}
public override string Description
{
get { return _prop.Description; }
}
public override string Category
{
get { return _prop.Category ?? _parent._defaultCategory; }
}
public override Type PropertyType
{
get { return (_parent._dictionary[_prop] ?? _prop.DefaultValue ?? "").GetType(); }
}
public override void SetValue(object component, object value)
{
_parent._dictionary[_prop] = value;
}
public override object GetValue(object component)
{
return _parent._dictionary[_prop];
}
public override bool IsReadOnly
{
get { return _prop.IsReadOnly; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return _prop.DefaultValue != null;
}
public override void ResetValue(object component)
{
SetValue(component, _prop.DefaultValue);
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
答案 2 :(得分:2)
PropertyGrid
将允许编辑任何具有get和set访问器的属性或附加到其上的适当编辑器或其提供的类型,描述如何转换甚至编辑该值。
因此,如果您公开一个属性,例如,一个INI文件的Stream
并附加一个自定义TypeConverter
,将其扩展为其中的名称/值对,您确实可以使用PropertyGrid
编辑INI文件。
有用的链接:
使用TypeConverterAttribute
声明将类型转换器附加到类型。类型转换器允许您提供有关如何将类型转换为其他类型和从其他类型转换类型的规则。我们提供了一系列覆盖来定制您的转化,所有这些都以Convert
开头。
通过各种GetPropertiesxxxx
调用,类型转换器还允许您指定您的类型可以编辑的属性以及它们的显示方式(例如,它们的名称)。这样可以扩展值(如编辑Point
类型)并根据值的状态显示或隐藏属性(例如,您的INI文件将使用它来显示或隐藏基于的属性文件的内容)。
类型转换器还允许您在编辑时指定类型可以显示为下拉列表的值列表。这是由GetStandardValuesXxx
覆盖集提供的,如果您不想创建自定义编辑器但是您有一个固定的允许值列表,则可能很有用。
编辑器允许您微调编辑类型实例的设计时体验。它们可以附加到属性或类型,并在编辑值时向PropertyGrid
指示要使用的编辑器。这允许您使用一些自定义用户界面(例如,滑块)显示对话框或您自己的下拉列表。