In this post, I am going to show how to easily filter a collection of objects without creating a whole new collection. We’ll be filtering collections which implement the IEnumerable interface. Specifically we’ll be casting our collection to an IEnumerable(Of T) which exposes the enumerater and is used to iterate over a collection of a specified type.
Personally I’ve used the IEnumerable(Of T) to filter a DataGrid in Silverlight. When you want to bind data to the DataGrid, you set the ItemsSource property to your collection. We’ll simply filter the DataGrid’s ItemsSource collection. Here’s what our Silverlight filtering application looks like:
First, let’s setup our UI. I’m going to create a collection of people. Each person has a name and an age. We want to filter on age. Therefore the UI will have a ComboBox so we can select an age to filter on. We’ll also have a filter Button and a clear filter Button. We’ll finally have a DataGrid to display our filtered (or unfiltered) results. Here’s the XAML to produce the UI:
1: <UserControl x:Class="FilterCollection.Page"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
5: Width="400" Height="250">
6: <Grid x:Name="LayoutRoot" Background="White">
7: <Grid.RowDefinitions>
8: <RowDefinition Height="25" />
9: <RowDefinition />
10: </Grid.RowDefinitions>
11: <Grid.ColumnDefinitions>
12: <ColumnDefinition />
13: <ColumnDefinition Width="75" />
14: <ColumnDefinition Width="75" />
15: </Grid.ColumnDefinitions>
16:
17: <ComboBox x:Name="cbxAge" Grid.Column="0" Grid.Row="0" Height="25" />
18: <Button x:Name="btnFilter" Content="Filter" Grid.Column="1" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
19: <Button x:Name="btnClear" Content="Clear" Grid.Column="2" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
20: <data:DataGrid x:Name="dgResults" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" AutoGenerateColumns="True" Margin="0,5,0,0" />
21: </Grid>
22: </UserControl>
Before we can create our collection of people, we’ll first need to create a Person object/class. Here’s the code to accomplish this:
1: Public Class Person
2: Private _Name As String
3: Private _Age As Integer
4:
5: Public Property Name() As String
6: Get
7: Return _Name
8: End Get
9: Set(ByVal value As String)
10: _Name = value
11: End Set
12: End Property
13:
14: Public Property Age() As Integer
15: Get
16: Return _Age
17: End Get
18: Set(ByVal value As Integer)
19: _Age = value
20: End Set
21: End Property
22:
23: Public Sub New(ByVal name As String, ByVal age As Integer)
24: _Name = name
25: _Age = age
26: End Sub
27:
28: End Class
Now that we have our UI and Person object ready, we can finally create our collection and initialize our UI. I first create the Persons collection, then create a collection of Age(s) to filter by. I then bind the ComboBox and the DataGrid to our collections. Keep in mind that the DataGrid is not filtered at this point.
1: 'initialize our collection of person(s)
2: _Persons = New List(Of Person)
3: _Persons.Add(New Person("Jake", 29))
4: _Persons.Add(New Person("Same", 28))
5: _Persons.Add(New Person("Beth", 29))
6: _Persons.Add(New Person("Sue", 13))
7: _Persons.Add(New Person("Tina", 12))
8: _Persons.Add(New Person("Pete", 13))
9: _Persons.Add(New Person("Andy", 10))
10: _Persons.Add(New Person("Ashley", 11))
11: _Persons.Add(New Person("Sydney", 10))
12:
13: 'initialize our combobox of ages
14: Dim ages As List(Of Integer) = New List(Of Integer)
15: For i As Integer = 1 To 30 Step 1
16: ages.Add(i)
17: Next
18: Me.cbxAge.ItemsSource = ages
19: Me.cbxAge.SelectedIndex = 0
20:
21: 'initialize our DataGrid to our collection of person(s)
22: Me.dgResults.ItemsSource = _Persons
Now we are ready to make the magic happen. When we select an Age and press our Filter Button, we want to filter the _Persons collection and rebind the DataGrid. Here’s the code:
1: 'acquire the age to filter on
2: Dim age As Integer = DirectCast(Me.cbxAge.SelectedItem, Integer)
3:
4: 'convert our Persons collection to an IEnumerable(Of T) where T (type) = Person
5: Dim source As IEnumerable(Of Person) = DirectCast(_Persons, IEnumerable(Of Person))
6:
7: 'filter the collection using the "Where" extension method
8: Me.dgResults.ItemsSource = source.Where(Function(p As Person) p.Age = age)
We first acquire the selected Age. Then we convert our collection of Person(s) to an IEnumerable(Of Person). This allows us to use the IEnumerable(Of T).Where method extension. Notice I’ve passed in a function to the Where predicate to test each Person to see whether their Age is equal to the selected age. Only people who’s age is equal to our filter will be bound to the DataGrid. Here is a picture of our filtered DataGrid:
If we want to clear our filter, we simply set the ItemsSource of our DataGrid back to the original _Persons collection.
1: 'reset the DataGrid's ItemsSource back to our original Persons collection
2: Me.dgResults.ItemsSource = _Persons
FilterCollection_Soln.zip (1.02 mb)