2015-02-10 2 views
5

У меня есть ViewModel с двумя ICollectionView s, которые привязаны как ItemsSource s к двум различным ListBox es. Оба обертывают то же самое ObservableCollection, но с разными фильтрами. Сначала все работает нормально, и оба ListBoxes отображаются правильно заполненными.SourceCollection ICollectionView null

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

Это, как я изменить элемент убедившись, что ICollectionViews обновляются путем удаления и добавления того же пункта:

private void UnassignTag(TagViewModel tag) 
{ 
    TrackChangedTagOnCollectionViews(tag, t => t.IsAssigned = false); 
} 

private void TrackChangedTagOnCollectionViews(TagViewModel tag, Action<TagViewModel> changeTagAction) 
{ 
    _tags.Remove(tag); 

    changeTagAction.Invoke(tag); 

    _tags.Add(tag); 
} 

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

Также я понял, что проблема исчезает, если я регистрирую слушателей в событиях CollectionChanged ICollectionViews. Я убедился, что я создаю и изменяю их из потока GUI и подозреваю, что сбор мусора является проблемой, но в настоящее время я застрял ... Идеи?

Update:

При отладке я понял, что SourceCollections все еще там прямо перед тем, как позвонить ShowDialog() на форму WinForms, в котором размещен мой UserControl. Когда будет показано диалоговое окно, они исчезнут.

я создаю ICollectionViews так:

AvailableTags = new CollectionViewSource { Source = _tags }.View; 
AssignedTags = new CollectionViewSource { Source = _tags }.View; 

Вот как я связываю один из двух (другой очень похож):

<ListBox Grid.Column="0" ItemsSource="{Binding AvailableTags}" Style="{StaticResource ListBoxStyle}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <Border Style="{StaticResource ListBoxItemBorderStyle}"> 
         <DockPanel> 
          <Button DockPanel.Dock="Right" ToolTip="Assign" Style="{StaticResource IconButtonStyle}" 
              Command="{Binding Path=DataContext.AssignSelectedTagCommand, RelativeSource={RelativeSource AncestorType={x:Type tags:TagsListView}}}" 
              CommandParameter="{Binding}"> 
           <Image Source="..."/> 
          </Button> 

          <TextBlock Text="{Binding Name}" Style="{StaticResource TagNameTextBlockStyle}"/> 
         </DockPanel> 
        </Border> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

Я использую RelayCommand<T> как ICommand реализации MvvmLight в мой ViewModel:

AssignSelectedTagCommand = new RelayCommand<TagViewModel>(AssignTag); 
+1

Пожалуйста, размещайте больше кода. –

+0

@ Синат: Я сомневаюсь, что это имеет к этому какое-то отношение. INotifyPropertyChanged на элементах не должен иметь никакого значения для фильтрации (и, как я писал: он работает в другом контексте). Я получил идею remove/add [здесь] (http://drwpf.com/blog/2008/10/20/itemscontrol-e-is-for-editable-collection/). – EagleBeak

+0

@Ganesh: Не могли бы вы быть более конкретными? Я нахожусь в убытке о том, какой код отправлять, не загромождая сообщение. ObservableCollection не изменяется нигде. Также нет ICollectionViews. – EagleBeak

ответ

9

У меня было это тоже, с аналогичным вариантом использования. Когда я обновляю базовую коллекцию, я бы назвал Refresh() во всех отфильтрованных представлениях. Иногда это приводит к тому, что NullReferenceException выбрасывается из ListCollectionView.PrepareLocalArray(), потому что SourceCollection имеет значение NULL.

Проблема в том, что вы не должны быть привязаны к CollectionView, но к объекту CollectionViewSource.View.

Вот как я это делаю:

public class ViewModel { 

    // ... 

    public ViewModel(ObservableCollection<ItemViewModel> items) 
    { 
     _source = new CollectionViewSource() 
     { 
      Source = items, 
      IsLiveFilteringRequested = true, 
      LiveFilteringProperties = { "FilterProperty" } 
     }; 

     _source.Filter += (src, args) => 
     { 
      args.Accepted = ((ItemViewModel) args.Item).FilterProperty == FilterField; 
     }; 
    } 

    // ... 

    public ICollectionView View 
    { 
     get { return _source.View; } 
    } 

    // ... 
} 
+0

Кто? Он делает то же самое: новый CollectionViewSource {Source = _tags} .View; –

+2

Это не совсем то же самое. «CollectionViewSource.View» - это свойство, и значение изменяется. – sconzey

+0

Что? Я пробовал свой путь, и он работает, но я совершенно не понимаю, почему ... Извините за downvote, это должен быть принятый ответ! –

0

Причина вашей проблемы является то, что CollectionViewSource становится сборщиком мусора.