Фильтрация ObservableCollection?

Когда я связываю ListBox напрямую с ObservableCollection, я получаю обновления в реальном времени, отображаемые в моем ListBox, но как только я добавляю другие методы LINQ в микс, мой ListBox больше не уведомляется о каких-либо изменениях ObservableCollection.

Здесь, позвольте мне проиллюстрировать пример:

public partial class MainPage : PhoneApplicationPage
{
    ObservableCollection<String> Words = new ObservableCollection<string>();

    public MainPage()
    {
        InitializeComponent();
        listBox1.ItemsSource = Words;
    }

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        Words.Add(DateTime.Now.ToString());
    }
}

Здесь я добавил Button и ListBox на простую страницу, и нажатие кнопки делает новый элемент немедленно отображаться в ListBox.

Однако, если я перехожу из

        listBox1.ItemsSource = Words;

к

        listBox1.ItemsSource = Words.Where(w => w.Contains(":"));

ListBox больше не обновляется.

Как я могу добавить "фильтр" между моим ObservableCollection и ListBox и все равно получить его для обновления без необходимости снова устанавливать .ItemsSource?

Ответ 1

Попробуйте использовать CollectionViewSource следующим образом:

WordsView = new CollectionViewSource();
WordsView.Filter += Words_Filter;
WordsView.Source = Words;

// ...
void Words_Filter(object sender, FilterEventArgs e)
{
    if (e.Item != null)
        e.Accepted = ((string)e.Item).Contains(":");
}

Ответ 2

Почему это не работает:

listBox1.ItemsSource = Words.Where(w => w.Contains(":"));

Вы не привязываете ObservableCollection, а IEnumerable, сгенерированный Linq. Этот новый "список" не уведомляет ListBox об изменениях в списке.

Ответ 3

Вы должны использовать свойство ICollectionView.Filter:

ICollectionView view = CollectionViewSource.GetDefaultView(Words);
view.Filter = WordFilter;

...


bool WordFilter(object o)
{
    string w = (string)o;
    return w.Contains(":")
}