JM

Adventures in Software Craftsmanship

iOS, JavaScript, XAML, HTML5, CSS

Filtering Data using CollectionViewSource

Sometime you end up needing to filter data in a list based on some criteria.  The easiest way to accomplish this in Silverlight is to use the CollectionViewSource.  Here’s a small example showing you how.

Download the sample project and source for this post: CollectionViewSource

SilverlightApplication6 - Windows Internet Explorer (2)

We’ll start with an Option class.  An option has a name and whether it is selected or not.

public class Option : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            RaisePropertyChanged("IsSelected");
        }
    }

    protected void RaisePropertyChanged(string propertyName)
    {
        var handler = this.PropertyChanged;

        if (handler != null)
        {
            handler(
                this,
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

We want to display a list of options on the screen that the user can select from.  Then on the other side of the screen we’ll display only the options they have selected.  Lets create a ViewModel that represents our data.

public class MainPageViewModel
{
    public MainPageViewModel()
    {
        this.Options = new ObservableCollection<Option>();
        for (int i = 0; i < 5; i++)
        {
            this.Options.Add(
                new Option
                {
                    Name = "Option " + (i + 1).ToString(),
                    IsSelected = i % 2 == 0
                });
        }

        this.FilteredOptions = new CollectionViewSource();
        this.FilteredOptions.Source = this.Options;
        this.FilteredOptions.Filter += (s, e) =>
        {
            Option option = e.Item as Option;

            e.Accepted = option.IsSelected;
        };
    }

    private ObservableCollection<Option> _options;
    public ObservableCollection<Option> Options
    {
        get { return _options; }
        set
        {
            _options = value;
        }
    }

    private CollectionViewSource _filteredOptions;
    public CollectionViewSource FilteredOptions
    {
        get { return _filteredOptions; }
        set
        {
            _filteredOptions = value;
        }
    }
}

Notice our Options collection and FilteredOptions CollectionViewSource.  The FilteredOptions Source property gets pointed to the Options collection.  So the Source of the CollectionViewSource is the collection you want to filter. We’re also handling the Filter event of the FilteredOptions.  This gets called when the collection is going to display items.  It passes in each item in the Source collection and you can tell it whether the item is Accepted or not.  Setting Accepted to true will display the item and likewise setting it to false will exclude the item.

Now lets look at the XAML for the UI.  The first ListBox is being bound to the Options Collection.  The second ListBox is being bound to the View property of FilteredOptions, our CollectionViewSource.  Why are we binding to the View?  This view represents our filtered data.

<ListBox
    ItemsSource="{Binding Options}"
    Margin="30"
    Grid.Row="1">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <CheckBox
                    IsChecked="{Binding IsSelected, Mode=TwoWay}"
                    Content="{Binding Name}"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<ListBox
    ItemsSource="{Binding FilteredOptions.View}"
    Grid.Column="1"
    Grid.Row="1"
    Margin="30">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <CheckBox
                    IsChecked="{Binding IsSelected}"
                    Content="{Binding Name}"
                    IsEnabled="False"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

All we have left to do is wire up our ViewModel in our MainPage and Refresh the FilteredOptions when the underlying data has changed.  We do this by setting the MainPageViewModel as the DataContext of the MainPage View, and by calling the Refresh() method on the View property of FilteredOptions.

public partial class MainPage : UserControl
{
    private MainPageViewModel _viewModel;

    public MainPage()
    {
        InitializeComponent();

        _viewModel = new MainPageViewModel();

        this.DataContext = _viewModel;
    }

    private void SelectedOptions_Click(object sender, RoutedEventArgs e)
    {
        _viewModel.FilteredOptions.View.Refresh();
    }
}

Download the sample project and source for this post: CollectionViewSource

1 Comment

  1. [...] This post was mentioned on Twitter by Larry King, Joe McBride. Joe McBride said: Filtering in-memory data using CollectionViewSource in #silverlight http://joem.me/CollectionViewSource /cc @matthiasshapiro [...]

RSS feed for comments on this post.

Leave a Response

Latest from Twitter