今回はリストビューのヘッダークリックでソートし、且つマークを表示してみます。
ファイルの一覧なので、ファイル名とサイズのソートをちょっと変えています。
プログラムは、下記の記事のものを流用します。
ListViewItemSorter.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public class ListViewItemSorter : IComparer
{
/// <summary>
/// 比較する方法
/// </summary>
public enum ComparerMode
{
/// <summary>
/// 文字列として比較
/// </summary>
String,
/// <summary>
/// 数値(Int32型)として比較
/// </summary>
Integer,
/// <summary>
/// 日時(DataTime型)として比較
/// </summary>
DateTime,
/// <summary>
/// ファイル名として比較
/// </summary>
FileName,
/// <summary>
/// ファイルサイズとして比較
/// </summary>
FileSize
};
private ComparerMode[] _columnModes;
private int _column;
/// <summary>
/// 並び替えるListView列の番号
/// </summary>
public int Column
{
set
{
//現在と同じ列の時は、昇順降順を切り替える
if (_column == value)
{
if (Order == SortOrder.Ascending)
{
Order = SortOrder.Descending;
}
else if (Order == SortOrder.Descending)
{
Order = SortOrder.Ascending;
}
}
_column = value;
}
get
{
return _column;
}
}
/// <summary>
/// 昇順か降順か
/// </summary>
public SortOrder Order { set; get; }
/// <summary>
/// 並び替えの方法
/// </summary>
public ComparerMode Mode { set; get; }
/// <summary>
/// 列ごとの並び替えの方法
/// </summary>
public ComparerMode[] ColumnModes
{
set
{
_columnModes = value;
}
}
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="col">並び替える列の番号</param>
/// <param name="ord">昇順か降順か</param>
/// <param name="cmod">並び替えの方法</param>
public ListViewItemSorter(int col, SortOrder ord, ComparerMode cmod)
{
Column = col;
Order = ord;
Mode = cmod;
}
public ListViewItemSorter()
{
Column = 0;
Order = SortOrder.Ascending;
Mode = ComparerMode.String;
}
//xがyより小さいときはマイナスの数、大きいときはプラスの数、
//同じときは0を返す
public int Compare(object x, object y)
{
if (Order == SortOrder.None)
{
//並び替えない時
return 0;
}
int result = 0;
//ListViewItemの取得
ListViewItem itemx = (ListViewItem)x;
ListViewItem itemy = (ListViewItem)y;
//並べ替えの方法を決定
if (_columnModes != null && _columnModes.Length > Column)
{
Mode = _columnModes[Column];
}
//並び替えの方法別に、xとyを比較する
switch (Mode)
{
case ComparerMode.String:
//文字列をとして比較
result = string.Compare(itemx.SubItems[Column].Text,
itemy.SubItems[Column].Text);
break;
case ComparerMode.Integer:
//Int32に変換して比較
//.NET Framework 2.0からは、TryParseメソッドを使うこともできる
result = int.Parse(itemx.SubItems[Column].Text).CompareTo(
int.Parse(itemy.SubItems[Column].Text));
break;
case ComparerMode.DateTime:
//DateTimeに変換して比較
//.NET Framework 2.0からは、TryParseメソッドを使うこともできる
result = DateTime.Compare(
DateTime.Parse(itemx.SubItems[Column].Text),
DateTime.Parse(itemy.SubItems[Column].Text));
break;
case ComparerMode.FileName:
result = CompareFileName(itemx.Name, itemy.Name);
break;
case ComparerMode.FileSize:
result = CompareFileSize(itemx.SubItems[Column].Text, itemy.SubItems[Column].Text);
break;
}
//降順の時は結果を+-逆にする
if (Order == SortOrder.Descending)
{
result = -result;
}
//結果を返す
return result;
}
/// <summary>
/// ファイル名の比較をします.
/// </summary>
/// <param name="path1"></param>
/// <param name="path2"></param>
/// <returns></returns>
private int CompareFileName(string path1, string path2)
{
FileInfo fi1 = new FileInfo(path1);
FileInfo fi2 = new FileInfo(path2);
if (fi1.Attributes == FileAttributes.Directory && fi2.Attributes != FileAttributes.Directory)
{
return -1;
}
else if (fi1.Attributes != FileAttributes.Directory && fi2.Attributes == FileAttributes.Directory)
{
return 1;
}
return string.Compare(path1, path2);
}
/// <summary>
/// 単位付きのファイルサイズを比較します.
/// </summary>
/// <param name="size1"></param>
/// <param name="size2"></param>
/// <returns></returns>
private int CompareFileSize(string sizeWithUnit1, string sizeWithUnit2)
{
string size1 = "0";
string unit1 = "";
string size2 = "0";
string unit2 = "";
List<string> units = new List<string>() { "KB","MB","GB"};
if (!string.IsNullOrEmpty(sizeWithUnit1))
{
size1 = sizeWithUnit1.Split(null)[0];
unit1 = sizeWithUnit1.Split(null)[1];
}
if (!string.IsNullOrEmpty(sizeWithUnit2))
{
size2 = sizeWithUnit2.Split(null)[0];
unit2 = sizeWithUnit2.Split(null)[1];
}
// 単位が違う場合は単位で比較
if (unit1 != unit2)
{
return units.IndexOf(unit1).CompareTo(units.IndexOf(unit2));
}
return decimal.Parse(size1).CompareTo(decimal.Parse(size2));
}
}
}
下記を追加します。
Form1.cs
private ListViewItemSorter ltvFileListSorter = null;
[StructLayout(LayoutKind.Sequential)]
public struct HDITEM
{
public Int32 mask;
public Int32 cxy;
[MarshalAs(UnmanagedType.LPTStr)]
public String pszText;
public IntPtr hbm;
public Int32 cchTextMax;
public Int32 fmt;
public Int32 lParam;
public Int32 iImage;
public Int32 iOrder;
};
// Parameters for ListView-Headers
private const Int32 HDI_FORMAT = 0x0004;
private const Int32 HDF_LEFT = 0x0000;
private const Int32 HDF_STRING = 0x4000;
private const Int32 HDF_SORTUP = 0x0400;
private const Int32 HDF_SORTDOWN = 0x0200;
private const Int32 LVM_GETHEADER = 0x1000 + 31; // LVM_FIRST + 31
private const Int32 HDM_GETITEM = 0x1200 + 11; // HDM_FIRST + 11
private const Int32 HDM_SETITEM = 0x1200 + 12; // HDM_FIRST + 12
/// <summary>
/// リストビューヘッダのイメージ登録
/// </summary>
/// <param name="Handle"></param>
/// <param name="msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "SendMessage")]
private static extern IntPtr SendMessageITEM(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam);
初期化時にソートクラスを設定
Form1.cs
// ソートクラスの設定
ltvFileListSorter = new ListViewItemSorter(0, SortOrder.Ascending, ListViewItemSorter.ComparerMode.String);
ltvFileListSorter.ColumnModes = new ListViewItemSorter.ComparerMode[]
{
ListViewItemSorter.ComparerMode.FileName,
ListViewItemSorter.ComparerMode.String,
ListViewItemSorter.ComparerMode.String,
ListViewItemSorter.ComparerMode.FileSize
};
listView1.ListViewItemSorter = ltvFileListSorter;
// ソートマークの設定
SetSortMark(listView1.Handle, 0, ltvFileListSorter.Order);
リストのヘッダがクリックされた際のイベントを追加
Form1.cs
/// <summary>
/// ファイル一覧列クリックイベントハンドラ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
ltvFileListSorter.Column = e.Column;
listView1.Sort();
// 初期化
foreach (ColumnHeader c in listView1.Columns)
{
SetSortMark(listView1.Handle, c.Index, SortOrder.None);
}
SetSortMark(listView1.Handle, e.Column, ltvFileListSorter.Order);
}
/// <summary>
/// リストビューのヘッダーソートマークの制御.
/// </summary>
/// <param name="listViewHandle"></param>
/// <param name="colIdx"></param>
/// <param name="order"></param>
public static void SetSortMark(IntPtr listViewHandle, int colIdx, SortOrder order)
{
IntPtr hColHeader = SendMessage(listViewHandle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
HDITEM hdItem = new HDITEM();
IntPtr colHeader = new IntPtr(colIdx);
hdItem.mask = HDI_FORMAT;
IntPtr rtn = SendMessageITEM(hColHeader, HDM_GETITEM, colHeader, ref hdItem);
if (order == SortOrder.Ascending)
{
hdItem.fmt &= ~HDF_SORTDOWN;
hdItem.fmt |= HDF_SORTUP;
}
else if (order == SortOrder.Descending)
{
hdItem.fmt &= ~HDF_SORTUP;
hdItem.fmt |= HDF_SORTDOWN;
}
else if (order == SortOrder.None)
{
hdItem.fmt &= ~HDF_SORTDOWN & ~HDF_SORTUP;
}
rtn = SendMessageITEM(hColHeader, HDM_SETITEM, colHeader, ref hdItem);
}
無事にソート、マークの表示ができました。
何故、標準で出来ないのか。
謎は深まりますね。
次回は今のところ未定です。
ではでは。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント