トップ回答者
CollectionViewSourceのSortDescriptionについてお伺いいたします

質問
-
2点質問です。
1.文字列で並べ替えているのですが、○●◎が区別されません。どうすれば区別されるようになるでしょうか。
先頭に○、●、◎などの文字を付けた文字列を並べ替えた場合に、○○○…、●●●…、◎◎◎…のようになるのが理想なのですが、○も●も◎も同列に扱われてしまい、2文字目で並び変わってしまいます。
2.SortDescriptionは一つだけ設定しています。同じ値の場合、元の配列の順に並べたいのですが、どうすればいいでしょうか。
第2キーとして配列の要素番号を指定できればいいのですが、やり方がわかりません。
以上、よろしくお願いいたします。
回答
-
1)文字の比較のStringComparisonによって結果が変わってくるみたいです。
2)CollectionViewSourceのSortDescriptionでなく、CollectionViewSource.Viewで並べ替えするとできるかも。(CustomSortのあるICollectionViewが得られれば)
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <DockPanel > <CheckBox DockPanel.Dock="Top" Margin="5" IsChecked="{Binding IsSort}" Content="ソート"/> <ComboBox DockPanel.Dock="Top" ItemsSource="{Binding StringComparisons}" SelectedItem="{Binding StringComparison}" /> <ListBox ItemsSource="{Binding CollectionViewSource.View}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" > <TextBlock Text="{Binding Index}" /> <TextBlock Text="{Binding Name}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </DockPanel > </Window>
class TestData :INotifyPropertyChanged { public static TestData Create() { List<TestItem> list = new List<TestItem>(); list.Add(new TestItem() { Index = 0, Name = "○1" }); list.Add(new TestItem() { Index = 1, Name = "●1" }); list.Add(new TestItem() { Index = 2, Name = "◎1" }); list.Add(new TestItem() { Index = 3, Name = "●2" }); list.Add(new TestItem() { Index = 4, Name = "○2" }); list.Add(new TestItem() { Index = 5, Name = "◎1" }); list.Add(new TestItem() { Index = 6, Name = "◎2" }); list.Add(new TestItem() { Index = 7, Name = "●3" }); list.Add(new TestItem() { Index = 7, Name = "●2" }); list.Add(new TestItem() { Index = 7, Name = "●1" }); list.Add(new TestItem() { Index = 0, Name = "あ○1" }); list.Add(new TestItem() { Index = 1, Name = "い●1" }); list.Add(new TestItem() { Index = 2, Name = "あ◎1" }); list.Add(new TestItem() { Index = 3, Name = "う●2" }); list.Add(new TestItem() { Index = 4, Name = "い○2" }); list.Add(new TestItem() { Index = 5, Name = "い◎1" }); list.Add(new TestItem() { Index = 6, Name = "あ◎2" }); list.Add(new TestItem() { Index = 7, Name = "い●3" }); list.Add(new TestItem() { Index = 7, Name = "う●2" }); list.Add(new TestItem() { Index = 7, Name = "い●1" }); TestData test = new TestData(); test.CollectionViewSource.Source = list; return test; } public System.Windows.Data.CollectionViewSource CollectionViewSource { get { return _CollectionViewSource; } } private System.Windows.Data.CollectionViewSource _CollectionViewSource =new CollectionViewSource(); public bool IsSort { get { return _IsSort; } set { if (_IsSort != value) { _IsSort = value; Sort(value); OnPropertyChanged("IsSort"); } } } private bool _IsSort; /// <summary>文字の比較方法</summary> public StringComparison StringComparison { get { return _StringComparison; } set { if (_StringComparison != value) { _StringComparison = value; OnPropertyChanged("StringComparison"); Sort(IsSort); } } } private StringComparison _StringComparison; /// <summary>文字の比較方法一覧</summary> public Array StringComparisons { get { if (_StringComparisons == null) { _StringComparisons = Enum.GetValues(typeof(StringComparison)); } return _StringComparisons; } } private Array _StringComparisons; /// <summary>並べ替え処理</summary> private void Sort(bool flag) { ListCollectionView listView = this.CollectionViewSource.View as ListCollectionView; if (listView != null) { if (flag) { listView.CustomSort = new TestItemSorter() { StringComparison = this.StringComparison }; } else { listView.CustomSort = null; } listView.Refresh(); } } #region INotifyPropertyChanged メンバ public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion } class TestItem : INotifyPropertyChanged { #region INotifyPropertyChanged メンバ public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion public int Index { get { return _Index; } set { if (_Index != value) { _Index = value; OnPropertyChanged("Index"); } } } private int _Index; public string Name { get { return _Name; } set { if (_Name != value) { _Name = value; OnPropertyChanged("Name"); } } } private string _Name; } /// <summary>並べ替え用のクラス</summary> class TestItemSorter : System.Collections.Generic.IComparer<TestItem> , System.Collections.IComparer { public StringComparison StringComparison { get { return _StringComparison; } set { _StringComparison = value; } } private StringComparison _StringComparison = StringComparison.Ordinal; public int Compare(TestItem x, TestItem y) { int cmp; cmp = string.Compare(x.Name, y.Name, this.StringComparison); if (cmp == 0) { cmp= x.Index.CompareTo(y.Index); } return cmp; } public int Compare(object x, object y) { TestItem item1 = x as TestItem; TestItem item2 = y as TestItem; if (item1 != null && item2 != null) { return Compare(item1, item2); } return 0; } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!) -
2.SortDescriptionは一つだけ設定しています。同じ値の場合、元の配列の順に並べたいのですが、どうすればいいでしょうか。
第2キーとして配列の要素番号を指定できればいいのですが、やり方がわかりません。
SortDescriptionは当然複数指定できますが、指定するキーはプロパティ名になりますから、配列の要素番号を直接指定することは無理だと思います。
やるとすれば何らかの形でプロパティとして第2キーを持たせることになるでしょう。
また、CustomViewの指定もできますが、gekkaさんが掲載されている方が100倍速いという情報があります。パフォーマンスが気になる場合は検討されてみてはいかがでしょうか?A much faster sorting for ListView in WPF
http://ligao101.wordpress.com/2007/07/31/a-much-faster-sorting-for-listview-in-wpf/
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
条件付であれば、別にプロパティを持たせなくてもできます。
元の順番を調べることさえできればいいので、元の配列でのIndexOfで順番を調べることで判定することで可能になります。
#配列が大きいとすごく遅くなるでしょうが#Compareの引数x,yが元の配列の順番で確定するのであれば、ましな実装できるのですが.../// <summary>並べ替え用のクラス</summary> class TestItemSorter : System.Collections.Generic.IComparer<TestItem> , System.Collections.IComparer { public IList<TestItem> BaseSource { get; set; } //元になる配列 public bool Desc { get; set; } public StringComparison StringComparison { get { return _StringComparison; } set { _StringComparison = value; } } private StringComparison _StringComparison = StringComparison.Ordinal; public int Compare(TestItem x, TestItem y) { int cmp; cmp = string.Compare(x.Name, y.Name, this.StringComparison); if (cmp == 0) { if (BaseSource != null) { int indexX = BaseSource.IndexOf(x); int indexY = BaseSource.IndexOf(y); cmp = indexX.CompareTo(indexY); } //cmp = x.Index.CompareTo(y.Index); } if(Desc) { cmp = -cmp; } return cmp; } public int Compare(object x, object y) { TestItem item1 = x as TestItem; TestItem item2 = y as TestItem; if (item1 != null && item2 != null) { return Compare(item1, item2); } return 0; } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)- 回答としてマーク 山本春海 2012年2月23日 7:18
すべての返信
-
1)文字の比較のStringComparisonによって結果が変わってくるみたいです。
2)CollectionViewSourceのSortDescriptionでなく、CollectionViewSource.Viewで並べ替えするとできるかも。(CustomSortのあるICollectionViewが得られれば)
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <DockPanel > <CheckBox DockPanel.Dock="Top" Margin="5" IsChecked="{Binding IsSort}" Content="ソート"/> <ComboBox DockPanel.Dock="Top" ItemsSource="{Binding StringComparisons}" SelectedItem="{Binding StringComparison}" /> <ListBox ItemsSource="{Binding CollectionViewSource.View}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" > <TextBlock Text="{Binding Index}" /> <TextBlock Text="{Binding Name}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </DockPanel > </Window>
class TestData :INotifyPropertyChanged { public static TestData Create() { List<TestItem> list = new List<TestItem>(); list.Add(new TestItem() { Index = 0, Name = "○1" }); list.Add(new TestItem() { Index = 1, Name = "●1" }); list.Add(new TestItem() { Index = 2, Name = "◎1" }); list.Add(new TestItem() { Index = 3, Name = "●2" }); list.Add(new TestItem() { Index = 4, Name = "○2" }); list.Add(new TestItem() { Index = 5, Name = "◎1" }); list.Add(new TestItem() { Index = 6, Name = "◎2" }); list.Add(new TestItem() { Index = 7, Name = "●3" }); list.Add(new TestItem() { Index = 7, Name = "●2" }); list.Add(new TestItem() { Index = 7, Name = "●1" }); list.Add(new TestItem() { Index = 0, Name = "あ○1" }); list.Add(new TestItem() { Index = 1, Name = "い●1" }); list.Add(new TestItem() { Index = 2, Name = "あ◎1" }); list.Add(new TestItem() { Index = 3, Name = "う●2" }); list.Add(new TestItem() { Index = 4, Name = "い○2" }); list.Add(new TestItem() { Index = 5, Name = "い◎1" }); list.Add(new TestItem() { Index = 6, Name = "あ◎2" }); list.Add(new TestItem() { Index = 7, Name = "い●3" }); list.Add(new TestItem() { Index = 7, Name = "う●2" }); list.Add(new TestItem() { Index = 7, Name = "い●1" }); TestData test = new TestData(); test.CollectionViewSource.Source = list; return test; } public System.Windows.Data.CollectionViewSource CollectionViewSource { get { return _CollectionViewSource; } } private System.Windows.Data.CollectionViewSource _CollectionViewSource =new CollectionViewSource(); public bool IsSort { get { return _IsSort; } set { if (_IsSort != value) { _IsSort = value; Sort(value); OnPropertyChanged("IsSort"); } } } private bool _IsSort; /// <summary>文字の比較方法</summary> public StringComparison StringComparison { get { return _StringComparison; } set { if (_StringComparison != value) { _StringComparison = value; OnPropertyChanged("StringComparison"); Sort(IsSort); } } } private StringComparison _StringComparison; /// <summary>文字の比較方法一覧</summary> public Array StringComparisons { get { if (_StringComparisons == null) { _StringComparisons = Enum.GetValues(typeof(StringComparison)); } return _StringComparisons; } } private Array _StringComparisons; /// <summary>並べ替え処理</summary> private void Sort(bool flag) { ListCollectionView listView = this.CollectionViewSource.View as ListCollectionView; if (listView != null) { if (flag) { listView.CustomSort = new TestItemSorter() { StringComparison = this.StringComparison }; } else { listView.CustomSort = null; } listView.Refresh(); } } #region INotifyPropertyChanged メンバ public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion } class TestItem : INotifyPropertyChanged { #region INotifyPropertyChanged メンバ public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } #endregion public int Index { get { return _Index; } set { if (_Index != value) { _Index = value; OnPropertyChanged("Index"); } } } private int _Index; public string Name { get { return _Name; } set { if (_Name != value) { _Name = value; OnPropertyChanged("Name"); } } } private string _Name; } /// <summary>並べ替え用のクラス</summary> class TestItemSorter : System.Collections.Generic.IComparer<TestItem> , System.Collections.IComparer { public StringComparison StringComparison { get { return _StringComparison; } set { _StringComparison = value; } } private StringComparison _StringComparison = StringComparison.Ordinal; public int Compare(TestItem x, TestItem y) { int cmp; cmp = string.Compare(x.Name, y.Name, this.StringComparison); if (cmp == 0) { cmp= x.Index.CompareTo(y.Index); } return cmp; } public int Compare(object x, object y) { TestItem item1 = x as TestItem; TestItem item2 = y as TestItem; if (item1 != null && item2 != null) { return Compare(item1, item2); } return 0; } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!) -
2.SortDescriptionは一つだけ設定しています。同じ値の場合、元の配列の順に並べたいのですが、どうすればいいでしょうか。
第2キーとして配列の要素番号を指定できればいいのですが、やり方がわかりません。
SortDescriptionは当然複数指定できますが、指定するキーはプロパティ名になりますから、配列の要素番号を直接指定することは無理だと思います。
やるとすれば何らかの形でプロパティとして第2キーを持たせることになるでしょう。
また、CustomViewの指定もできますが、gekkaさんが掲載されている方が100倍速いという情報があります。パフォーマンスが気になる場合は検討されてみてはいかがでしょうか?A much faster sorting for ListView in WPF
http://ligao101.wordpress.com/2007/07/31/a-much-faster-sorting-for-listview-in-wpf/
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
条件付であれば、別にプロパティを持たせなくてもできます。
元の順番を調べることさえできればいいので、元の配列でのIndexOfで順番を調べることで判定することで可能になります。
#配列が大きいとすごく遅くなるでしょうが#Compareの引数x,yが元の配列の順番で確定するのであれば、ましな実装できるのですが.../// <summary>並べ替え用のクラス</summary> class TestItemSorter : System.Collections.Generic.IComparer<TestItem> , System.Collections.IComparer { public IList<TestItem> BaseSource { get; set; } //元になる配列 public bool Desc { get; set; } public StringComparison StringComparison { get { return _StringComparison; } set { _StringComparison = value; } } private StringComparison _StringComparison = StringComparison.Ordinal; public int Compare(TestItem x, TestItem y) { int cmp; cmp = string.Compare(x.Name, y.Name, this.StringComparison); if (cmp == 0) { if (BaseSource != null) { int indexX = BaseSource.IndexOf(x); int indexY = BaseSource.IndexOf(y); cmp = indexX.CompareTo(indexY); } //cmp = x.Index.CompareTo(y.Index); } if(Desc) { cmp = -cmp; } return cmp; } public int Compare(object x, object y) { TestItem item1 = x as TestItem; TestItem item2 = y as TestItem; if (item1 != null && item2 != null) { return Compare(item1, item2); } return 0; } }
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)- 回答としてマーク 山本春海 2012年2月23日 7:18