Реализация INotifyCollectionChanged для коллекции без индексов - PullRequest
12 голосов
/ 06 июня 2011

Я только сейчас промочил пальцы в WPF после нескольких лет работы исключительно в ASP.Net.Проблема, с которой я сейчас борюсь, заключается в том, что у меня есть собственный класс коллекции, который мне нужно привязать к списку.Кажется, все работает, за исключением удаления элемента из коллекции.Когда я пытаюсь получить сообщение об ошибке: “Collection Remove event must specify item position.” Проблема в том, что в этой коллекции не используются индексы, поэтому я не вижу способа указать позицию, и до сих пор Google не смог показать мне работоспособное решение ...

Класс определен для реализации ICollection<> и INotifyCollectionChanged.Мой внутренний контейнер элементов - это Dictionary, который использует значение имени (строки) элемента для ключа.Помимо методов, определенных этими двумя интерфейсами, в этой коллекции есть индексатор, который позволяет элементам получать доступ по имени, и переопределяет методы Contains и Remove, чтобы их также можно было вызывать с именем элемента.Это работает для добавления и редактирования, но выдает вышеупомянутое исключение, когда я пытаюсь удалить.

Вот выдержка из соответствующего кода:

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
}

Ответы [ 4 ]

9 голосов
/ 08 июня 2011

Ваша пользовательская коллекция выглядит как переосмысление KeyedCollection<TKey,TItem>, в котором внутренне используется словарь, и имеют индексы. Индексатор для int индексов может быть скрыт, если TKey это int или int на основе перечисления, но это можно исправить .

Что касается KeyedCollection работы с WPF, я обнаружил эту статью , в которой он в основном делает ObservableKeyedCollection<TKey,TItem> путем реализации INotifyCollectionChanged и переопределения SetItem(), InsertItem(), ClearItems() и RemoveItem() вместе с добавлением AddRange() и передачей Func<TItem,TKey> в конструктор для получения TKey из TItem.

2 голосов
/ 08 июня 2011

Итак, я добавил временный хак, изменив событие удаления на сброс, и начал работать над некоторыми другими областями своего кода. Когда я вернулся к этой проблеме, я обнаружил / понял, что класс SortedList удовлетворит мои требования и позволит мне правильно реализовать события Collection Changed с минимальными изменениями в моем существующем коде.

Для тех, кто не знаком с этим классом (я никогда не использовал его раньше), вот краткое резюме, основанное на чтении, которое я сделал до сих пор. В большинстве случаев он ведет себя как словарь, хотя его внутренняя структура отличается. В этой коллекции хранятся отсортированные списки ключей и значений вместо хеш-таблицы. Это означает, что с получением данных в коллекцию и из нее затрачивается немного больше времени, но ее потребление памяти ниже. Насколько заметна эта разница, зависит от того, сколько данных вам нужно хранить и какие типы данных вы используете для своих ключей.

Поскольку мой объем данных в этом случае относительно мал, и мне нужно отсортировать элементы в списке по их именам, использование этого класса является хорошим ответом в моем случае. Если у кого-то есть аргумент, почему этот класс не следует использовать, пожалуйста, дайте мне знать.

Спасибо всем за их предложения и комментарии, надеюсь, эта ветка поможет кому-то еще в пути.

2 голосов
/ 06 июня 2011

Принимает немного косвенности, но вы можете сделать это с Linq. Не включая обработку ошибок, вы можете сделать это:

var items = dict.Keys.Select((k, i) => new { idx = i, key = k });
var index = items.FirstOrDefault(f => f.key == name).idx;

Вы также можете использовать значения вместо ключей, если вы остаетесь последовательными.

0 голосов
/ 19 октября 2017

Мне удалось использовать действие NotifyCollectionChangedAction.Replace с пустым списком NewItems, чтобы успешно вызвать событие CollectionChanged для неиндексированной коллекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...