2014-10-01 3 views
0

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

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

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

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 делает не содержать любые предметы больше и коллекция предметов empt, как и ожидалось. Однако при повторном применении привязки восстанавливаются все старые элементы. Коллекция Items содержит те же экземпляры, что и раньше. Вызывается метод GetEnumerator, но новые элементы игнорируются.

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

Alex

+1

Почему бы вам просто не создать новую коллекцию и не присвоить ее свойству ItemSource? – Clemens

+0

'element.SetCurrentValue (ItemsControl.ItemsSourceProperty, null);' Я считаю, что это установит ваш 'ItemsControl.ItemSource' в' null', где ваш код обновит коллекцию? – Bolu

+0

Коллекцию можно разделить на несколько (несвязанных) объектов. Когда я заменю его на одном месте, мне придется его заменять повсюду. Но у меня нет доступа ко всем этим местам. – LionAM

ответ

1

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

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

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

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

CollectionViewSource.GetDefaultView(itemsSourceObject).Refresh(); 

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

+0

Спасибо. Оно работает. Как ты это нашел? И знаете ли вы, что произойдет, если есть несколько экземпляров ListBox (с тем же ItemsSource)? Оба обновлены - или только один из них ... – LionAM

+0

@LionAM: Я честно не помню, как я его нашел. Я уверен, что искал «CollectionView» и некоторые другие ключевые слова, но я не помню специфику. Если коллекция привязана к более чем одному 'ItemsControl' (' ListBox' или иначе), все должно быть обновлено. –

+0

@LionAM: Обратите внимание, что это происходит только тогда, когда вы связали сбор непосредственно (заставляя WPF использовать представление по умолчанию); если вы явно создали свой собственный вид, то очевидно, что это представление используется вместо представления по умолчанию, и вам нужно обновить это представление. Конечно, в этом сценарии, поскольку вы создали представление, его намного легче найти (например, просто сохранить ссылку самостоятельно) :) –

0

Как ListBox.Items.Refresh() не имеет желаемого эффекта, я попробовал обновления на подстилающей CollectionView. И voilà - обновления 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. Есть ли способ изменить это поведение?