コラムヘッダーのクリックイベントを用意する
ListView のコラムヘッダーをクリックして ListView のデータをソートするには、まずコラムヘッダーの Click イベントを用意する。
まず、コードに空の Click イベント実行関数を記述する(ListView を含むウィンドウのクラス内に記述)。
Private Sub DayOfTheWeek_Click(sender As Object, e As RoutedEventArgs) End Sub Private Sub StartTime_Click(sender As Object, e As RoutedEventArgs) End Sub Private Sub EndTime_Click(sender As Object, e As RoutedEventArgs) End Sub
次に、「【VB.NET】【WPF】ListView の基本的な使い方」の記事で示した ListView の xaml に下記の Click イベントを追加します。
<GridViewColumnHeader Content="曜日" Click="DayOfTheWeek_Click"/>
<GridViewColumnHeader Content="開始時刻" Click="StartTime_Click" />
<GridViewColumnHeader Content="終了時刻" Click="EndTime_Click" />
各列のクリックイベントの処理をまとめる
前述のコード(3つのイベント実行関数)から1つのソート処理関数を呼び出すように追記しましょう。次のように書き加えます。
Enum ScheduleSortEnum Undefined By_DayOfTheWeek By_StartTime By_EndTime End Enum Private Sub DayOfTheWeek_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(ScheduleSortEnum.By_DayOfTheWeek) End Sub Private Sub StartTime_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(ScheduleSortEnum.By_StartTime) End Sub Private Sub EndTime_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(ScheduleSortEnum.By_EndTime) End Sub Friend Sub ScheduleSort(SortBy As ScheduleSortEnum) 'ここでソート処理を行う End Sub
クリックイベントの具体的な処理内容を記述する
前述の最後の関数(ScheduleSort)の中身を記述します(下記コードの1-2行目にグローバル変数も2つ追加しています)。
下記コード内の SortClass はまだ定義していないのでエラーになりますが、このあとで定義します。
Dim PreSortDirection As TriState = TriState.UseDefault Dim PreSortBy As ScheduleSortEnum = ScheduleSortEnum.Undefined Friend Sub ScheduleSort(SortBy As ScheduleSortEnum) 'ここで各列がクリックされたときのソート処理を行う Dim lv As ListView = Me.ScheduleListView If Me.ScheduleListView.Items.Count = 0 Then Return End If 'デフォルトのビューを取得する Dim oView As ListCollectionView = CType(CollectionViewSource.GetDefaultView(lv.ItemsSource), ListCollectionView) '最初のソートの場合、またはソート対象のコラムが前回と違う場合は前回値を初期化 If oView.CustomSort Is Nothing OrElse PreSortBy <> SortBy Then PreSortBy = ScheduleSortEnum.Undefined PreSortDirection = TriState.UseDefault End If 'ソート方向(デフォルト・昇順・降順)を決定 Dim SortDirection As TriState = TriState.UseDefault If PreSortDirection = TriState.True Then SortDirection = TriState.False ElseIf PreSortDirection = TriState.False Then SortDirection = TriState.UseDefault ElseIf PreSortDirection = TriState.UseDefault Then SortDirection = TriState.True End If 'ソート If SortDirection = TriState.UseDefault Then oView.CustomSort = Nothing 'ソート解除 Else oView.CustomSort = New SortClass(SortDirection, SortBy) ' <-- SortClass はこの後で定義する。 End If PreSortBy = SortBy PreSortDirection = SortDirection End Sub
IComparer を実装したソートクラスを定義する
そしてソートの心臓部である SortClass クラスを記述します。
Private Class SortClass Implements System.Collections.IComparer Dim SortDirection As TriState Dim SortBy As ScheduleSortEnum Sub New(_SortDirection As TriState, _SortBy As ScheduleSortEnum) SortDirection = _SortDirection SortBy = _SortBy End Sub Public Function Compare(_x As Object, _y As Object) As Integer _ Implements System.Collections.IComparer.Compare Dim x As ScheduleItemClass = _x Dim y As ScheduleItemClass = _y Dim result As Integer If SortBy = ScheduleSortEnum.By_DayOfTheWeek Then result = x.DayOfTheWeek.CompareTo(y.DayOfTheWeek) ElseIf SortBy = ScheduleSortEnum.By_StartTime Then result = x.StartTime.CompareTo(y.StartTime) ElseIf SortBy = ScheduleSortEnum.By_EndTime Then result = x.EndTime.CompareTo(y.EndTime) End If If result > 0 Then If sortDirection = TriState.True Then Return 1 Else Return -1 End If ElseIf result < 0 Then If sortDirection = TriState.True Then Return -1 Else Return 1 End If Else Return 0 End If End Function End Class
これで ListView の各列をクリックするとソートできるはずです。実行して試してみてください。
月火水木金土日を正しくソートする
ソートできましたか?
えっ?「ソートできたけど、曜日の列のソート順が変だよ」って?
・・・そうでした。
「月火水木金土日」を普通にソートしても意図した通りに並んでくれないんですよね。
というわけで、意図した通りに並ぶように、前述の SortClass クラスの中に下記のディクショナリを追記して・・・
Dim YobiDic As New Dictionary(Of String, Integer) From { {"月", 0}, {"火", 1}, {"水", 2}, {"木", 3}, {"金", 4}, {"土", 5}, {"日", 6} }
前述の SortClass の21行目を下記のように書き換えましょう。
result = YobiDic(x.DayOfTheWeek).CompareTo(YobiDic(y.DayOfTheWeek))
これで意図した通りに曜日がソートされるようになりましたね。
コラムヘッダーに昇順・降順を示す▲▼を表示する
でもまだ何か足りないものがありますね。
このままだと、列ヘッダをクリックしても、現在どういう状態(昇順? 降順?)でソートされているのか分かりにくい。
というわけで、列ヘッダに昇順・降順を示す▲▼が表示されるようにしてみましょう。
リソースディクショナリに▲▼の記号を定義する
まず Visual Basic の上部メニューで[プロジェクト]>[リソース ディクショナリの追加]を選択してリソースディクショナリを追加します。
リソースディクショナリ(ここでは Dictionary1.xaml とします)に▲▼の記号を表す下記の xaml を記述します。
<!-- ListView のコラムヘッダで使用するソートの昇順・降順マーク --> <DataTemplate x:Key="HeaderTemplateArrowUp"> <DockPanel> <TextBlock HorizontalAlignment="Center" Text="{Binding}"/> <Path x:Name="arrow" StrokeThickness = "1" Fill = "gray" Data = "M 6,12 L 14,12 L 10,4 L 6,12"/> </DockPanel> </DataTemplate> <DataTemplate x:Key="HeaderTemplateArrowDown"> <DockPanel> <TextBlock HorizontalAlignment="Center" Text="{Binding}"/> <Path x:Name="arrow" StrokeThickness = "1" Fill = "gray" Data = "M 6,4 L 10,12 L 14,4 L 6,4"/> </DockPanel> </DataTemplate>
リソースディクショナリへの参照を定義する
次に、ウィンドウの xaml に下記(リソースディクショナリへの参照)を記述します(<Window …> タグの下あたりに記述するのが良いでしょう)。
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <!-- 外部リソースの取り込み --> <ResourceDictionary Source="Dictionary1.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources>
コードの追加
そして記号を表示できるように ScheduleSort 関数を書き換えます。下記のハイライト部分が変更箇所です。
Dim PreSortDirection As TriState = TriState.UseDefault Dim PreSortBy As ScheduleSortEnum = ScheduleSortEnum.Undefined Dim PreSortColumnHeader As GridViewColumnHeader = Nothing Friend Sub ScheduleSort(oColumnHeader As GridViewColumnHeader, SortBy As ScheduleSortEnum) 'ここで各列がクリックされたときのソート処理を行う Dim lv As ListView = Me.ScheduleListView If Me.ScheduleListView.Items.Count = 0 Then Return End If 'デフォルトのビューを取得する Dim oView As ListCollectionView = CType(CollectionViewSource.GetDefaultView(lv.ItemsSource), ListCollectionView) '最初のソートの場合、またはソート対象のコラムが前回と違う場合は前回値を初期化 If oView.CustomSort Is Nothing OrElse PreSortBy <> SortBy Then PreSortBy = ScheduleSortEnum.Undefined PreSortDirection = TriState.UseDefault If preSortColumnHeader IsNot Nothing Then 'ソート記号(▼▲)を消す preSortColumnHeader.Column.HeaderTemplate = Nothing End If End If 'ソート方向(デフォルト・昇順・降順)を決定 Dim SortDirection As TriState = TriState.UseDefault If PreSortDirection = TriState.True Then SortDirection = TriState.False ElseIf PreSortDirection = TriState.False Then SortDirection = TriState.UseDefault ElseIf PreSortDirection = TriState.UseDefault Then SortDirection = TriState.True End If 'ソート対象のコラムのソート記号(▼▲)を決定する If SortDirection = TriState.True Then oColumnHeader.Column.HeaderTemplate = DirectCast(FindResource("HeaderTemplateArrowUp"), DataTemplate) ElseIf SortDirection = TriState.False Then oColumnHeader.Column.HeaderTemplate = DirectCast(FindResource("HeaderTemplateArrowDown"), DataTemplate) Else 'ソート記号(▼▲)を消す oColumnHeader.Column.HeaderTemplate = Nothing End If 'ソート If SortDirection = TriState.UseDefault Then oView.CustomSort = Nothing 'ソート解除 Else oView.CustomSort = New SortClass(SortDirection, SortBy) End If PreSortBy = SortBy PreSortDirection = SortDirection PreSortColumnHeader = oColumnHeader End Sub
3つのクリックイベントの実行関数も修正して、ScheduleSort の第1引数に sender (実体は GridViewColumnHeader)を指定します。
Private Sub DayOfTheWeek_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(sender, ScheduleSortEnum.By_DayOfTheWeek) End Sub Private Sub StartTime_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(sender, ScheduleSortEnum.By_StartTime) End Sub Private Sub EndTime_Click(sender As Object, e As RoutedEventArgs) 'ソート処理関数を呼び出す Call ScheduleSort(sender, ScheduleSortEnum.By_EndTime) End Sub
これで、まぁまぁイイ感じにソートできるようになったと思います。
ソート前 |
ソート後 |