Я хочу автоматически показывать каждый IList
как расширяемый в моем PropertyGrid
(Под «расширяемым» я, очевидно, подразумеваю, что элементы будут показаны). Я не хочу, чтобы использовать атрибуты в каждом списке (Еще раз, я хочу, чтобы работать на КАЖДОМ IList
)PropertyGrid расширяемая коллекция
Я попытался его для того чтобы достигнуть с помощью пользовательского 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;
}
}
И я использую его для всех IList
с со следующей строкой:
TypeDescriptor.AddAttributes(typeof (IList), new TypeConverterAttribute(typeof(ExpandableCollectionConverter)));
Некоторые Разъяснения
Я хочу, чтобы сетка автоматически обновлять при изменении списка. Обновление, когда меняется другое свойство, не помогает.
Решение, которое работает, это решение, в котором:
- Если развернуть список, пока он пуст, а затем добавить элементы, сетка обновляется с деталями расширен
- Если вы добавляете элементы в список, разверните его, а затем удалите элементы (без коллапса), сетка обновится с расширенными элементами и не выбрасывает
ArgumentOutOfRangeException
, потому что пытается показать уже удаленные элементы - Я хочу, чтобы все это для утилита конфигурации. Только
PropertyGrid
следует изменить коллекции
ВАЖНО EDIT:
мне удалось сделать расширенные коллекции обновления с Reflection
и призывающих 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
ведет себя асинхронно, и поэтому перезагрузка происходит после выключения флага. Возможно, это еще одно направление для изучения.Единственная проблема заключается в рекурсии
Почему бы вам просто не называть 'TypeDescriptor.AddAttributes (typeof (IList), новый TypeConverterAttribute (typeof (ExpandableObjectConverter)));' вместо вашего пользовательского класса? –
@SimonMourier, потому что тогда я не вижу элементы в коллекции, но свойства 'Capacity' и' Count' –
Это требование не появляется в вашем вопросе. BTW, он работает для меня с свойством типа ArrayList. Я полагаю, это зависит от класса в SelectedObject. вы должны заполнить свой вопрос со всем соответствующим кодом и полным вопросом. –