1
votes

I am writing windows phone 8.1 universal application and main applicaiton control is Pivot with few pivot items. In the pivot items are ListViews containing TestItems. I want to filter items on one list by IsRead property. Is it possible to just filter main collection without keeping 2 collections? CollectionViewSource does not support filtering a sorting on universal apps, if I know. But keeping (and synchronizing on changes) two collections doesn't look like good idea.

EDIT: I have used ObservableCollection because list of items may be updated on the background. Probably it was not clear from original question.

class TestItem : ModelBase
{
    private bool isRead;
    public bool IsRead
    {
        get { return isRead; }
        set
        {
            isRead = value;
            NotifyPropertyChanged();
        }
    }

    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            NotifyPropertyChanged();
        }
    }
}

class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        Items = new ObservableCollection<TestItem>();
    }

    public ObservableCollection<TestItem> Items { get; private set; }
    public ObservableCollection<TestItem> ItemsRead { get; private set; } // key point

    private void RefreshItems()
    {
        // data manipulation - on both collections?
    }

    // ...
}
3

3 Answers

0
votes

You can use Linq;

In your case:

using System.Linq;

class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        Items = new ObservableCollection<TestItem>();
    }

    public ObservableCollection<TestItem> Items { get; private set; }
    //public ObservableCollection<TestItem> ItemsRead { get; private set; } // key point
    public IEnumerable<TestItem> ItemsRead
    {
        get
        {
            IEnumerable<TestItem> itemsRead = from item in Items
                                          where item.IsRead
                                          select item;
            return itemsRead;
        }
    }


    private void RefreshItems()
    {
        // data manipulation - on both collections?
    }

    // ...
}

Please, check syntax, it can contain some mistakes. You can manipulate with the first collection, the second collection will be automatically updated.

0
votes

You can define a CollectionViewSource in your XAML:

<Grid.Resources>
  <CollectionViewSource x:Name="MyCollectionViewSource"/>
</Grid.Resources>

And then set it's source like this:

//Global variable
MainViewModel vm;

//Constructor
public MyPage(){
    //Other code
    vm = new MainViewModel();
    vm.Items.CollectionChanged += Items_CollectionChanged;
    UpdateViewSource();
}

private void Items_CollectionChanged(object sender, CollectionChangedEventArgs e){
    UpdateViewSource();
}

private void UpdateViewSource(){
    MyCollectionViewSource.Source = vm.Items.Where(x => x.IsRead);
}

I haven't tested this code.

0
votes

You need only one ObservableCollection containing the initial objects and another property (let's say ItemsFiltered) with a get method returning the results after filtering. In the constructor you can subscribe to the CollectionChanged event of the observable collection to raise the OnPropertyChanged event for the ItemsFiltered property. You raise the same event when the filter state is changed. This is a simple example:

public MainViewModel()
{
    _initialItems.CollectionChanged += (sender, e) => OnPropertyChanged("Items");
}

private ObservableCollection<TestItem> _initialItems = new ObservableCollection<TestItem>();

public List<TestItem> Items
{
    get
    {
        if (IsReadFilter)
        {
            return _initialItems.Where(i => i.IsRead).ToList();
        }

        return _initialItems;
    }
}

private bool _isReadFilter;
public bool IsReadFilter
{
    get { return _isReadFilter; }
    set
    {
        if (_isReadFilter != value)
        {
            _isReadFilter = value;
            OnPropertyChanged("IsReadFilter");
            OnPropertyChanged("Items");
        }
    }
}

Basically, the idea is that every time IsReadFilter value is changed, the UI gets notified that the Items property is changed and calls its get method to get the new value and update. Items are also updated every time the observable collection is changed from other places.