none
CollectionViewSourceのSortDescriptionについてお伺いいたします RRS feed

  • 質問

  • 2点質問です。

    1.文字列で並べ替えているのですが、○●◎が区別されません。どうすれば区別されるようになるでしょうか。

    先頭に○、●、◎などの文字を付けた文字列を並べ替えた場合に、○○○…、●●●…、◎◎◎…のようになるのが理想なのですが、○も●も◎も同列に扱われてしまい、2文字目で並び変わってしまいます。

     

    2.SortDescriptionは一つだけ設定しています。同じ値の場合、元の配列の順に並べたいのですが、どうすればいいでしょうか。

    第2キーとして配列の要素番号を指定できればいいのですが、やり方がわかりません。

     

    以上、よろしくお願いいたします。

    2012年1月23日 8:10

回答

  • 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!)
    • 回答の候補に設定 山本春海 2012年2月1日 1:11
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月23日 11:49
  • 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/
    • 回答の候補に設定 山本春海 2012年2月1日 1:12
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月24日 7:34
    モデレータ
  • 条件付であれば、別にプロパティを持たせなくてもできます。
    元の順番を調べることさえできればいいので、元の配列でのIndexOfで順番を調べることで判定することで可能になります。
    #配列が大きいとすごく遅くなるでしょうが

    /// <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;
        }
    }
    
    #Compareの引数x,yが元の配列の順番で確定するのであれば、ましな実装できるのですが...

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月25日 3:48

すべての返信

  • 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!)
    • 回答の候補に設定 山本春海 2012年2月1日 1:11
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月23日 11:49
  • 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/
    • 回答の候補に設定 山本春海 2012年2月1日 1:12
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月24日 7:34
    モデレータ
  • gekka様、trapemiya様 回答いただきありがとうございます。

    1についてはgekka様のサンプルコードを参考に上手く並び替えることができました。

    2についてはやはりプロパティを持たせるしかないのですね。取り組んでみることにします。

     

    2012年1月25日 0:50
  • 条件付であれば、別にプロパティを持たせなくてもできます。
    元の順番を調べることさえできればいいので、元の配列でのIndexOfで順番を調べることで判定することで可能になります。
    #配列が大きいとすごく遅くなるでしょうが

    /// <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;
        }
    }
    
    #Compareの引数x,yが元の配列の順番で確定するのであれば、ましな実装できるのですが...

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
    • 回答としてマーク 山本春海 2012年2月23日 7:18
    2012年1月25日 3:48