Listbox не обновляет содержимое ItemsSource

У меня странная проблема с ListBox: я связал его с коллекцией, которая не реализует INotifyCollectionChanged (невозможно, поскольку элементы создаются динамически по запросу):

IEnumerator IEnumerable.GetEnumerator()
{
    foreach (var metaData in Metadata.Where((m) => m.IsEnabled))
    {
        yield return new DynamicPropertyValue(this, metaData);
    }
}

Поэтому, когда IsVisible из ListBox изменяется, я пытаюсь принудительно обновить ItemsSource, установив для него значение Null, а затем обновить привязку:

static void ItemsControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (true.Equals(e.NewValue))
    {
        var element = (ItemsControl)sender;
        var bindingExpression = BindingOperations.GetBindingExpression(element, ItemsControl.ItemsSourceProperty);
        if (bindingExpression != null)
        {
            element.SetCurrentValue(ItemsControl.ItemsSourceProperty, null);
            bindingExpression.UpdateTarget();

            var count = element.ItemsSource.OfType<object>().Count();
            if (count != element.Items.Count)
            {
                Debug.WriteLine("Wrong number of items");
            }
        }
    }
}

Когда для ItemsSource установлено значение Null, ItemsControl больше не содержит никаких элементов, и коллекция Items пуста, как и ожидалось. Однако при повторном применении привязки все старые элементы восстанавливаются. Коллекция Items содержит те же экземпляры, что и раньше. Метод GetEnumerator вызывается, но новые элементы игнорируются.

Я не понимаю, почему это так или как я могу избежать того, что ItemsControl кеширует свои товары.

Alex

2 ответа

Решение

Я наткнулся на ваш пост, пытаясь понять, как добиться того же. Не желая использовать рефлексию, я продолжал исследовать и нашел лучший ответ.:)

Одна из вещей, к которой я все еще пытаюсь привыкнуть, это то, как многие функции в WPF доступны не через прямые вызовы членов интересующего вас объекта, а через вызовы статических членов, которые выполняют некоторые закулисные действия. магия для достижения цели.

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

Без более полного примера кода я не могу сказать, как будет выглядеть точный код в вашем сценарии. Но основная идея состоит в том, чтобы сделать что-то вроде этого:

CollectionViewSource.GetDefaultView(itemsSourceObject).Refresh();

Т.е. это ищет представление по умолчанию для объекта, привязанного к ItemsSource свойство (то есть предполагается, что переменная с именем itemsSourceObject и передает его значение в метод), а затем, конечно, вызывает Refresh() способ обновить вид.

Как ListBox.Items.Refresh() не имеет желаемого эффекта, я попытался обновить базовый CollectionView. И вуаля - обновления ListBox. Проблема с этим, однако, заключается в том, что единственный способ получить доступ к этому CollectionView - использовать Reflection, поскольку свойство является внутренним:

var collectionViewProperty = typeof (ItemCollection).GetProperty("CollectionView", BindingFlags.Instance | BindingFlags.NonPublic);
var collectionView = (CollectionView) collectionViewProperty.GetValue(itemsControl.Items);
collectionView.Refresh();

Кажется, что CollectionView кэшируется для каждого ItemsSource, который использовался с ItemsControl. Есть ли способ изменить это поведение?

Другие вопросы по тегам