WPFのListViewでヘッダークリックでソート2 – ListViewSortBehavior

C# コンピュータ
C#

ListViewのヘッダークリックでソートするコードを確認しました。
再利用する場合、コードビハインドだと中身を理解するのが大変なので、ListViewSortBehaviorを作ることで再利用しやすくしてみました。

ソースコード

ファイル名:FileInfoModel.cs

public class FileInfoModel
{
    public string Name { get; set; } = "";
    public int Length { get; set; } = 0;    
}

ファイル名:ListViewSortBehavior.cs

using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Xaml.Behaviors;

namespace ListViewSort02;

public class ListViewSortBehavior : Behavior<ListView>
{
    private ListSortDirection _sortDirection;
    private string _sortPropertyName = "";

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(HeaderClicked));
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.RemoveHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(HeaderClicked));
    }

    private void HeaderClicked(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource is GridViewColumnHeader header && header.Column != null )
        {
            string? propertyName = header.Column.GetValue(SortPropertyNameProperty) as string;
            if (propertyName is null || string.IsNullOrEmpty(propertyName))
            {
                return;
            }

            if (_sortPropertyName == propertyName)
            {
                _sortDirection = _sortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
            }
            else
            {
                _sortPropertyName = propertyName;
                _sortDirection = ListSortDirection.Ascending;
            }

            Sort(propertyName, _sortDirection);
        }
    }

    private void Sort(string propertyName, ListSortDirection direction)
    {
        var files = AssociatedObject.ItemsSource;
        var collectionView = CollectionViewSource.GetDefaultView(files);


        collectionView.SortDescriptions.Clear();
        collectionView.SortDescriptions.Add(new SortDescription(propertyName, direction));
    }

    public static readonly DependencyProperty SortPropertyNameProperty =
        DependencyProperty.RegisterAttached("SortPropertyName", typeof(string), typeof(ListViewSortBehavior));

    public static string GetSortPropertyName(DependencyObject obj)
    {
        return (string)obj.GetValue(SortPropertyNameProperty);
    }

    public static void SetSortPropertyName(DependencyObject obj, string value)
    {
        obj.SetValue(SortPropertyNameProperty, value);
    }
}

ファイル名:MainWindow.xaml

<Window
    x:Class="ListViewSort02.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ListViewSort02"
    mc:Ignorable="d"
    Title="ListViewSort02"
    Height="450" Width="800"
    xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors">
  <Window.DataContext>
    <local:MainWindowViewModel />
  </Window.DataContext>
  <Grid>
    <ListView ItemsSource="{Binding Files}">
        <i:Interaction.Behaviors>
            <local:ListViewSortBehavior />
        </i:Interaction.Behaviors>
        <ListView.View>
            <GridView>
                <GridViewColumn Width="200"
                    local:ListViewSortBehavior.SortPropertyName="Name">
                    <GridViewColumnHeader
                        x:Name="HeadName1"
                        Content="名前" />
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                              <TextBlock
                                Width="100"
                                TextAlignment="Left"
                                Text="{Binding Name}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Width="200"
                    local:ListViewSortBehavior.SortPropertyName="Length">                    
                    <GridViewColumnHeader
                        x:Name="HeadLength1"
                        Content="長さ" />
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                              <TextBlock
                                Width="100"
                                TextAlignment="Right"
                                Text="{Binding Length}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>    
        </ListView.View>
    </ListView>
  </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using System.ComponentModel;
using Reactive.Bindings;

namespace ListViewSort02;
public class MainWindowViewModel : INotifyPropertyChanged
{
    // INotifyPropertyChanged
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged(string name) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    /**************************************************************************
    * プロパティ
    **************************************************************************/

    // ファイルの一覧
    public ReactiveCollection<FileInfoModel> Files { get; private set; } = [];

    public MainWindowViewModel()
    {
        PropertyChanged += (s, e) => {};

        Files.AddOnScheduler(new FileInfoModel(){Name = "a.txt", Length = 99});
        Files.AddOnScheduler(new FileInfoModel(){Name = "b.txt", Length = 88});
        Files.AddOnScheduler(new FileInfoModel(){Name = "c.txt", Length = 77});
    }
}

実行

説明

前回のコードビハインドで書いたSortのコードが、View内で完結しているのでBehaviorに切り出せるのではないかと思い試してみました。
合わせてViewModelを書いてMVVMパターンにしてみました。

コメント