And*_*ker 10 .net c# propertygrid typeconverter propertydescriptor
我希望自动显示每个IList在我的可扩展PropertyGrid(通过"可扩展",我显然意味着将显示项目).我不想在每个列表上使用属性(再次,我希望它适用于每个列表IList)
我试图通过使用自定义PropertyDescriptor和a来实现它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);
}
}
Run Code Online (Sandbox Code Playgroud)
.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我用IList以下几行将它用于所有s:
TypeDescriptor.AddAttributes(typeof (IList), new TypeConverterAttribute(typeof(ExpandableCollectionConverter)));
Run Code Online (Sandbox Code Playgroud)
一些澄清
我希望在更改列表时网格自动更新.当另一个属性发生变化时刷新并没有帮助.
一个有效的解决方案是一个解决方案,其中:
ArgumentOutOfRangeException因为它试图显示已删除的项目PropertyGrid应该更改集合重要编辑:
我还是设法使扩大收藏与更新Reflection,并呼吁NotifyValueGivenParenton方法context的对象时PropertyDescriptorGetValue方法被调用(当RefreshRequired事件引发的):
var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
Run Code Online (Sandbox Code Playgroud)
它完美地工作,除了它导致事件被无限次地提升,因为调用NotifyValueGivenParent导致重新加载,因此PropertyDescriptor,引发事件,等等.
我试图通过添加一个简单的标志来解决它,如果它已经重新加载将阻止重新加载,但由于某种原因,NotifyValueGivenParent行为是异步的,因此重新加载发生在标志关闭后.也许这是另一个探索的方向.唯一的问题是递归
没有必要使用ObservableCollection. 您可以按如下方式修改描述符类:
public class ExpandableCollectionPropertyDescriptor : PropertyDescriptor
{
private IList collection;
private readonly int _index;
public ExpandableCollectionPropertyDescriptor(IList coll, int idx)
: base(GetDisplayName(coll, idx), null)
{
collection = coll;
_index = idx;
}
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 bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return this.collection.GetType(); }
}
public override object GetValue(object component)
{
return collection[_index];
}
public override bool IsReadOnly
{
get { return false; }
}
public override string Name
{
get { return _index.ToString(CultureInfo.InvariantCulture); }
}
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;
}
}
Run Code Online (Sandbox Code Playgroud)
ExpandableCollectionConverter我将派生该类,而不是CollectionConverter派生类,因此您仍然可以使用省略号按钮以旧方式编辑集合(因此,如果集合不是只读的,您可以添加/删除项目):
public class ListConverter : CollectionConverter
{
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
IList list = value as IList;
if (list == null || list.Count == 0)
return base.GetProperties(context, value, attributes);
var items = new PropertyDescriptorCollection(null);
for (int i = 0; i < list.Count; i++)
{
object item = list[i];
items.Add(new ExpandableCollectionPropertyDescriptor(list, i));
}
return items;
}
}
Run Code Online (Sandbox Code Playgroud)
我会ListConverter在我想要查看可扩展列表的属性上使用它。当然,您通常可以像在示例中那样注册类型转换器,但这会覆盖所有内容,这可能不是总体意图。
public class MyClass
{
[TypeConverter(typeof(ListConverter))]
public List<int> List { get; set; }
public MyClass()
{
List = new List<int>();
}
[RefreshProperties(RefreshProperties.All)]
[Description("Change this property to regenerate the List")]
public int Count
{
get { return List.Count; }
set { List = Enumerable.Range(1, value).ToList(); }
}
}
Run Code Online (Sandbox Code Playgroud)
重要提示:RefreshProperties应该为更改其他属性的属性定义属性。在此示例中,更改Count将替换整个列表。
使用它会propertyGrid1.SelectedObject = new MyClass();产生以下结果: