コラムヘッダーのクリックイベントを用意する
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
これで、まぁまぁイイ感じにソートできるようになったと思います。
|
ソート前 |
ソート後 |



