Внедрение INotifyCollectionChanged в коллекции без индексов
Я только сейчас получаю мочи в WPF после нескольких лет работы в ASP.Net исключительно. Проблема, с которой я в настоящее время борется, заключается в том, что у меня есть собственный класс коллекции, который мне нужно связать с списком. Кажется, что все работает, кроме как удалить элемент из коллекции. Когда я пытаюсь получить ошибку: "Collection Remove event must specify item position."
Уловка заключается в том, что эта коллекция не использует индексы, поэтому я не вижу способа указать позицию, и пока Google не смог показать мне работоспособное решение...
Класс определен для реализации ICollection<>
и INotifyCollectionChanged
. Мой контейнер внутренних элементов - это Dictionary
, который использует значение Name (string) для ключа. Помимо методов, определенных этими двумя интерфейсами, эта коллекция имеет индекс, который позволяет элементам получать доступ по имени и переопределяет для методов Contains
и Remove
, чтобы их также можно было вызвать с помощью элемента Name. Это работает над добавлением и редактированием, но при попытке удалить это исключение вызывается выше.
Вот выдержка из соответствующего кода:
class Foo
{
public string Name
{
get;
set;
}
}
class FooCollection : ICollection<Foo>, INotifyCollectionChanged
{
Dictionary<string, Foo> Items;
public FooCollection()
{
Items = new Dictionary<string, Foo>();
}
#region ICollection<Foo> Members
//***REMOVED FOR BREVITY***
public bool Remove(Foo item)
{
return this.Remove(item.Name);
}
public bool Remove(string name)
{
bool Value = this.Contains(name);
if (Value)
{
NotifyCollectionChangedEventArgs E = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, Items[name]);
Value = Items.Remove(name);
if (Value)
{
RaiseCollectionChanged(E);
}
}
return Value;
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
#endregion
}
Ответы
Ответ 1
Ваша пользовательская коллекция выглядит как повторное изобретение KeyedCollection<TKey,TItem>
, которое внутренне использует словарь и имеет индексы. Индексатор для индексов int
может быть скрыт, если TKey
есть int
или int
, но это может быть исправлено.
Что касается создания KeyedCollection
работы с WPF, я нашел эту статью, в которой он в основном делает ObservableKeyedCollection<TKey,TItem>
, реализуя INotifyCollectionChanged
и переопределить SetItem()
, InsertItem()
, ClearItems()
и RemoveItem()
, а также добавить AddRange()
и передать Func<TItem,TKey>
конструктору для получения TKey
от a TItem
.
Ответ 2
Делает небольшую косвенность, но вы можете сделать это с Linq. Не считая обработки ошибок, вы можете сделать это:
var items = dict.Keys.Select((k, i) => new { idx = i, key = k });
var index = items.FirstOrDefault(f => f.key == name).idx;
Вы также можете использовать значения вместо ключей, пока вы остаетесь неизменными.
Ответ 3
Итак, я бросил временный взлом, изменив событие удаления на reset и ушел, чтобы работать над некоторыми другими областями моего кода. Когда я вернулся к этой проблеме, я обнаружил/понял, что класс SortedList удовлетворит мои требования и позволит мне правильно реализовать события с изменением коллекции с минимальными изменения в моем существующем коде.
Для тех, кто не знаком с этим классом (я никогда раньше не использовал его), вот краткое резюме, основанное на чтении, которое я сделал до сих пор. В большинстве случаев он, похоже, ведет себя как словарь, хотя внутренняя структура отличается. Эта коллекция поддерживает отсортированные списки ключей и значений вместо хеш-таблицы. Это означает, что в сбор данных и из коллекции есть немного больше накладных расходов, но потребление памяти ниже. Насколько заметна эта разница, похоже, зависит от того, сколько данных вам нужно хранить и какие типы данных вы используете для своих ключей.
Так как мой объем данных в этом экземпляре относительно низок, и мне нужно, чтобы элементы в списке сортировались по их значениям имен, использование этого класса представляется хорошим ответом в моем случае. Если у кого-то есть аргумент, почему этот класс не должен использоваться, сообщите мне.
Спасибо всем за их предложения и комментарии, надеюсь, эта тема помогает кому-то еще на этом пути.
Ответ 4
Мне удалось использовать действие NotifyCollectionChangedAction.Replace
с пустым NewItems
списком, чтобы поднять событие CollectionChanged
для неиндексированной коллекции.