Q105. ListBox 等で、コレクションを操作せずソートするには
A.ItemoSource でバインドされたコレクションを操作せずに コントロールで並び替えを行いたい場合が多々あると思います。その場合、リソース内で CollectionViewSource を使えば、コレクションを操作せずにソートすることができます。
以下サンプルです。ViewModel で用意したコレクションを、三つの ListBox を使いプロパティごとにソートして表示します。
まず、Model と ViewModel のコードです。ViewModel のコンストラクタで、従業員のコレクションを生成しています。コレクションは ListBox とバインドさせるため通知プロパティで実装しています。
using System.Collections.ObjectModel; using System.ComponentModel; namespace CollectionViewSourceSample { /// <summary>従業員クラス</summary> public class Person { public string Name { get; set; } public string Post { get; set; } public int Age { get; set; } } /// <summary>ビューモデル</summary> class ViewModel : INotifyPropertyChanged { /// <summary>コンストラクタ</summary> public ViewModel() { this.Persons = new ObservableCollection<Person>(); this.Persons.Add(new Person() { Name = "秋山", Post = "営業", Age = 36 }); this.Persons.Add(new Person() { Name = "東郷", Post = "総務", Age = 22 }); this.Persons.Add(new Person() { Name = "児玉", Post = "役員", Age = 50 }); this.Persons.Add(new Person() { Name = "山本", Post = "開発", Age = 32 }); this.Persons.Add(new Person() { Name = "乃木", Post = "販売", Age = 29 }); this.Persons.Add(new Person() { Name = "大山", Post = "人事", Age = 41 }); this.Persons.Add(new Person() { Name = "山縣", Post = "営業", Age = 24 }); } Person _person; /// <summary>選択された従業員</summary> public Person Person { get { return _person; } set { if (_person == value) return; _person = value; OnPropertyChanged("Person"); } } ObservableCollection<Person> _persons; /// <summary>従業員のコレクション</summary> public ObservableCollection<Person> Persons { get { return _persons; } set { if (_persons == value) return; _persons = value; OnPropertyChanged("Persons"); } } #region INotifyPropertyChanged メンバ public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { if (PropertyChanged == null) return; PropertyChanged(this, new PropertyChangedEventArgs(name)); } #endregion } }
XAML です。Window.Resources 内で「名前」・「役職」・「年齢」用のリソースを用意します。「年齢」のみ降順にしてみました。あとは用意したリソースを ListBox にバインドさせるだけです。
<Window x:Class="CollectionViewSourceSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" xmlns:local="clr-namespace:CollectionViewSourceSample" Title="MainWindow" Height="300" Width="520" > <Window.DataContext> <local:ViewModel /> </Window.DataContext> <Window.Resources> <!-- 名前でソート --> <CollectionViewSource x:Key="names" Source="{Binding Persons}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="Name" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource> <!-- 役職でソート --> <CollectionViewSource x:Key="posts" Source="{Binding Persons}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="Post" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource> <!-- 年齢でソート(降順) --> <CollectionViewSource x:Key="ages" Source="{Binding Persons}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="Age" Direction="Descending" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="4"/> <ColumnDefinition/> <ColumnDefinition Width="4"/> <ColumnDefinition/> <ColumnDefinition Width="4"/> <ColumnDefinition/> <ColumnDefinition Width="4"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="22"/> <RowDefinition/> <RowDefinition Height="4"/> </Grid.RowDefinitions> <Grid Grid.ColumnSpan="5" Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="22"/> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="110"/> <ColumnDefinition Width="90"/> <ColumnDefinition Width="50"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="50"/> <ColumnDefinition Width="50"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="1" Text="選択された従業員" HorizontalAlignment="Right" VerticalAlignment="Center" Padding="4,0" /> <TextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Text="{Binding Person.Name}" IsReadOnly="True" /> <TextBlock Grid.Row="1" Grid.Column="2" Text="役職" HorizontalAlignment="Right" VerticalAlignment="Center" Padding="4,0" /> <TextBox Grid.Row="1" Grid.Column="3" VerticalAlignment="Center" Text="{Binding Person.Post}" IsReadOnly="True" /> <TextBlock Grid.Row="1" Grid.Column="4" Text="年齢" HorizontalAlignment="Right" VerticalAlignment="Center" Padding="4,0" /> <TextBox Grid.Row="1" Grid.Column="5" VerticalAlignment="Center" Text="{Binding Person.Age}" IsReadOnly="True" /> </Grid> <TextBlock Grid.Row="1" Grid.Column="1" Text="名前でソート" VerticalAlignment="Center" /> <ListBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Source={StaticResource names}}" DisplayMemberPath="Name" SelectedItem="{Binding Person}" /> <TextBlock Grid.Row="1" Grid.Column="3" Text="役職でソート" VerticalAlignment="Center" /> <ListBox Grid.Row="2" Grid.Column="3" ItemsSource="{Binding Source={StaticResource posts}}" DisplayMemberPath="Post" SelectedItem="{Binding Person}" /> <TextBlock Grid.Row="1" Grid.Column="5" Text="年齢でソート(降順)" VerticalAlignment="Center" /> <ListBox Grid.Row="2" Grid.Column="5" ItemsSource="{Binding Source={StaticResource ages}}" DisplayMemberPath="Age" SelectedItem="{Binding Person}" /> </Grid> </Window>