現在、リストビューでカスタムソーターを使用しています。最初の列をクリックするたびにリストビューを並べ替えることができますが、他の列で並べ替えることはできません。
SortStyle:昇順か降順かを決定する変数。
if (e.Column == 0)
{
if (SortStyle == 0)
{
List.ListViewItemSorter = customSortDsc;
SortStyle = 1;
}
else
{
List.ListViewItemSorter = customSortAsc;
SortStyle = 0;
}
}
これは、最初の列でソートする場合は正常に機能しますが、他の列でソートする場合は、最初の列でソートするだけです。クリックされた列でソートする方法はありますか?
ListViewから始めている場合は、自分で大いに助けて、代わりに ObjectListView を使用してください。 ObjectListViewは、.NET WinForms ListViewのオープンソースラッパーです。これにより、ListViewがはるかに使いやすくなり、多くの一般的な問題が解決されます。列のクリックによるソートは、自動的に処理される多くの機能の1つです。
真剣に、通常のListViewの代わりにObjectListViewを使用して後悔することはありません。
カスタムソーターは忘れてください。次のページのコードを使用してやり直してください。 IComparerインターフェイスを継承するクラスを定義する方法を示します。各行はコメント化されているため、実際に何が起こっているかを確認できます。唯一の複雑な問題は、リストビューコントロールからリストビューアイテムを取得する方法です。これらを二乗して取得する必要があるのは、IComparerインターフェイスクラスとcolumnClickメソッドをコピーして貼り付けるだけです。
列名を使用して並べ替え、列に格納されているデータ型に基づいて処理する必要がある場合や、列が既に並べ替えられている場合(asc/desc)に並べ替えの詳細を設定します。 ColumnClickイベントハンドラーのスニペットを次に示します。
private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
{
ListViewItemComparer sorter = GetListViewSorter(e.Column);
listView.ListViewItemSorter = sorter;
listView.Sort();
}
private ListViewItemComparer GetListViewSorter(int columnIndex)
{
ListViewItemComparer sorter = (ListViewItemComparer)listView.ListViewItemSorter;
if (sorter == null)
{
sorter = new ListViewItemComparer();
}
sorter.ColumnIndex = columnIndex;
string columnName = packagedEstimateListView.Columns[columnIndex].Name;
switch (columnName)
{
case ApplicationModel.DisplayColumns.DateCreated:
case ApplicationModel.DisplayColumns.DateUpdated:
sorter.ColumnType = ColumnDataType.DateTime;
break;
case ApplicationModel.DisplayColumns.NetTotal:
case ApplicationModel.DisplayColumns.GrossTotal:
sorter.ColumnType = ColumnDataType.Decimal;
break;
default:
sorter.ColumnType = ColumnDataType.String;
break;
}
if (sorter.SortDirection == SortOrder.Ascending)
{
sorter.SortDirection = SortOrder.Descending;
}
else
{
sorter.SortDirection = SortOrder.Ascending;
}
return sorter;
}
以下は私のListViewItemComparerです
public class ListViewItemComparer : IComparer
{
private int _columnIndex;
public int ColumnIndex
{
get
{
return _columnIndex;
}
set
{
_columnIndex = value;
}
}
private SortOrder _sortDirection;
public SortOrder SortDirection
{
get
{
return _sortDirection;
}
set
{
_sortDirection = value;
}
}
private ColumnDataType _columnType;
public ColumnDataType ColumnType
{
get
{
return _columnType;
}
set
{
_columnType = value;
}
}
public ListViewItemComparer()
{
_sortDirection = SortOrder.None;
}
public int Compare(object x, object y)
{
ListViewItem lviX = x as ListViewItem;
ListViewItem lviY = y as ListViewItem;
int result;
if (lviX == null && lviY == null)
{
result = 0;
}
else if (lviX == null)
{
result = -1;
}
else if (lviY == null)
{
result = 1;
}
switch (ColumnType)
{
case ColumnDataType.DateTime:
DateTime xDt = DataParseUtility.ParseDate(lviX.SubItems[ColumnIndex].Text);
DateTime yDt = DataParseUtility.ParseDate(lviY.SubItems[ColumnIndex].Text);
result = DateTime.Compare(xDt, yDt);
break;
case ColumnDataType.Decimal:
Decimal xD = DataParseUtility.ParseDecimal(lviX.SubItems[ColumnIndex].Text.Replace("$", string.Empty).Replace(",", string.Empty));
Decimal yD = DataParseUtility.ParseDecimal(lviY.SubItems[ColumnIndex].Text.Replace("$", string.Empty).Replace(",", string.Empty));
result = Decimal.Compare(xD, yD);
break;
case ColumnDataType.Short:
short xShort = DataParseUtility.ParseShort(lviX.SubItems[ColumnIndex].Text);
short yShort = DataParseUtility.ParseShort(lviY.SubItems[ColumnIndex].Text);
result = xShort.CompareTo(yShort);
break;
case ColumnDataType.Int:
int xInt = DataParseUtility.ParseInt(lviX.SubItems[ColumnIndex].Text);
int yInt = DataParseUtility.ParseInt(lviY.SubItems[ColumnIndex].Text);
return xInt.CompareTo(yInt);
break;
case ColumnDataType.Long:
long xLong = DataParseUtility.ParseLong(lviX.SubItems[ColumnIndex].Text);
long yLong = DataParseUtility.ParseLong(lviY.SubItems[ColumnIndex].Text);
return xLong.CompareTo(yLong);
break;
default:
result = string.Compare(
lviX.SubItems[ColumnIndex].Text,
lviY.SubItems[ColumnIndex].Text,
false);
break;
}
if (SortDirection == SortOrder.Descending)
{
return -result;
}
else
{
return result;
}
}
}
ListViewで文字列と数値の両方のソートに対応するために、記事 here に小さな変更を加えました。
Form1.csに含まれるもの
using System;
using System.Windows.Forms;
namespace ListView
{
public partial class Form1 : Form
{
Random rnd = new Random();
private ListViewColumnSorter lvwColumnSorter;
public Form1()
{
InitializeComponent();
// Create an instance of a ListView column sorter and assign it to the ListView control.
lvwColumnSorter = new ListViewColumnSorter();
this.listView1.ListViewItemSorter = lvwColumnSorter;
InitListView();
}
private void InitListView()
{
listView1.View = View.Details;
listView1.GridLines = true;
listView1.FullRowSelect = true;
//Add column header
listView1.Columns.Add("Name", 100);
listView1.Columns.Add("Price", 70);
listView1.Columns.Add("Trend", 70);
for (int i = 0; i < 10; i++)
{
listView1.Items.Add(AddToList("Name" + i.ToString(), rnd.Next(1, 100).ToString(), rnd.Next(1, 100).ToString()));
}
}
private ListViewItem AddToList(string name, string price, string trend)
{
string[] array = new string[3];
array[0] = name;
array[1] = price;
array[2] = trend;
return (new ListViewItem(array));
}
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine if clicked column is already the column that is being sorted.
if (e.Column == lvwColumnSorter.SortColumn)
{
// Reverse the current sort direction for this column.
if (lvwColumnSorter.Order == SortOrder.Ascending)
{
lvwColumnSorter.Order = SortOrder.Descending;
}
else
{
lvwColumnSorter.Order = SortOrder.Ascending;
}
}
else
{
// Set the column number that is to be sorted; default to ascending.
lvwColumnSorter.SortColumn = e.Column;
lvwColumnSorter.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
this.listView1.Sort();
}
}
}
ListViewColumnSorter.csに含まれるもの
using System;
using System.Collections;
using System.Windows.Forms;
/// <summary>
/// This class is an implementation of the 'IComparer' interface.
/// </summary>
public class ListViewColumnSorter : IComparer
{
/// <summary>
/// Specifies the column to be sorted
/// </summary>
private int ColumnToSort;
/// <summary>
/// Specifies the order in which to sort (i.e. 'Ascending').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Case insensitive comparer object
/// </summary>
private CaseInsensitiveComparer ObjectCompare;
/// <summary>
/// Class constructor. Initializes various elements
/// </summary>
public ListViewColumnSorter()
{
// Initialize the column to '0'
ColumnToSort = 0;
// Initialize the sort order to 'none'
OrderOfSort = SortOrder.None;
// Initialize the CaseInsensitiveComparer object
ObjectCompare = new CaseInsensitiveComparer();
}
/// <summary>
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
/// </summary>
/// <param name="x">First object to be compared</param>
/// <param name="y">Second object to be compared</param>
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
public int Compare(object x, object y)
{
int compareResult;
ListViewItem listviewX, listviewY;
// Cast the objects to be compared to ListViewItem objects
listviewX = (ListViewItem)x;
listviewY = (ListViewItem)y;
decimal num = 0;
if (decimal.TryParse(listviewX.SubItems[ColumnToSort].Text, out num))
{
compareResult = decimal.Compare(num, Convert.ToDecimal(listviewY.SubItems[ColumnToSort].Text));
}
else
{
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
}
// Calculate correct return value based on object comparison
if (OrderOfSort == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (OrderOfSort == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
else
{
// Return '0' to indicate they are equal
return 0;
}
}
/// <summary>
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
/// </summary>
public int SortColumn
{
set
{
ColumnToSort = value;
}
get
{
return ColumnToSort;
}
}
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// </summary>
public SortOrder Order
{
set
{
OrderOfSort = value;
}
get
{
return OrderOfSort;
}
}
}
私のソリューションは、列ヘッダーをクリックしたときにlistViewアイテムを並べ替えるクラスです。
各列のタイプを指定できます。
listView.ListViewItemSorter = new ListViewColumnSorter();
listView.ListViewItemSorter.ColumnsTypeComparer.Add(0, DateTime);
listView.ListViewItemSorter.ColumnsTypeComparer.Add(1, int);
それでおしまい !
C#クラス:
using System.Collections;
using System.Collections.Generic;
using EDV;
namespace System.Windows.Forms
{
/// <summary>
/// Cette classe est une implémentation de l'interface 'IComparer' pour le tri des items de ListView. Adapté de http://support.Microsoft.com/kb/319401.
/// </summary>
/// <remarks>Intégré par EDVariables.</remarks>
public class ListViewColumnSorter : IComparer
{
/// <summary>
/// Spécifie la colonne à trier
/// </summary>
private int ColumnToSort;
/// <summary>
/// Spécifie l'ordre de tri (en d'autres termes 'Croissant').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Objet de comparaison ne respectant pas les majuscules et minuscules
/// </summary>
private CaseInsensitiveComparer ObjectCompare;
/// <summary>
/// Constructeur de classe. Initialise la colonne sur '0' et aucun tri
/// </summary>
public ListViewColumnSorter()
: this(0, SortOrder.None) { }
/// <summary>
/// Constructeur de classe. Initializes various elements
/// <param name="columnToSort">Spécifie la colonne à trier</param>
/// <param name="orderOfSort">Spécifie l'ordre de tri</param>
/// </summary>
public ListViewColumnSorter(int columnToSort, SortOrder orderOfSort)
{
// Initialise la colonne
ColumnToSort = columnToSort;
// Initialise l'ordre de tri
OrderOfSort = orderOfSort;
// Initialise l'objet CaseInsensitiveComparer
ObjectCompare = new CaseInsensitiveComparer();
// Dictionnaire de comparateurs
ColumnsComparer = new Dictionary<int, IComparer>();
ColumnsTypeComparer = new Dictionary<int, Type>();
}
/// <summary>
/// Cette méthode est héritée de l'interface IComparer. Il compare les deux objets passés en effectuant une comparaison
///qui ne tient pas compte des majuscules et des minuscules.
/// <br/>Si le comparateur n'existe pas dans ColumnsComparer, CaseInsensitiveComparer est utilisé.
/// </summary>
/// <param name="x">Premier objet à comparer</param>
/// <param name="x">Deuxième objet à comparer</param>
/// <returns>Le résultat de la comparaison. "0" si équivalent, négatif si 'x' est inférieur à 'y'
///et positif si 'x' est supérieur à 'y'</returns>
public int Compare(object x, object y)
{
int compareResult;
ListViewItem listviewX, listviewY;
// Envoit les objets à comparer aux objets ListViewItem
listviewX = (ListViewItem)x;
listviewY = (ListViewItem)y;
if (listviewX.SubItems.Count < ColumnToSort + 1 || listviewY.SubItems.Count < ColumnToSort + 1)
return 0;
IComparer objectComparer = null;
Type comparableType = null;
if (ColumnsComparer == null || !ColumnsComparer.TryGetValue(ColumnToSort, out objectComparer))
if (ColumnsTypeComparer == null || !ColumnsTypeComparer.TryGetValue(ColumnToSort, out comparableType))
objectComparer = ObjectCompare;
// Compare les deux éléments
if (comparableType != null) {
//Conversion du type
object valueX = listviewX.SubItems[ColumnToSort].Text;
object valueY = listviewY.SubItems[ColumnToSort].Text;
if (!edvTools.TryParse(ref valueX, comparableType) || !edvTools.TryParse(ref valueY, comparableType))
return 0;
compareResult = (valueX as IComparable).CompareTo(valueY);
}
else
compareResult = objectComparer.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
// Calcule la valeur correcte d'après la comparaison d'objets
if (OrderOfSort == SortOrder.Ascending) {
// Le tri croissant est sélectionné, renvoie des résultats normaux de comparaison
return compareResult;
}
else if (OrderOfSort == SortOrder.Descending) {
// Le tri décroissant est sélectionné, renvoie des résultats négatifs de comparaison
return (-compareResult);
}
else {
// Renvoie '0' pour indiquer qu'ils sont égaux
return 0;
}
}
/// <summary>
/// Obtient ou définit le numéro de la colonne à laquelle appliquer l'opération de tri (par défaut sur '0').
/// </summary>
public int SortColumn
{
set
{
ColumnToSort = value;
}
get
{
return ColumnToSort;
}
}
/// <summary>
/// Obtient ou définit l'ordre de tri à appliquer (par exemple, 'croissant' ou 'décroissant').
/// </summary>
public SortOrder Order
{
set
{
OrderOfSort = value;
}
get
{
return OrderOfSort;
}
}
/// <summary>
/// Dictionnaire de comparateurs par colonne.
/// <br/>Pendant le tri, si le comparateur n'existe pas dans ColumnsComparer, CaseInsensitiveComparer est utilisé.
/// </summary>
public Dictionary<int, IComparer> ColumnsComparer { get; set; }
/// <summary>
/// Dictionnaire de comparateurs par colonne.
/// <br/>Pendant le tri, si le comparateur n'existe pas dans ColumnsTypeComparer, CaseInsensitiveComparer est utilisé.
/// </summary>
public Dictionary<int, Type> ColumnsTypeComparer { get; set; }
}
}
ListViewの初期化:
<var>Visual.WIN.ctrlListView.OnShown</var> :
eventSender.Columns.Clear();
eventSender.SmallImageList = edvWinForm.ImageList16;
eventSender.ListViewItemSorter = new ListViewColumnSorter();
var col = eventSender.Columns.Add("Répertoire");
col.Width = 160;
col.ImageKey = "Domain";
col = eventSender.Columns.Add("Fichier");
col.Width = 180;
col.ImageKey = "File";
col = eventSender.Columns.Add("Date");
col.Width = 120;
col.ImageKey = "DateTime";
eventSender.ListViewItemSorter.ColumnsTypeComparer.Add(col.Index, DateTime);
col = eventSender.Columns.Add("Position");
col.TextAlign = HorizontalAlignment.Right;
col.Width = 80;
col.ImageKey = "Num";
eventSender.ListViewItemSorter.ColumnsTypeComparer.Add(col.Index, Int32);
ListViewを埋める:
<var>Visual.WIN.cmdSearch.OnClick</var> :
//non récursif et sans fonction
..ctrlListView:Items.Clear();
..ctrlListView:Sorting = SortOrder.None;
var group = ..ctrlListView:Groups.Add(DateTime.Now.ToString()
, Path.Combine(..cboDir:Text, ..ctrlPattern1:Text) + " contenant " + ..ctrlSearch1:Text);
var perf = Environment.TickCount;
var files = new DirectoryInfo(..cboDir:Text).GetFiles(..ctrlPattern1:Text)
var search = ..ctrlSearch1:Text;
var ignoreCase = ..Search.IgnoreCase;
//var result = new StringBuilder();
var dirLength : int = ..cboDir:Text.Length;
var position : int;
var added : int = 0;
for(var i : int = 0; i < files.Length; i++){
var file = files[i];
if(search == ""
|| (position = File.ReadAllText(file.FullName).IndexOf(String(search)
, StringComparison(ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture))) > =0) {
// result.AppendLine(file.FullName.Substring(dirLength) + "\tPos : " + pkvFile.Value);
var item = ..ctrlListView:Items.Add(file.FullName.Substring(dirLength));
item.SubItems.Add(file.Name);
item.SubItems.Add(File.GetLastWriteTime(file.FullName).ToString());
item.SubItems.Add(position.ToString("# ### ##0"));
item.Group = group;
++added;
}
}
group.Header += " : " + added + "/" + files.Length + " fichier(s)"
+ " en " + (Environment.TickCount - perf).ToString("# ##0 msec");
ListView列をクリックして:
<var>Visual.WIN.ctrlListView.OnColumnClick</var> :
// Déterminer si la colonne sélectionnée est déjà la colonne triée.
var sorter = eventSender.ListViewItemSorter;
if ( eventArgs.Column == sorter .SortColumn )
{
// Inverser le sens de tri en cours pour cette colonne.
if (sorter.Order == SortOrder.Ascending)
{
sorter.Order = SortOrder.Descending;
}
else
{
sorter.Order = SortOrder.Ascending;
}
}
else
{
// Définir le numéro de colonne à trier ; par défaut sur croissant.
sorter.SortColumn = eventArgs.Column;
sorter.Order = SortOrder.Ascending;
}
// Procéder au tri avec les nouvelles options.
eventSender.Sort();
上記で使用される関数edvTools.TryParse
class edvTools {
/// <summary>
/// Tente la conversion d'une valeur suivant un type EDVType
/// </summary>
/// <param name="pValue">Référence de la valeur à convertir</param>
/// <param name="pType">Type EDV en sortie</param>
/// <returns></returns>
public static bool TryParse(ref object pValue, System.Type pType)
{
int lIParsed;
double lDParsed;
string lsValue;
if (pValue == null) return false;
if (pType.Equals(typeof(bool))) {
bool lBParsed;
if (pValue is bool) return true;
if (double.TryParse(pValue.ToString(), out lDParsed)) {
pValue = lDParsed != 0D;
return true;
}
if (bool.TryParse(pValue.ToString(), out lBParsed)) {
pValue = lBParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(Double))) {
if (pValue is Double) return true;
if (double.TryParse(pValue.ToString(), out lDParsed)
|| double.TryParse(pValue.ToString().Replace(NumberDecimalSeparatorNOT, NumberDecimalSeparator), out lDParsed)) {
pValue = lDParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(int))) {
if (pValue is int) return true;
if (Int32.TryParse(pValue.ToString(), out lIParsed)) {
pValue = lIParsed;
return true;
}
else if (double.TryParse(pValue.ToString(), out lDParsed)) {
pValue = (int)lDParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(Byte))) {
if (pValue is byte) return true;
byte lByte;
if (Byte.TryParse(pValue.ToString(), out lByte)) {
pValue = lByte;
return true;
}
else if (double.TryParse(pValue.ToString(), out lDParsed)) {
pValue = (byte)lDParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(long))) {
long lLParsed;
if (pValue is long) return true;
if (long.TryParse(pValue.ToString(), out lLParsed)) {
pValue = lLParsed;
return true;
}
else if (double.TryParse(pValue.ToString(), out lDParsed)) {
pValue = (long)lDParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(Single))) {
if (pValue is float) return true;
Single lSParsed;
if (Single.TryParse(pValue.ToString(), out lSParsed)
|| Single.TryParse(pValue.ToString().Replace(NumberDecimalSeparatorNOT, NumberDecimalSeparator), out lSParsed)) {
pValue = lSParsed;
return true;
}
else
return false;
}
if (pType.Equals(typeof(DateTime))) {
if (pValue is DateTime) return true;
DateTime lDTParsed;
if (DateTime.TryParse(pValue.ToString(), out lDTParsed)) {
pValue = lDTParsed;
return true;
}
else if (pValue.ToString().Contains("UTC")) //Date venant de JScript
{
if (_MonthsUTC == null) InitMonthsUTC();
string[] lDateParts = pValue.ToString().Split(' ');
lDTParsed = new DateTime(int.Parse(lDateParts[5]), _MonthsUTC[lDateParts[1]], int.Parse(lDateParts[2]));
lDateParts = lDateParts[3].ToString().Split(':');
pValue = lDTParsed.AddSeconds(int.Parse(lDateParts[0]) * 3600 + int.Parse(lDateParts[1]) * 60 + int.Parse(lDateParts[2]));
return true;
}
else
return false;
}
if (pType.Equals(typeof(Array))) {
if (pValue is System.Collections.ICollection || pValue is System.Collections.ArrayList)
return true;
return pValue is System.Data.DataTable
|| pValue is string && (pValue as string).StartsWith("<");
}
if (pType.Equals(typeof(DataTable))) {
return pValue is System.Data.DataTable
|| pValue is string && (pValue as string).StartsWith("<");
}
if (pType.Equals(typeof(System.Drawing.Bitmap))) {
return pValue is System.Drawing.Image || pValue is byte[];
}
if (pType.Equals(typeof(System.Drawing.Image))) {
return pValue is System.Drawing.Image || pValue is byte[];
}
if (pType.Equals(typeof(System.Drawing.Color))) {
if (pValue is System.Drawing.Color) return true;
if (pValue is System.Drawing.KnownColor) {
pValue = System.Drawing.Color.FromKnownColor((System.Drawing.KnownColor)pValue);
return true;
}
int lARGB;
if (!int.TryParse(lsValue = pValue.ToString(), out lARGB)) {
if (lsValue.StartsWith("Color [A=", StringComparison.InvariantCulture)) {
foreach (string lsARGB in lsValue.Substring("Color [".Length, lsValue.Length - "Color []".Length).Split(','))
switch (lsARGB.TrimStart().Substring(0, 1)) {
case "A":
lARGB = int.Parse(lsARGB.Substring(2)) * 0x1000000;
break;
case "R":
lARGB += int.Parse(lsARGB.TrimStart().Substring(2)) * 0x10000;
break;
case "G":
lARGB += int.Parse(lsARGB.TrimStart().Substring(2)) * 0x100;
break;
case "B":
lARGB += int.Parse(lsARGB.TrimStart().Substring(2));
break;
default:
break;
}
pValue = System.Drawing.Color.FromArgb(lARGB);
return true;
}
if (lsValue.StartsWith("Color [", StringComparison.InvariantCulture)) {
pValue = System.Drawing.Color.FromName(lsValue.Substring("Color [".Length, lsValue.Length - "Color []".Length));
return true;
}
return false;
}
pValue = System.Drawing.Color.FromArgb(lARGB);
return true;
}
if (pType.IsEnum) {
try {
if (pValue == null) return false;
if (pValue is int || pValue is byte || pValue is ulong || pValue is long || pValue is double)
pValue = Enum.ToObject(pType, pValue);
else
pValue = Enum.Parse(pType, pValue.ToString());
}
catch {
return false;
}
}
return true;
}
}
パーティーに遅れて、ここに短いものがあります。次の制限があります。
SubItems
'Texts
の単純な文字列ソートのみを行いますListView
のTag
を使用します任意のListView
をサービスに登録および登録解除できます。 Sorting
がNone
に設定されていることを確認してください。
public static class LvSort
{
static List<ListView> LVs = new List<ListView>();
public static void registerLV(ListView lv)
{
if (!LVs.Contains(lv) && lv is ListView)
{
LVs.Add(lv);
lv.ColumnClick +=Lv_ColumnClick;
}
}
public static void unRegisterLV(ListView lv)
{
if (LVs.Contains(lv) && lv is ListView)
{
LVs.Remove(lv);
lv.ColumnClick -=Lv_ColumnClick;
}
}
private static void Lv_ColumnClick(object sender, ColumnClickEventArgs e)
{
ListView lv = sender as ListView;
if (lv == null) return;
int c = e.Column;
bool asc = (lv.Tag == null) || ( lv.Tag.ToString() != c+"");
var items = lv.Items.Cast<ListViewItem>().ToList();
var sorted = asc ? items.OrderByDescending(x => x.SubItems[c].Text).ToList() :
items.OrderBy(x => x.SubItems[c].Text).ToList();
lv.Items.Clear();
lv.Items.AddRange(sorted.ToArray());
if (asc) lv.Tag = c+""; else lv.Tag = null;
}
}
単に登録するには..:
public Form1()
{
InitializeComponent();
LvSort.registerLV(yourListView1);
}
更新:
以下に、少し拡張したバージョンを示します。このバージョンでは、あらゆるソート規則を使用して、あらゆる種類のデータ型をソートできます。必要なのは、データ用の特別な文字列変換を記述し、それを関数リストに追加して列をマークすることだけです。そのためには、列のタグに列名にマーカー文字列を追加するだけです。
DataTimesのソート用と整数用の1つを追加しました。
このバージョンでは、ギザギザのリストビュー、つまりサブアイテムの数が異なるリストビューもソートされます。
public static class LvCtl
{
static List<ListView> LVs = new List<ListView>();
delegate string StringFrom (string s);
static Dictionary<string, StringFrom> funx = new Dictionary<string, StringFrom>();
public static void registerLV(ListView lv)
{
if (!LVs.Contains(lv) && lv is ListView)
{
LVs.Add(lv);
lv.ColumnClick +=Lv_ColumnClick;
funx.Add("", stringFromString);
for (int i = 0; i < lv.Columns.Count; i++)
{
if (lv.Columns[i].Tag == null) continue;
string n = lv.Columns[i].Tag.ToString();
if (n == "") continue;
if (n.Contains("__date")) funx.Add(n, stringFromDate);
if (n.Contains("__int")) funx.Add(n, stringFromInt);
else funx.Add(n, stringFromString);
}
}
}
static string stringFromString(string s)
{
return s;
}
static string stringFromInt(string s)
{
int i = 0;
int.TryParse(s, out i);
return i.ToString("00000") ;
}
static string stringFromDate(string s)
{
DateTime dt = Convert.ToDateTime(s);
return dt.ToString("yyyy.MM.dd HH.mm.ss");
}
private static void Lv_ColumnClick(object sender, ColumnClickEventArgs e)
{
ListView lv = sender as ListView;
if (lv == null) return;
int c = e.Column;
string nt = lv.Columns[c].Tag != null ? lv.Columns[c].Tag.ToString() : "";
string n = nt.Replace("__", "§").Split('§')[0];
bool asc = (lv.Tag == null) || ( lv.Tag.ToString() != c+"");
var items = lv.Items.Cast<ListViewItem>().ToList();
var sorted = asc?
items.OrderByDescending(x => funx[nt]( c < x.SubItems.Count ?
x.SubItems[c].Text: "")).ToList() :
items.OrderBy(x => funx[nt](c < x.SubItems.Count ?
x.SubItems[c].Text : "")).ToList();
lv.Items.Clear();
lv.Items.AddRange(sorted.ToArray());
if (asc) lv.Tag = c+""; else lv.Tag = null;
}
public static void unRegisterLV(ListView lv)
{
if (LVs.Contains(lv) && lv is ListView)
{
LVs.Remove(lv);
lv.ColumnClick -=Lv_ColumnClick;
}
}
}
この質問は、プログラマーが希望する結果を得るために一生懸命努力しなければならなかった5年前に最初に投稿されたことがわかります。 Visual Studio 2012以降では、怠programmerなプログラマーはリストビューのプロパティ設定のデザインビューに移動し、[プロパティ]-> [並べ替え]をクリックして、[昇順]を選択できます。怠zyな(スマートな)プログラマーが活用できるさまざまな結果を取得するためのプロパティ機能は他にもたくさんあります。
重いものについては、datagridviewをお勧めします。多くの自動機能リストが含まれています。
私はこのトリックを使用しました
private void lv_TavComEmpty_ColumnClick(object sender, ColumnClickEventArgs e)
{
ListView lv = (ListView)sender;
//propriety SortOrder make me some problem on graphic layout
//i use this tag to set last order
if (lv.Tag == null || (int)lv.Tag > 0)
//if (lv.Sorting == SortOrder.Ascending)
{
ListViewItem[] tmp = lv.Items.Cast<ListViewItem>().OrderBy(t => t.SubItems[e.Column].Text).ToArray();
lv.Items.Clear();
lv.Items.AddRange(tmp);
lv.Tag = -1;
//lv.Sorting = SortOrder.Descending;
}
else
{
ListViewItem[] tmp = lv.Items.Cast<ListViewItem>().OrderByDescending(t => t.SubItems[e.Column].Text).ToArray();
lv.Items.Clear();
lv.Items.AddRange(tmp);
lv.Tag = +1;
//lv.Sorting = SortOrder.Ascending;
}
}
他の人が使用していると思われるものと同じ基本クラスを使用し、文字列、日付、数値の並べ替えを許可するように変更しました。
次のようなバッキングフィールドを使用して初期化できます。
private readonly ListViewColumnSorterExt fileSorter;
...
public Form1()
{
InitializeComponent();
fileSorter = new ListViewColumnSorterExt(myListView);
}
コードは次のとおりです。
public class ListViewColumnSorterExt : IComparer
{
/// <summary>
/// Specifies the column to be sorted
/// </summary>
private int ColumnToSort;
/// <summary>
/// Specifies the order in which to sort (i.e. 'Ascending').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Case insensitive comparer object
/// </summary>
private CaseInsensitiveComparer ObjectCompare;
private ListView listView;
/// <summary>
/// Class constructor. Initializes various elements
/// </summary>
public ListViewColumnSorterExt(ListView lv)
{
listView = lv;
listView.ListViewItemSorter = this;
listView.ColumnClick += new ColumnClickEventHandler(listView_ColumnClick);
// Initialize the column to '0'
ColumnToSort = 0;
// Initialize the sort order to 'none'
OrderOfSort = SortOrder.None;
// Initialize the CaseInsensitiveComparer object
ObjectCompare = new CaseInsensitiveComparer();
}
private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
{
ReverseSortOrderAndSort(e.Column, (ListView)sender);
}
/// <summary>
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
/// </summary>
/// <param name="x">First object to be compared</param>
/// <param name="y">Second object to be compared</param>
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
public int Compare(object x, object y)
{
int compareResult;
ListViewItem listviewX, listviewY;
// Cast the objects to be compared to ListViewItem objects
listviewX = (ListViewItem)x;
listviewY = (ListViewItem)y;
if (decimal.TryParse(listviewX.SubItems[ColumnToSort].Text, out decimal dx) && decimal.TryParse(listviewY.SubItems[ColumnToSort].Text, out decimal dy))
{
//compare the 2 items as doubles
compareResult = decimal.Compare(dx, dy);
}
else if (DateTime.TryParse(listviewX.SubItems[ColumnToSort].Text, out DateTime dtx) && DateTime.TryParse(listviewY.SubItems[ColumnToSort].Text, out DateTime dty))
{
//compare the 2 items as doubles
compareResult = DateTime.Compare(dtx, dty);
}
else
{
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
}
// Calculate correct return value based on object comparison
if (OrderOfSort == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (OrderOfSort == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
else
{
// Return '0' to indicate they are equal
return 0;
}
}
/// <summary>
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
/// </summary>
private int SortColumn
{
set
{
ColumnToSort = value;
}
get
{
return ColumnToSort;
}
}
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// </summary>
private SortOrder Order
{
set
{
OrderOfSort = value;
}
get
{
return OrderOfSort;
}
}
private void ReverseSortOrderAndSort(int column, ListView lv)
{
// Determine if clicked column is already the column that is being sorted.
if (column == this.SortColumn)
{
// Reverse the current sort direction for this column.
if (this.Order == SortOrder.Ascending)
{
this.Order = SortOrder.Descending;
}
else
{
this.Order = SortOrder.Ascending;
}
}
else
{
// Set the column number that is to be sorted; default to ascending.
this.SortColumn = column;
this.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
lv.Sort();
}
}
並べ替え順序に関してアイコンを割り当てたい場合は、画像リストをリストビューに追加し、並べ替えに使用する画像の名前を反映するように以下のサンプルを更新してください(いつでも任意の名前を割り当てることができます)それらをインポートします)。上記のlistView_ColumnClickを次のように更新します。
private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
{
if (sender is ListView lv)
{
ReverseSortOrderAndSort(e.Column, lv);
if ( lv.Columns[e.Column].ImageList.Images.Keys.Contains("Ascending")
&& lv.Columns[e.Column].ImageList.Images.Keys.Contains("Descending"))
{
switch (Order)
{
case SortOrder.Ascending:
lv.Columns[e.Column].ImageKey = "Ascending";
break;
case SortOrder.Descending:
lv.Columns[e.Column].ImageKey = "Descending";
break;
case SortOrder.None:
lv.Columns[e.Column].ImageKey = string.Empty;
break;
}
}
}
}
Microsoftの例を少し変更しました: https://support.Microsoft.com/en-us/kb/319401
このメソッドは、昇順でソートするために一度だけソートします。私の変更により、両方の方法でソートされます。
public class ListViewItemComparer : IComparer
{
private int col;
bool bAsc = false;
public ListViewItemComparer()
{
col = 0;
}
public ListViewItemComparer(int column, bool b)
{
col = column;
bAsc = b;
}
public int Compare(object x, object y)
{
if (bAsc)
{
return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
bAsc = false;
}
else
{
return String.Compare(((ListViewItem)y).SubItems[col].Text, ((ListViewItem)x).SubItems[col].Text);
bAsc = true;
}
}
}
次に、列ヘッダーがクリックされるたびにこのクラスのオブジェクトを作成します
bool sortAscending = false;
private void inventoryList_ColumnClick(object sender, ColumnClickEventArgs e)
{
if (!sortAscending)
{
sortAscending = true;
}
else
{
sortAscending = false;
}
this.inventoryList.ListViewItemSorter = new ListViewItemComparer(e.Column, sortAscending);
}
このような手動ソートアルゴリズムを使用できます
public void ListItemSorter(object sender, ColumnClickEventArgs e)
{
ListView list = (ListView)sender;
int total = list.Items.Count;
list.BeginUpdate();
ListViewItem[] items = new ListViewItem[total];
for (int i = 0; i < total; i++)
{
int count = list.Items.Count;
int minIdx = 0;
for (int j = 1; j < count; j++)
if (list.Items[j].SubItems[e.Column].Text.CompareTo(list.Items[minIdx].SubItems[e.Column].Text) < 0)
minIdx = j;
items[i] = list.Items[minIdx];
list.Items.RemoveAt(minIdx);
}
list.Items.AddRange(items);
list.EndUpdate();
}
このメソッドは、O ^ 2順および昇順で選択ソートを使用します。降順の場合は「>」を「<」で変更するか、このメソッドの引数を追加できます。クリックされた列をソートし、少量のデータに最適です。
ListView.SortExpression を使用します。
複数の列が並べ替えられる場合、このプロパティには並べ替えの基準となるフィールドのコンマ区切りリストが含まれます。
これは依然としてトップビューのスレッドであるため、列ごとにリストビューを並べ替える動的なソリューションを思いついたのではないかと思います。他の誰かがそれを使用したい場合のためのコードを以下に示します。リストビューアイテムをデータテーブルに送信し、データテーブルのデフォルトビューを列名でソートし(クリックした列のインデックスを使用)、defaultview.totable()メソッドでそのテーブルを上書きするだけです。次に、それらをリストビューに追加し直します。そして、ワラ、列ごとにソートされたリストビューです。
public void SortListView(int Index)
{
DataTable TempTable = new DataTable();
//Add column names to datatable from listview
foreach (ColumnHeader iCol in MyListView.Columns)
{
TempTable.Columns.Add(iCol.Text);
}
//Create a datarow from each listviewitem and add it to the table
foreach (ListViewItem Item in MyListView.Items)
{
DataRow iRow = TempTable.NewRow();
// the for loop dynamically copies the data one by one instead of doing irow[i] = MyListView.Subitems[1]... so on
for (int i = 0; i < MyListView.Columns.Count; i++)
{
if (i == 0)
{
iRow[i] = Item.Text;
}
else
{
iRow[i] = Item.SubItems[i].Text;
}
}
TempTable.Rows.Add(iRow);
}
string SortType = string.Empty;
//LastCol is a public int variable on the form, and LastSort is public string variable
if (LastCol == Index)
{
if (LastSort == "ASC" || LastSort == string.Empty || LastSort == null)
{
SortType = "DESC";
LastSort = "DESC";
}
else
{
SortType = "ASC";
LastSort = "ASC";
}
}
else
{
SortType = "DESC";
LastSort = "DESC";
}
LastCol = Index;
MyListView.Items.Clear();
//Sort it based on the column text clicked and the sort type (asc or desc)
TempTable.DefaultView.Sort = MyListView.Columns[Index].Text + " " + SortType;
TempTable = TempTable.DefaultView.ToTable();
//Create a listview item from the data in each row
foreach (DataRow iRow in TempTable.Rows)
{
ListViewItem Item = new ListViewItem();
List<string> SubItems = new List<string>();
for (int i = 0; i < TempTable.Columns.Count; i++)
{
if (i == 0)
{
Item.Text = iRow[i].ToString();
}
else
{
SubItems.Add(iRow[i].ToString());
}
}
Item.SubItems.AddRange(SubItems.ToArray());
MyListView.Items.Add(Item);
}
}
このメソッドは既存の列名を使用するため動的であり、各列のインデックスや名前、またはリストビュー/データテーブルにある列の数を知る必要はありません。 listview.columnclickとSortListView(e.column)のイベントを作成することで呼び出すことができます。
RedEyeが指摘した例に基づいて、以下のコードが必要なクラスを示します。
列は常に同じ方法でソートされると想定しているため、
ColumnClick内部でイベントシンク:
public class ListViewColumnSorterExt : IComparer {
/// <summary>
/// Specifies the column to be sorted
/// </summary>
private int ColumnToSort;
/// <summary>
/// Specifies the order in which to sort (i.e. 'Ascending').
/// </summary>
private SortOrder OrderOfSort;
/// <summary>
/// Case insensitive comparer object
/// </summary>
private CaseInsensitiveComparer ObjectCompare;
private ListView listView;
/// <summary>
/// Class constructor. Initializes various elements
/// </summary>
public ListViewColumnSorterExt(ListView lv) {
listView = lv;
listView.ListViewItemSorter = this;
listView.ColumnClick += new ColumnClickEventHandler(listView_ColumnClick);
// Initialize the column to '0'
ColumnToSort = 0;
// Initialize the sort order to 'none'
OrderOfSort = SortOrder.None;
// Initialize the CaseInsensitiveComparer object
ObjectCompare = new CaseInsensitiveComparer();
}
private void listView_ColumnClick(object sender, ColumnClickEventArgs e) {
ReverseSortOrderAndSort(e.Column, (ListView)sender);
}
/// <summary>
/// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison.
/// </summary>
/// <param name="x">First object to be compared</param>
/// <param name="y">Second object to be compared</param>
/// <returns>The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y'</returns>
public int Compare(object x, object y) {
int compareResult;
ListViewItem listviewX, listviewY;
// Cast the objects to be compared to ListViewItem objects
listviewX = (ListViewItem)x;
listviewY = (ListViewItem)y;
// Compare the two items
compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text);
// Calculate correct return value based on object comparison
if (OrderOfSort == SortOrder.Ascending) {
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
else if (OrderOfSort == SortOrder.Descending) {
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
else {
// Return '0' to indicate they are equal
return 0;
}
}
/// <summary>
/// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0').
/// </summary>
private int SortColumn {
set {
ColumnToSort = value;
}
get {
return ColumnToSort;
}
}
/// <summary>
/// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending').
/// </summary>
private SortOrder Order {
set {
OrderOfSort = value;
}
get {
return OrderOfSort;
}
}
private void ReverseSortOrderAndSort(int column, ListView lv) {
// Determine if clicked column is already the column that is being sorted.
if (column == this.SortColumn) {
// Reverse the current sort direction for this column.
if (this.Order == SortOrder.Ascending) {
this.Order = SortOrder.Descending;
}
else {
this.Order = SortOrder.Ascending;
}
}
else {
// Set the column number that is to be sorted; default to ascending.
this.SortColumn = column;
this.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
lv.Sort();
}
}
ソートオプションに満足していると仮定すると、クラスプロパティはprivateです。
あなたが書く必要がある唯一のコードは:
フォーム宣言で
private ListViewColumnSorterExt listViewColumnSorter;
フォームコンストラクターで
listViewColumnSorter = new ListViewColumnSorterExt(ListView1);
...これで完了です。
そして、複数のListViewを処理する単一のソーターはどうでしょうか?
public class MultipleListViewColumnSorter {
private List<ListViewColumnSorterExt> sorters;
public MultipleListViewColumnSorter() {
sorters = new List<ListViewColumnSorterExt>();
}
public void AddListView(ListView lv) {
sorters.Add(new ListViewColumnSorterExt(lv));
}
}
フォーム宣言で
private MultipleListViewColumnSorter listViewSorter = new MultipleListViewColumnSorter();
フォームコンストラクターで
listViewSorter.AddListView(ListView1);
listViewSorter.AddListView(ListView2);
// ... and so on ...