【C# WPF DataGrid】メンバ変数名でソートする

本記事では、メンバ変数名でリストをソートする方法を書きます。

この方法は、WPFアプリケーションのDataGridをソートするときなどに役立ちます。

開発環境
  • Windows10
  • Microsoft Visual Studio Community2022
  • .NET 6
  • WPF アプリ
目次

リストをソートする方法

通常のソート

例えば、↓のリストがあるとき

private List<FriutsViewModel> FriutListViewModel = new List<FriutsViewModel>()
{
    new FriutsViewModel("りんご", "赤", 148),
    new FriutsViewModel("みかん", "オレンジ", 130),
    new FriutsViewModel("キウイ", "緑", 136),
    new FriutsViewModel("ぶどう", "赤", 540),
    new FriutsViewModel("かき", "オレンジ", 120),
    new FriutsViewModel("ばなな", "黄", 108),
};

internal class FriutsViewModel
{
    public string name { get; }
    public string color { get; }
    public int price { get; }

    public FriutsViewModel(string name, string color, int price)
    {
        this.name = name;
        this.color = color;
        this.price = price;
    }
}

通常ソートする場合は、

を使用します。使用方法としては、

// 価格(price) で昇順( 小さい→大きい ) にソート
FriutListViewModel = FriutListViewModel.OrderBy(viewModel => viewModel.price).ToList();

// 色(color) で降順( 大きい→小さい ) にソート
FriutListViewModel = FriutListViewModel.OrderByDescending(viewModel => viewModel.color).ToList();

となります。このコードでソートが実行できます。

メンバ変数名でソート

次は、メンバ変数名でソートします。ソートするメソッドは同じですメンバ変数名とクラスの型を引数としてソートできます

以下メソッドを使用します。

先ほどのコードは↓のように書き換えられます。

// 価格(price) で昇順( 小さい→大きい ) にソート
FriutListViewModel = FriutListViewModel.OrderBy(viewModel =>
{
    return typeof(FriutsViewModel).GetProperty("price")?.GetValue(viewModel);
}).ToList();

// 色(color) で降順( 大きい→小さい ) にソート
FriutListViewModel = FriutListViewModel.OrderByDescending(viewModel =>
{
    return typeof(FriutsViewModel).GetProperty("color")?.GetValue(viewModel);
}).ToList();

先ほどと同じ結果となります。

DataGrid のソート

メンバ変数名のソートはDataGrid のソートで役立ちます。

↓はDataGridのソートをオリジナルで実装したときの記事です。

DataGrid のヘッダーをクリックしたとき、取得できるのはリストのメンバ変数名です

色をクリックした場合、メンバ変数名”color” を取得できます。このメンバ変数名を使ってソートする必要があります

メンバ変数名だけでは、↓のように、ネット上でよく見かけるOrderByの方法ではソートできません

FriutListViewModel = FriutListViewModel.OrderBy(viewModel => viewModel.color).ToList();

Xamlファイル

xaml ファイルです。それぞれの列に対して、DataGridTextColumnタグのBinding属性で名称を設定しています。ヘッダークリックしたとき、この名称を文字列で取得できます。

設定する名称は、紐づくリストのメンバ変数名と一致させなければなりません。

MainWindow.xaml
<Window x:Class="DataGridProject.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:DataGridProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="500">
    <Grid x:Name="Grid_FruitsList">
        <Grid.Resources>
            <FrameworkElement x:Key="proxyElement" />
        </Grid.Resources>
        <ContentControl Visibility="Collapsed" Content="{StaticResource proxyElement}" />
        <DataGrid Margin="28,29,250,30"
                  AutoGenerateColumns = "False"
                  IsReadOnly="True" 
                  SelectionMode="Single"
                  RowHeaderWidth="0"
                  ItemsSource="{Binding Path=_friutsModelList}"
                  Sorting="DataGrid_Sorting" >
            <!-- 選択時に_isSelectedの値変更 -->
            <DataGrid.RowStyle>
                <Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
                    <Setter Property="IsSelected" Value="{Binding _isSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                </Style>
            </DataGrid.RowStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Width="100" Binding="{Binding _name}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="Content" Value="{Binding Path=DataContext._nameHeader, Source={StaticResource proxyElement}}"/>
                            <Setter Property="Background" Value="Blue"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                </DataGridTextColumn>
                <DataGridTextColumn Width="50" Binding="{Binding _color}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="Content" Value="{Binding Path=DataContext._colorHeader, Source={StaticResource proxyElement}}"/>
                            <Setter Property="Background" Value="Blue"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                </DataGridTextColumn>
                <DataGridTextColumn Width="50" Binding="{Binding _price}">
                    <DataGridTextColumn.HeaderStyle>
                        <Style TargetType="DataGridColumnHeader">
                            <Setter Property="Content" Value="{Binding Path=DataContext._priceHeader, Source={StaticResource proxyElement}}"/>
                            <Setter Property="Background" Value="Blue"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Style>
                    </DataGridTextColumn.HeaderStyle>
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
        <GroupBox Margin="273,33,30,55" Header="選択中"/>
        <TextBlock HorizontalAlignment="Left" Margin="280,56,0,0" TextWrapping="Wrap" Text="名前 : " VerticalAlignment="Top" RenderTransformOrigin="0,-0.937"/>
        <TextBox x:Name="TextBox_Name" HorizontalAlignment="Left" Margin="325,56,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
        <TextBlock HorizontalAlignment="Left" Margin="280,89,0,0" TextWrapping="Wrap" Text="色 : " VerticalAlignment="Top" RenderTransformOrigin="0.603,2.195"/>
        <TextBox x:Name="TextBox_Color" HorizontalAlignment="Left" Margin="325,89,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
        <TextBlock HorizontalAlignment="Left" Margin="280,124,0,0" TextWrapping="Wrap" Text="価格 : " VerticalAlignment="Top"/>
        <TextBox x:Name="TextBox_Price" HorizontalAlignment="Left" Margin="325,122,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
    </Grid>
</Window>

DataGridクリックで呼ばれるイベントハンドラ

クリックしたヘッダー列に対し、その位置に対応するBinding 属性で設定した名称を引数として取得できます

MainWindow.xaml.cs ヘッダークリックで呼ばれるメソッド
/// <summary>
/// ヘッダーがクリックされたとき呼び出し
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
    e.Handled = true;

    string sortMemberPath = e.Column.SortMemberPath;

    dataGridSortDirector.Sort(sortMemberPath);
}

DataGridSortingEventArgs のcolum.SortMemberPathが、先ほどBinding属性に設定した名称の文字列となっています。

DataGridのDataGrid.Columns のColumn要素に紐づくクラス

クラスのメンバ変数名と、Binding属性に設定した名称は同じにしています

FriutsDataGridColumnModel.cs
namespace DataGridProject.FriutListDataGrid
{
    /// <summary>
    /// フルーツデータグリッド列モデル
    /// </summary>
    /// <remarks>MainWindow.xaml→DataGrid.Columns のColumn要素と対応</remarks>
    public class FriutsDataGridColumnModel
    {
        /// <summary>
        /// 選択状態
        /// </summary>
        public bool _isSelected { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string _name { get; set; }

        /// <summary>
        /// 色
        /// </summary>
        public string _color { get; set; }

        /// <summary>
        /// 価格
        /// </summary>
        public int _price { get; set; }
    }
}

DataGridと紐づくクラス

メンバ変数名に対するソートはここで行っています。Binding属性で名称を引数にして、ソートを行っています。

FriutsListGridModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace DataGridProject.FriutListDataGrid
{
    /// <summary>
    /// フルーツリストグリッドモデル
    /// </summary>
    /// <remarks>MainWindow.xaml→Grid_FruitsList と対応</remarks>
    public class FriutsListGridModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        /// <summary>
        /// 名称ヘッダー
        /// </summary>
        public string _nameHeader { get; set; }

        /// <summary>
        /// 色ヘッダー
        /// </summary>
        public string _colorHeader { get; set; }

        /// <summary>
        /// 価格ヘッダー
        /// </summary>
        public string _priceHeader { get; set; }

        /// <summary>
        /// フルーツデータグリッド列モデル一覧
        /// </summary>
        public ObservableCollection<FriutsDataGridColumnModel> _friutsModelList { get; set; }

        /// <summary>
        /// 降順にソートする
        /// </summary>
        /// <param name="sortMemberPath">ソートメンバーパス</param>
        public void SortByDescending( string sortMemberPath)
        {
            _friutsModelList = new ObservableCollection<FriutsDataGridColumnModel>(
                _friutsModelList.OrderByDescending(c => typeof(FriutsDataGridColumnModel).GetProperty(sortMemberPath)?.GetValue(c))
                );
            NotifyPropertyChanged(nameof(FriutsListGridModel._friutsModelList));
        }

        /// <summary>
        /// 昇順にソートする
        /// </summary>
        /// <param name="sortMemberPath">ソートメンバーパス</param>
        public void SortByAscending(string sortMemberPath)
        {
            _friutsModelList = new ObservableCollection<FriutsDataGridColumnModel>(
                _friutsModelList.OrderBy(c => typeof(FriutsDataGridColumnModel).GetProperty(sortMemberPath)?.GetValue(c))
                );
            NotifyPropertyChanged(nameof(FriutsListGridModel._friutsModelList));
        }

        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
}

プロジェクト一覧は↓から取得できます。

GitHub
GitHub - HomeProgrammer81/DataGridProject at main_SortOriginal Contribute to HomeProgrammer81/DataGridProject development by creating an account on GitHub.

まとめ

メンバ変数名でリストをソートする方法を書きました。

この方法は、WPFアプリケーションのDataGridをソートするときなどに役立ちます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次