Posts tagged ‘WPF’

WPF: Dynamic Filtering Data – the simple approach

You often find yourself wanting to quickly filter data based on a criteria defined by a user in an application.  A classic example is filtering what’s shown in a listbox by what a user has typed into a textbox.

There are many approaches to solving the problem but a quick and simple approach is: (first the xaml):

<Window x:Class="Window1"  
  Title="Window1" Height="598" Width="1015">
     <local:contacts x:Key="myContacts" />    
     <CollectionViewSource x:Key="contactsViewSource"
         Source="{StaticResource myContacts}" Filter="CollectionViewSource_Filter" />

  <Grid Name="Grid1">
      <RowDefinition Height="60*" />
      <RowDefinition Height="423*" />
    <TextBox Margin="62,26,16,0" Name="txtSearch" Height="23" VerticalAlignment="Top"
     AcceptsReturn="True" FontStyle="Italic" KeyUp="txtSearch_KeyUp" />
    <Label Margin="12,12,66,0" Name="Label1" Height="28" VerticalAlignment="Top">Search</Label>

    <ListBox Grid.Row="1" Name="lstContacts" IsSynchronizedWithCurrentItem="True" 
      ItemsSource="{Binding Source={StaticResource contactsViewSource}}" />


The local resource contacts is a class that is defined in the application.  The namespace is imported into the XAML with xmlns:local=”clr-namespace:contactManager“‘.  The class itself is referenced in XAML by <local:contacts x:Key=”myContacts” />‘.

The class itself inherits from an observableCollection – although the approach would work with any datasource that can be bound to the listbox. After contacts has been imported – we setup a collectionViewSource – rather than using the default viewSource provided by WPF.

The code behind to connect the filter text with the filter applied to the listbox is:

Partial Public Class Window1  
  Private Function findString(ByVal haystack As String, ByVal needle As String, _       
                                 Optional ByVal comparison As StringComparison _          
                                    = StringComparison.CurrentCultureIgnoreCase) As Integer
    ' We are using string.indexOf rather than string.contains as string.contains     
    ' doesn't allow us to specify CurrentCultureIgnoreCase         
    Return haystack.IndexOf(needle, 0, comparison) 

  End Function 

Private Sub CollectionViewSource_Filter(ByVal sender As System.Object, _                                   
                                    ByVal e As System.Windows.Data.FilterEventArgs) 
   ' Cast the listbox item to the type used   
  Dim thisContact As contact = CType(e.Item, contact) 

  If (thisContact IsNot Nothing) Then  ' Handle an empty item 

    If String.IsNullOrEmpty(txtSearch.Text) Then           
      ' Make sure we have some text that we wish to filter

      e.Accepted = True         
      ' If we don't then we want all items to appear in the listbox

      ' findString returns the index of the needle (search text) in       
      ' the haystack (text to be searched)             
      ' IndexOf returns -1 if the needle isn't found in the haystack 

      If (findString(thisContact.firstName, txtSearch.Text) > -1) _                 
               Or (findString(thisContact.lastName, txtSearch.Text) > -1) Then 

        ' The needle exists in the haystack - so accept this entry                 
        e.Accepted = True 

        ' This item doesn't contain the needle we are looking for

        e.Accepted = False

      End If 

    End If

    ' thisContact IS nothing - so don't bother displaying it 

    e.Accepted = False 

  End If 

End Sub 

Private Sub txtSearch_KeyUp(ByVal sender As System.Object, _                    
                                ByVal e As System.Windows.Input.KeyEventArgs)          
' This event will be raised whenever the keyUp event occurs on the     
' textbox that we are using to filter         
' Do a simple refresh on the listbox 

    CType(Me.FindResource("contactsViewSource"), CollectionViewSource).View.Refresh() 

  End Sub 

End Class 

  And that's it - a listbox containing items is filtered based on the text typed by the user in a search box.

Technorati Tags: ,,

Sunday 25th November, 2007 at 7:58 pm 2 comments

DataBinding ObservableCollection with WPF

Watch out for the code you place in your sub New() of the ObservableCollection – it can cause the WPF designer problems as the designer will attempt to execute the constructor during design time.  If you’re trying to populate the collection from a file based database then the connection string, in some situations, VS 2008 will look in the wrong place for the database.  It appears that using the |Data Directory| prefix for the database causes VS 2008 (RTM) to look in the working directory of VS – rather than the project directory.

Symptoms of this kind of problem are VS reporting:

“Could not create an instance of type ‘abc’. …Window1.xaml 8 3” and your XAML reference will be squiggily underlined.
A solution to this problem is wraping the constructor of the object implementing the ObservableCollection in a try/catch and swallowing the error (suggested only during the development cycle) or by detecting if you’re in designtime.

A simple way to detect if you’re in designtime with WPF is:

If DesignerProperties.GetIsInDesignMode(Application.Current.MainWindow) Then
  Add(New contact("DesignTime", "Detected"))
    For Each thisRow In oContacts
      Add(New contact(thisRow))
  Catch ex As Exception
    Add(New contact("DesignTime", "NotDetected"))
  End Try
End If

Saturday 24th November, 2007 at 3:51 pm 3 comments

Recent Posts