none
WPF Listviewの列並び替えアルゴリズムのユーザー定義 RRS feed

  • 質問

  • WPF でListviewを動的にコントロールするプログラムをVisual Basicで書いています。

    Listviewの内容を、ヘッダーをクリックして正順、逆順でソートするところまでは行きましたが、ソートのアルゴリズムが文字列の処理を対象に設計されているので、

    同じ列に表示された1.2.3.9.11 という整数を正順にソートしたいところが1.11.2.3.9と並び替えられてしまいます。

    プログラムは、下記のリンクにあるものを使用しています。

    方法 : ヘッダーがクリックされたときに GridView 列を並べ替える
    http://msdn.microsoft.com/ja-jp/library/ms745786.aspx

    従来のWindows FormではIcompareableインターフェイスをオーバーライドして、ユーザー定義のソートアルゴリズムを作成できたのですが、WPFになってからはどうやってソートアルゴリズムをカスタマイズしたら良いのでしょうか教えて下さい。なお、XAMLでは「Listviewを動的にコントロールする」という動作が実現できませんから、ソースにXAMLを使うのはやめて下さい。

     

    また、MSDN WEBサイトのどこを見ればこの問題の解決方法が記載されているのでしょうか。

    私は初心者で、VB以外の言語は読めませんので、分かり易く回答して下さい。

     

    2011年11月16日 3:15

回答

  • データソースが IList(かつ IBindingList でない)の場合の既定の ICollectionView 実装である ListCollectionView には、IComparer 型の CustomSort プロパティが存在します。これを使えるなら、ICollectionView を ListCollectionView に CType して設定するだけです。

    データソースのビューが ListCollectionView じゃない場合はまた違う方法になるでしょう。

    また、MSDN WEBサイトのどこを見ればこの問題の解決方法が記載されているのでしょうか。

    サンプルを見れば、ヘッダクリック時のイベントハンドラは基本的にソート対象を抽出しているだけで、実際にソートしているのは Sort メソッドであること、Sort メソッドにおいてソート処理の主体は ICollectionView であることは分かると思います。

    そこから、ICollectionView → データバインディングの概要 → データバインディングに関する「方法」トピック → ビュー内のデータを並べ替える → CustomSort 、といった流れになりますかね。

    VB以外の言語は読めませんので、

    WPF を扱うのであれば、C# を避けることはできないと言っていいと思います。MSDN はまだしも、ほとんどの Web 上の情報は C# で書かれていると感じるからです。

    知りたいことの大部分は「ライブラリの使い方」のはずです。そうであれば、VB だろうが C# だろうが、クラス名やメソッド名さえ分かればどういうことをすればいいのか大体見当はつきます。いちいちコードを一行ずつ追う必要はあまりありません。

    ロジック的な疑問に関してはそういうわけにもいきませんが、基礎的な文法に関しては VB と少し記法が違うだけ程度の差ですので、ネットに転がっている C# → VB converter を使うなどすれば大抵は解決します。

    • 回答としてマーク 馋涎欲滴 2011年11月21日 14:18
    2011年11月16日 3:58

すべての返信

  • データソースが IList(かつ IBindingList でない)の場合の既定の ICollectionView 実装である ListCollectionView には、IComparer 型の CustomSort プロパティが存在します。これを使えるなら、ICollectionView を ListCollectionView に CType して設定するだけです。

    データソースのビューが ListCollectionView じゃない場合はまた違う方法になるでしょう。

    また、MSDN WEBサイトのどこを見ればこの問題の解決方法が記載されているのでしょうか。

    サンプルを見れば、ヘッダクリック時のイベントハンドラは基本的にソート対象を抽出しているだけで、実際にソートしているのは Sort メソッドであること、Sort メソッドにおいてソート処理の主体は ICollectionView であることは分かると思います。

    そこから、ICollectionView → データバインディングの概要 → データバインディングに関する「方法」トピック → ビュー内のデータを並べ替える → CustomSort 、といった流れになりますかね。

    VB以外の言語は読めませんので、

    WPF を扱うのであれば、C# を避けることはできないと言っていいと思います。MSDN はまだしも、ほとんどの Web 上の情報は C# で書かれていると感じるからです。

    知りたいことの大部分は「ライブラリの使い方」のはずです。そうであれば、VB だろうが C# だろうが、クラス名やメソッド名さえ分かればどういうことをすればいいのか大体見当はつきます。いちいちコードを一行ずつ追う必要はあまりありません。

    ロジック的な疑問に関してはそういうわけにもいきませんが、基礎的な文法に関しては VB と少し記法が違うだけ程度の差ですので、ネットに転がっている C# → VB converter を使うなどすれば大抵は解決します。

    • 回答としてマーク 馋涎欲滴 2011年11月21日 14:18
    2011年11月16日 3:58
  • 御回答ありがとうございます。教示いただいた回答を元に、MSDNドキュメントの一文字一句を理解しながら読み進めていますが解決できません。

    「サンプルを見れば、ヘッダクリック時のイベントハンドラは基本的にソート対象を抽出しているだけで、実際にソートしているのは Sort メソッドであること、Sort メソッドにおいてソート処理の主体は ICollectionView であることは分かると思います。  そこから、ICollectionView→データバインディングの概要→データバインディングに関する「方法」トピック →ビュー内のデータを並べ替える → CustomSort 、といった流れになりますかね。」とありますが、ICollectionView インターフェイスで公開されるメンバー、及びSortDescriptionsプロパティーのドキュメントにも、sort()メソッドについての記述がありません。

    「実際にソートしているのは Sort メソッドであること」 ←この部分の引用元が分からないです。

    listviewは、表示ソースとなるコレクションクラスをディスプレーするための容器として考え、ソートはコレクションクラスに対して行った後にlistviewの表示をリフレッシュするという作戦に変更しようかと考えています。こうすれば、コレクションクラスにはarray()の様な初歩的なクラスが利用でき、思いのままにカスタムソートができるからです。皆さん、どう思われますでしょうか。

    2011年11月19日 13:03
  • 「実際にソートしているのは Sort メソッドであること」 ←この部分の引用元が分からないです。

    最初にリンクしていた このページ の下の方に書かれていませんか?
    見つからないのであれば、「Private Sub Sort」で検索してみてください。

    このサンプルでは Window1 に独自の Sort メソッドを作っており、GridViewColumnHeaderClickedHandler メソッドから呼び出しています。
    肝心なのはこの Sort メソッド内で使っている ICollectionView に対する操作なので、「Sort メソッド」を探すことではありません。

    # 具体的方法は知りませんが、ページの読み方に関してだったのでレスをつけました。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年11月19日 13:09
    モデレータ
  • ListCollectionView.CustomSortのサンプルソースが海外のサイトにありましたので、参考にして完成させました。

    WPFについてはまだ全然知識がありませんが、着実に経験を積んでいきたいと思います。回答されたお二方には大変感謝します。

    *XAML*

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <StackPanel Height="305" Name="StackPanel1" Width="439" >
            <!--<ListView Height="98" Name="ListView1" Width="269" VerticalAlignment="Top" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler" />-->
            <ListView Height="98" Name="ListView1" Width="269" VerticalAlignment="Top" />
            <Button Content="Button" Height="37" Name="Button1" Width="60" VerticalAlignment="Top" />
            </StackPanel>
    </Window>

    *VB*

    Imports System.ComponentModel
    Imports System.Collections.ObjectModel

    Class MainWindow

        Dim S_direction As Integer = 1
        Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            Dim lv1 As ListView = Me.ListView1
            Dim gvc As GridViewColumn
            Dim myGridView As New GridView()

            gvc = New GridViewColumn()
            gvc.Header = "Num"
            gvc.Width = 100

            myGridView.Columns.Add(gvc)

            myGridView.Columns(0).DisplayMemberBinding = New Binding("Num")

            lv1.View = myGridView
            lv1.Items.Clear()
            lv1.ItemsSource = New LVsource
        End Sub

        ' Itemsource class
        Public Class LVsource
            Inherits ObservableCollection(Of Str0)

            Public Sub New()
                Add(New Str0("1"))
                Add(New Str0("11"))
                Add(New Str0("9"))
                Add(New Str0("3"))
            End Sub

        End Class

        'custom structure
        Public Structure Str0
            Public _num As String

            Public Sub New(ByRef s1 As String)
                Me._num = s1
            End Sub
            Public Property Num() As String
                Get
                    Return _num
                End Get
                Set(ByVal value As String)
                    _num = value
                End Set
            End Property
        End Structure

        Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
            Dim dataView As ListCollectionView = CType(CollectionViewSource.GetDefaultView(Me.ListView1.ItemsSource), ListCollectionView)

            dataView.CustomSort = New SortByNum(S_direction)
            S_direction = -S_direction
        End Sub

        Public Class SortByNum
            Implements IComparer
            Dim sd As Integer
            Sub New(i As Integer)
                sd = i
            End Sub

            Public Function Compare(x As Object, y As Object) As Integer Implements System.Collections.IComparer.Compare
                If Integer.Parse(CType(x, Str0).Num) > Integer.Parse(CType(y, Str0).Num) Then
                    Return (-sd)
                Else
                    Return (sd)
                End If
            End Function
        End Class

    End Class

    • 回答としてマーク 馋涎欲滴 2011年11月21日 14:17
    • 回答としてマークされていない 馋涎欲滴 2011年11月21日 14:18
    2011年11月21日 14:16