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. Есть ли способ изменить это поведение?