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"  
  xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation  
  xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml  
  xmlns:local="clr-namespace:contactManager"  
  xmlns:system="clr-namespace:System;assembly=mscorlib" 
  Title="Window1" Height="598" Width="1015">
  <Window.Resources>
     <local:contacts x:Key="myContacts" />    
     <CollectionViewSource x:Key="contactsViewSource"
         Source="{StaticResource myContacts}" Filter="CollectionViewSource_Filter" />

  </Window.Resources>
  <Grid Name="Grid1">
    <Grid.RowDefinitions>
      <RowDefinition Height="60*" />
      <RowDefinition Height="423*" />
    </Grid.RowDefinitions>
    <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}}" />
  </Grid>

</Window>

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

    Else                  
      ' 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 

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

        e.Accepted = False

      End If 

    End If

  Else            
    ' 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: ,,

Advertisements

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"))
Else
  Try
    oContactsTA.Fill(oContacts)
    For Each thisRow In oContacts
      Add(New contact(thisRow))
    Next
  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