【发布时间】:2015-12-11 11:59:38
【问题描述】:
我想在我的PropertyGrid 中自动将每个IList 显示为可扩展(通过“可扩展”,我显然是指将显示这些项目)。
我不想在每个列表上都使用属性(再一次,我希望它适用于每个IList)
我尝试通过使用自定义 PropertyDescriptor 和 ExpandableObjectConverter 来实现它。它可以工作,但是在我从列表中删除项目后,PropertyGrid 没有被刷新,仍然显示已删除的项目。
我尝试使用ObservableCollection 以及提升OnComponentChanged 和RefreshProperties 属性,但没有任何效果。
这是我的代码:
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;
}
}
我将它用于所有ILists,并带有以下行:
TypeDescriptor.AddAttributes(typeof (IList), new TypeConverterAttribute(typeof(ExpandableCollectionConverter)));
一些说明
我希望网格在我更改列表时自动更新。当另一个属性发生变化时刷新,没有帮助。
有效的解决方案是:
- 如果您在列表为空时展开列表,然后添加项目,则网格会随着展开的项目刷新
- 如果您将项目添加到列表中,展开它,然后删除项目(不折叠),网格会随着项目展开刷新,而不是抛出
ArgumentOutOfRangeException,因为它试图显示已删除的项目 - 我想要一个配置实用程序的全部内容。只有
PropertyGrid应该更改集合
重要编辑:
我确实设法使用Reflection 更新扩展集合,并在调用PropertyDescriptor GetValue 方法时(引发RefreshRequired 事件时)在context 对象上调用NotifyValueGivenParent 方法:
var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
它工作得很好,除了它会导致事件无限次引发,因为调用NotifyValueGivenParent 会导致重新加载PropertyDescriptor,然后引发事件等等。
我试图通过添加一个简单的标志来解决它,如果它已经重新加载,它将阻止重新加载,但由于某种原因NotifyValueGivenParent 的行为是异步的,因此重新加载会在标志关闭后发生。
也许这是另一个探索的方向。唯一的问题是递归
【问题讨论】:
-
为什么不直接调用
TypeDescriptor.AddAttributes(typeof(IList), new TypeConverterAttribute(typeof(ExpandableObjectConverter)));而不是自定义类? -
@SimonMourier 因为那时我看不到集合中的项目,但
Capacity和Count属性 -
此要求未出现在您的问题中。顺便说一句,它适用于我的 ArrayList 类型的属性。我想这取决于 SelectedObject 中的类。你应该用所有相关的代码和完整的问题来填写你的问题。
-
@SimonMourier 我虽然很明显我的意图是展示这些项目。 (我编辑了问题以说明这一点)
-
嗨,你可以给 ownerGrid 属性提供反射,而不是 notifyParent 方法,史蒂夫梅德利在这里建议link。在这里工作得很好。
标签: c# .net propertygrid typeconverter propertydescriptor