【C#】【Form】 DataGridView ~ ソート編 ~

■ はじめに

 DataGridView のソートについて、纏める

【1】 ソートを禁止するには

 * DataGridViewColumn.SortMode を、NotSortableに設定する

サンプル

this.dataGridView1.Columns["Age"].SortMode = DataGridViewColumnSortMode.NotSortable;
補足:DataGridViewColumn.SortModeプロパティ
 * DataGridViewColumnSortMode.NotSortable  : ソート禁止
 * DataGridViewColumnSortMode.Automatic    : 自動的にソート ※1
 * DataGridViewColumnSortMode.Programmatic : 自分で並び替えを行う

※1
ただし、Automatic を設定したとしても独自クラスのリストをバインドする場合、ソートは効かない
(詳細は、以下「■ 独自クラスのリストをバインドした場合にソートできるようにする」を参照)

【2】 独自のソートを実装するには

[2-1] SortメソッドでIComparerを指定して呼び出す方法
[2-2] SortCompareイベントを処理する方法
[2-3] DataView.Sortプロパティを使用する方法

使用上の注意

[2-1]/[2-2]の使用条件について
 * 使用する際は、以下の条件がある
  a) DataSourceプロパティが設定されていない
  b) VirtualModeプロパティが false の場合

 => DataSource を設定している場合は、[2-3]を使用する

 * DataGridViewColumn.SortModeプロパティは以下の通り
  a) [2-1]の場合、SortMode = Programmatic にする
  b) [2-2]の場合、SortMode = Automatic にする (SortMode = Programmatic だと動かなかった)

[2-3] DataView.Sortプロパティを使用する方法

// SQLのOrderByみたいに
dataTable.DefaultView.Sort = "Name DESC, ...";
サンプル
using System;
using System.Data;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form2 : Form
  {
    public Form2()
    {
      InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
      // DataTableを宣言
      var dataTable = new DataTable("Person");

      // 列を追加
      dataTable.Columns.Add("ID", typeof(long));
      dataTable.Columns.Add("Name", typeof(string));
      dataTable.Columns.Add("Age", typeof(int));

      // DataTableにデータ追加
      dataTable.Rows.Add(1L, "Mike", 23);
      dataTable.Rows.Add(2L, "Tom", 18);
      dataTable.Rows.Add(3L, "Smith", 32);
      dataTable.Rows.Add(4L, "Naomi", 56);
      dataTable.Rows.Add(5L, "Kevin", 45);

      this.dataGridView1.DataSource = dataTable;

      foreach (DataGridViewColumn dataGridViewColumn in this.dataGridView1.Columns)
      {
        dataGridViewColumn.SortMode = DataGridViewColumnSortMode.Programmatic;
      }
    }

    // ヘッダー部クリックイベント
    private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
      var dataGridView = (DataGridView)sender;

      DataTable dataTable = (DataTable)dataGridView.DataSource;

      if (dataGridView.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending)
      {
        // ここに、独自のソートを書く(今は普通にソート)
        dataTable.DefaultView.Sort = dataGridView.Columns[e.ColumnIndex].Name;
        dataGridView.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
      }
      else
      {
        // ここに、独自のソートを書く(今は普通にソート)
        dataTable.DefaultView.Sort = dataGridView.Columns[e.ColumnIndex].Name + " DESC";
        dataGridView.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending;
      }
    }
  }
}
サンプル(抜粋) : 複数行でのソート
    private void Form1_Load(object sender, EventArgs e)
    {
      // DataTableを宣言
      var dataTable = new DataTable("Person");

      // 列を追加
      dataTable.Columns.Add("ID", typeof(long));
      dataTable.Columns.Add("Name", typeof(string));
      dataTable.Columns.Add("Age", typeof(int));

      // DataTableにデータ追加
      dataTable.Rows.Add(1L, "Mike", 23);
      dataTable.Rows.Add(2L, "Tom", 18);
      dataTable.Rows.Add(3L, "Smith", 32);
      dataTable.Rows.Add(4L, "Naomi", 56);
      dataTable.Rows.Add(5L, "Kevin", 45);

      DataView dataView = dataTable.DefaultView;
      dataView.Sort = "ID, Age ASC";

      this.dataGridView1.DataSource = dataTable;

      //2つの列のヘッダーに並び替えグリフを表示する
      this.dataGridView1.Columns["ID"].HeaderCell.SortGlyphDirection =
          SortOrder.Ascending;
      this.dataGridView1.Columns["Age"].HeaderCell.SortGlyphDirection =
          SortOrder.Ascending;
    }

【3】 独自クラスのリストをバインドした場合にソートできるようにする

DataGridView の使用上の注意
 * 独自クラスのリストをバインドする場合、ソートが効かなくなる

サンプル:ソートが効かなくなる例

ヘッダーをクリックしてもソートされない
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      var list = new List<Person>()
      {
        new Person(1L, "Mike", 23),
        new Person(2L, "Tom", 18),
        new Person(3L, "Smith", 32),
        new Person(4L, "Naomi", 56),
        new Person(5L, "Kevin", 45)
      };

      this.dataGridView1.DataSource = list;
    }
  }

  // 独自クラス
  public class Person
  {
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(long id, string name, int age)
    {
      this.Id = id;
      this.Name = name;
      this.Age = age;
    }
  }
}

解決案:ソート可能なバインディングリストを独自で作成する

 * 以下のサイトが参考になる
https://garafu.blogspot.com/2016/09/cs-sorablebindinglist.html

番外編:独自クラスではなくDataTable等に置き換える

 * コードは、上記の「[2-3] DataView.Sortプロパティを使用する方法」を参照。


関連記事

Windows Form ~ 目次 ~

https://blogs.yahoo.co.jp/dk521123/8054245.html

DataGridView

DataGridView ~ プロパティ編 ~
https://blogs.yahoo.co.jp/dk521123/14718079.html
DataGridView ~イベント編 ~
https://blogs.yahoo.co.jp/dk521123/23687833.html
DataGridView ~ 画像・アイコン編 ~
https://blogs.yahoo.co.jp/dk521123/22293894.html
DataGridView を Label のように扱う
https://blogs.yahoo.co.jp/dk521123/29362064.html
DataGridView に ACCESS のデータを表示させる
https://blogs.yahoo.co.jp/dk521123/32859068.html
DataGridView に右クリックを適用する
https://blogs.yahoo.co.jp/dk521123/30488275.html