Внедрение 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 для неиндексированной коллекции.