我想IList
在我的PropertyGrid
(通过“可扩展”,我显然是指将显示这些项目)中自动将每个显示为可扩展。我不想在每个列表上使用属性(再一次,我希望它适用于每个列表IList
)
我试图通过使用自定义PropertyDescriptor
和ExpandableObjectConverter
. 它可以工作,但是在我从列表中删除项目后,PropertyGrid
它没有被刷新,仍然显示已删除的项目。
我尝试ObservableCollection
与 raiseOnComponentChanged
和RefreshProperties
attribute 一起使用,但没有任何效果。
这是我的代码:
public class ExpandableCollectionPropertyDescriptor : PropertyDescriptor
{
private IList _collection;
private readonly int _index = -1;
internal event EventHandler RefreshRequired;
public ExpandableCollectionPropertyDescriptor(IList coll, int idx) : base(GetDisplayName(coll, idx), null)
{
_collection = coll
_index = idx;
}
public override bool SupportsChangeEvents
{
get { return true; }
}
private static string GetDisplayName(IList list, int index)
{
return "[" + index + "] " + CSharpName(list[index].GetType());
}
private static string CSharpName(Type type)
{
var sb = new StringBuilder();
var name = type.Name;
if (!type.IsGenericType)
return name;
sb.Append(name.Substring(0, name.IndexOf('`')));
sb.Append("<");
sb.Append(string.Join(", ", type.GetGenericArguments()
.Select(CSharpName)));
sb.Append(">");
return sb.ToString();
}
public override AttributeCollection Attributes
{
get
{
return new AttributeCollection(null);
}
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get
{
return _collection.GetType();
}
}
public override object GetValue(object component)
{
OnRefreshRequired();
return _collection[_index];
}
public override bool IsReadOnly
{
get { return false; }
}
public override string Name
{
get { return _index.ToString(); }
}
public override Type PropertyType
{
get { return _collection[_index].GetType(); }
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
public override void SetValue(object component, object value)
{
_collection[_index] = value;
}
protected virtual void OnRefreshRequired()
{
var handler = RefreshRequired;
if (handler != null) handler(this, EventArgs.Empty);
}
}
.
internal class ExpandableCollectionConverter : ExpandableObjectConverter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
{
if (destType == typeof(string))
{
return "(Collection)";
}
return base.ConvertTo(context, culture, value, destType);
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
IList collection = value as IList;
PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
for (int i = 0; i < collection.Count; i++)
{
ExpandableCollectionPropertyDescriptor pd = new ExpandableCollectionPropertyDescriptor(collection, i);
pd.RefreshRequired += (sender, args) =>
{
var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
};
pds.Add(pd);
}
// return the property descriptor Collection
return pds;
}
}
我将它用于IList
以下行的所有 s:
TypeDescriptor.AddAttributes(typeof (IList), new TypeConverterAttribute(typeof(ExpandableCollectionConverter)));
一些说明
我希望网格在更改列表时自动更新。当另一个属性发生变化时刷新,没有帮助。
一个有效的解决方案是一个解决方案,其中:
- 如果在列表为空时展开列表,然后添加项目,则网格会刷新并显示展开的项目
- 如果您将项目添加到列表中,展开它,然后删除项目(不折叠),网格会刷新并扩展项目,而不是抛出
ArgumentOutOfRangeException
,因为它试图显示已删除的项目 - 我想要一个配置实用程序的全部内容。只有
PropertyGrid
应该更改集合
重要编辑:
我确实设法使扩展集合更新Reflection
,并在调用GetValue 方法时(引发事件时)NotifyValueGivenParent
在context
对象上调用方法:PropertyDescriptor
RefreshRequired
var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
它完美地工作,除了它会导致事件被无限次引发,因为调用NotifyValueGivenParent
会导致重新加载,PropertyDescriptor
因此引发事件等等。
我试图通过添加一个简单的标志来解决它,如果它已经重新加载,它将阻止重新加载,但由于某种原因,它的NotifyValueGivenParent
行为是异步的,因此重新加载会在标志关闭后发生。也许这是另一个探索的方向。唯一的问题是递归