GeoffM
GeoffM

Reputation: 1611

Sorting ListBox with MVVM code binding

I have a ListBox which happily displays data using a code-behind MVVM object. However, I want to sort the entries and so I thought an intermediate CollectionViewSource might work. But instead the program crashes on startup.

Original xaml extract:

<ListBox SelectedItem="{Binding SelectedCategory}"
    DisplayMemberPath="name"
    ItemsSource="{Binding Categories}"
    Name="CategoriesListBox" />

Code behind extract:

public class ViewModel : INotifyPropertyChanged
    {
        private trainCategory[] _categories;
        private trainCategory _selectedCategory;

        public event PropertyChangedEventHandler PropertyChanged;

        public trainCategory[] Categories
        {
            get { return _categories; }
            set
            {
                if (_categories == value)
                {
                    return;
                }
                _categories = value;
                RaisePropertyChanged("Categories");
            }
        } //etc

Replacement XAML for ListBox:

<ListBox SelectedItem="{Binding SelectedCategory}"
    DisplayMemberPath="name"
    ItemsSource="{Binding Source={StaticResource SortedItems}}"
    Name="CategoriesListBox" />

And the CollectionViewSource:

<CollectionViewSource x:Key="SortedItems" Source="{Binding Categories}">
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="name"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

It seems to me that the CollectionViewSource goes in between the view model and the ListBox, but it clearly doesn't (or I've done it wrong). Any pointers appreciated.

Upvotes: 2

Views: 4199

Answers (2)

Wood
Wood

Reputation: 739

What exception are you getting on startup? Remember to have your Resources section before all other code that needs to use the resources.

An alternative to that would be:

<ListBox SelectedItem="{Binding SelectedCategory}" DisplayMemberPath="name" Name="CategoriesListBox">
    <ListBox.ItemsSource>
        <Binding>
            <Binding.Source>
                <CollectionViewSource Source="{Binding Categories}">
                    <CollectionViewSource.SortDescriptions>
                        <scm:SortDescription PropertyName="name"/>
                    </CollectionViewSource.SortDescriptions>
                </CollectionViewSource>
            </Binding.Source>
        </Binding>
    </ListBox.ItemsSource>
</ListBox>

Also remember to have the proper xmlns declaration for scm in any case.

Upvotes: 1

EtherDragon
EtherDragon

Reputation: 2698

Use your original xaml

<ListBox SelectedItem="{Binding SelectedCategory}" 
    DisplayMemberPath="name" 
    ItemsSource="{Binding Categories}" 
    Name="CategoriesListBox" /> 

Update your View Model to use a List instead:

   public List<trainCategory> _categories;
   public List<trainCategory> Categories 
    { 
        get 
        {   // This LINQ statement returns a sorted list
            return (from c in _categories
                    orderby c
                    select c);
        } 
        set 
        { 
            if (_categories == value) 
            { 
                return; 
            } 
            _categories = value; 
            RaisePropertyChanged("Categories"); 
        } 
    } //etc 

Then you can skip all that nastiness of trying to bind to a static resporce. Just bind strait to the property in your view model.

Alternately, you can still use arrays as your backing variable in your viewmodel:

   public trainCategory[] _categories;
   public List<trainCategory> Categories 
    { 
        get 
        {   // This LINQ statement returns a sorted list
            return (from c in _categories
                    orderby c
                    select c).ToList();
        } 
        set 
        { 
            if (_categories == value.ToArray()) 
            { 
                return; 
            } 
            _categories = value.ToArray();
            RaisePropertyChanged("Categories");
        } 
    } //etc 

Upvotes: 2

Related Questions