#992 – Scrolling an Item in a ListBox into View

You can programmatically scroll an item in a ListBox into view by using the ListBox.ScrollIntoView method.

In the example below, we scroll the actor Jane Wyman into view when the user clicks the button.

        <ListBox Name="lbActors" Margin="15,5" Width="200" Height="150"
                 ItemsSource="{Binding ActorList}"
                 DisplayMemberPath="NameAndDates"/>
        <Button Content="Find Jane Wyman" Margin="10"
                Click="btnFindJane_Click"/>

992-001

In the Click event handler for the button, we use Linq to find the Actor object for Jane Wyman and we then call ScrollIntoView.

        private void btnFindJane_Click(object sender, RoutedEventArgs e)
        {
            Actor jane = (from a in ActorList
                         where a.FirstName == "Jane"
                            && a.LastName == "Wyman"
                         select a).First();
            lbActors.ScrollIntoView(jane);
            lbActors.SelectedItem = jane;
        }

992-002

Advertisements

#991 – Specifying which Field Is Used for Finding an Item by Typing

You can normally jump to a particular item in an items control by typing the first few characters of the item.  This works, however, based on the string representation of each bound item (the output of ToString).  In some cases, you want to use a specific field when finding an item by typing text.  You can do this by setting TextSearch.TextPath on the list control.

In the example below, we have a data template that displays last and first names of each actor.  We specify that we want to use the last name as a search path.

        <ListBox Name="lbActors" Margin="15,5" Width="200" Height="220"
                 ItemsSource="{Binding ActorList}"
                 TextSearch.TextPath="LastName">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding LastCommaFirst}"/>
                        <TextBlock Text="{Binding Dates}"
                                   FontStyle="Italic"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

We can now just start typing an actor’s last name in order to jump to that actor.  (E.g. “Crawford”).

991-001

#990 – Typing Text to Select an Item in a ListBox

If a ListBox has focus, you can just type some text in order to select an item.  By default, the text that you enter will be matched against the property specified by the DisplayMemberPath property, or by the value of the bound object’s ToString method, if DisplayMemberPath is not specified.

In the example below, the NameAndDates property is used as the display string.  A CollectionViewSource is used to sort by last name.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15,5" Width="200" Height="190"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}"
                 DisplayMemberPath="NameAndDates" />
    </StackPanel>

Once the ListBox has focus, we can type a letter to jump to the next item starting with that letter.  For example, if we enter ‘J’ and then enter ‘J’ again, Joan Crawford is first selected, followed by Joan Fontaine.

990-001

990-002

#989 – Enabling Live Filtering in a CollectionViewSource

Like sorting, filtering in a CollectionViewSource is not automatically done when you change the contents of one of the data bound items.  By default, you need to call the Refresh method of the CollectionViewSource.

In the example below, we filter on first name “Joan” and then change Joan Fontaine’s first name to “Bob”.  Notice that the list is not re-filtered–Bob remains in the list.

989-001

989-002

You can fix this by adding the FirstName property to the LiveFilteringProperties collection of the CollectionViewSource and setting IsLiveFilteringRequested to true.

        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}"
                              IsLiveFilteringRequested="True">
            <CollectionViewSource.LiveFilteringProperties>
                <clr:String>FirstName</clr:String>
            </CollectionViewSource.LiveFilteringProperties>
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>

Now, when we change Joan to Bob and we’re filtering on “Joan”, Bob automatically disappears from the list.

989-003

#988 – Enabling Live Sorting in a CollectionViewSource

By default, when you’re using a CollectionViewSource to do sorting, grouping and filtering in a list-based control, the sorting/grouping/filtering behavior will only updated when you explicitly refresh the CollectionViewSource (by calling Refresh) or when you add or remove something to the collection.

You can enable live sorting in the CollectionViewSource to cause it to resort items when one or more properties on the bound objects change.  In the example below, we set the IsLiveSortingRequested property to true and specify that the Actor.LastName property is the property to live sort on.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}"
                              IsLiveSortingRequested="True">
            <CollectionViewSource.LiveSortingProperties>
                <clr:String>LastName</clr:String>
            </CollectionViewSource.LiveSortingProperties>
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

Now when we make a change to the last name of one of the actors, the sorting is updated.

988-001

 

988-002

#987 – CollectionViewSource Updates on Refresh or Change to Collection

By default, when you’re using a CollectionViewSource to do sorting, grouping and filtering in a list-based control, the sorting/grouping/filtering behavior will only updated when you explicitly refresh the CollectionViewSource (by calling Refresh) or when you add or remove something to the collection.

For example, if we add an actor to a list of actors and we are sorting by last name, the actor will be inserted at the correct spot.

987-001

 

987-002

 

If you change the contents of one of the bound objects, however, the sorting behavior is not updated.  For example, if we change the last name of “Jennifer Jones” to “Garner” and we do not call the Refresh method of the CollectionViewSource, the sorting will be wrong.

987-003

#986 – Filtering a ListBox Using a CollectionViewSource

When populating a ListBox from a CollectionViewSource, you can also filter the data.  Below is an example of a ListBox that is bound to a CollectionViewSource.

    <Window.Resources>
        <CollectionViewSource x:Key="cvsActors" Source="{Binding ActorList}" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="LastName" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <StackPanel>
        <ListBox Name="lbActors" Margin="15" Width="200" Height="200"
                 ItemsSource="{Binding Source={StaticResource cvsActors}}"/>
        <CheckBox Content="Only Joans" IsChecked="{Binding OnlyJoans}"
                  Margin="15"/>
    </StackPanel>

In code, we add a handler for the Filter event of the CollectionViewSource.  The handler is called for each item in the list.

            // Requires: using System.Windows.Data
            ((CollectionViewSource)this.Resources["cvsActors"]).Filter += ActorList_Filter;

In the handler, we set the Accepted property of the argument if the item should be included in the list.

        void ActorList_Filter(object sender, FilterEventArgs e)
        {
            // Set e.Accepted to true to include the item in the list
            if (!onlyJoans)
                e.Accepted = true;
            else
            {
                Actor a = e.Item as Actor;
                e.Accepted = (a.FirstName == "Joan") ? true : false;
            }
        }

986-001
986-002

We also have to make sure to “refresh” the CollectionViewSource when the OnlyJoans property changes.  This will trigger it to re-filter the collection.

        private bool onlyJoans;
        public bool OnlyJoans
        {
            get { return onlyJoans; }
            set
            {
                if (value != onlyJoans)
                {
                    onlyJoans = value;
                    RaisePropertyChanged("OnlyJoans");
                    ((CollectionViewSource)this.Resources["cvsActors"]).View.Refresh();
                }
            }
        }