using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Collections; namespace Common { public enum ColumnDataType { String, Integer, DateTime, Decimal, Custom }; // Supports sorting by column in ascending or descending order public class ListViewItemComparer : IComparer { private int column; private SortOrder order; public void ColumnClick(int newColumn) { // Determine if clicked column is already the column that is being sorted. if (newColumn == column) { // Reverse the current sort direction for this column. if (order == SortOrder.Ascending) { order = SortOrder.Descending; } else { order = SortOrder.Ascending; } } else { // Set the column number that is to be sorted; default to ascending. column = newColumn; order = SortOrder.Ascending; } } public void SetColumn(int newColumn) { column = newColumn; } public void SetOrder(SortOrder newOrder) { order = newOrder; } public ListViewItemComparer() { column = -1; order = SortOrder.None; } public delegate int CompareItemEventHandler(ListViewItem x, ListViewItem y, int column); public event CompareItemEventHandler OnCompareItem; public int Compare(object x, object y) { ListViewItem listViewItemX = (ListViewItem)x; ListViewItem listViewItemY = (ListViewItem)y; int result = 0; if (listViewItemX != null && listViewItemY != null) { if (column < listViewItemX.SubItems.Count && column < listViewItemY.SubItems.Count && column >= 0) { ColumnDataType dataType = ColumnDataType.String; if (listViewItemX.ListView.Columns[column].Tag != null) { dataType = (ColumnDataType) (listViewItemX.ListView.Columns[column].Tag); } if (dataType == ColumnDataType.Integer) { if (int.TryParse(listViewItemX.SubItems[column].Text, out var xi) && int.TryParse(listViewItemY.SubItems[column].Text, out var yi)) { if (xi < yi) result = -1; else if (xi > yi) result = 1; else result = 0; } } else if (dataType == ColumnDataType.Decimal) { if (decimal.TryParse(listViewItemX.SubItems[column].Text, out var xi) && decimal.TryParse(listViewItemY.SubItems[column].Text, out var yi)) { if (xi < yi) result = -1; else if (xi > yi) result = 1; else result = 0; } } else if (dataType == ColumnDataType.DateTime) { if (DateTime.TryParse(listViewItemX.SubItems[column].Text, out var xi) && DateTime.TryParse(listViewItemY.SubItems[column].Text, out var yi)) { result = DateTime.Compare(xi, yi); } } else if (dataType == ColumnDataType.Custom) { if (OnCompareItem != null) { result = OnCompareItem(listViewItemX, listViewItemY, column); } } else result = string.CompareOrdinal(listViewItemX.SubItems[column].Text, listViewItemY.SubItems[column].Text); } else { if (!listViewItemX.Selected && listViewItemY.Selected) result = 1; else if (listViewItemX.Selected && !listViewItemY.Selected) result = -1; } } switch (order) { case SortOrder.Ascending: return result; case SortOrder.Descending: return -result; default: return 0; } } public static void ConfigureListView(ListView listView, int column = -1, SortOrder order = SortOrder.None) { if (listView.ListViewItemSorter == null) { listView.ListViewItemSorter = new ListViewItemComparer(); } ((ListViewItemComparer) listView.ListViewItemSorter).SetOrder(order); ((ListViewItemComparer) listView.ListViewItemSorter).SetColumn(column); if ((column != -1) && (order != SortOrder.None)) { listView.Sort(); listView.SetSortIcon(column, order); } listView.ColumnClick += delegate (object sender, ColumnClickEventArgs e) { ((ListViewItemComparer) ((ListView) sender).ListViewItemSorter).ColumnClick(e.Column); ((ListView) sender).Sort(); ((ListView) sender).SetSortIcon(((ListViewItemComparer) ((ListView) sender).ListViewItemSorter).column, ((ListViewItemComparer) ((ListView) sender).ListViewItemSorter).order); }; } public static void SetColumnDataType(ListView listView, int index, ColumnDataType dataType) { listView.Columns[index].Tag = dataType; } } internal static class ListViewExtensions { [StructLayout(LayoutKind.Sequential)] public struct LVCOLUMN { public Int32 mask; public Int32 cx; [MarshalAs(UnmanagedType.LPTStr)] public string pszText; public IntPtr hbm; public Int32 cchTextMax; public Int32 fmt; public Int32 iSubItem; public Int32 iImage; public Int32 iOrder; } const Int32 HDI_WIDTH = 0x0001; const Int32 HDI_HEIGHT = HDI_WIDTH; const Int32 HDI_TEXT = 0x0002; const Int32 HDI_FORMAT = 0x0004; const Int32 HDI_LPARAM = 0x0008; const Int32 HDI_BITMAP = 0x0010; const Int32 HDI_IMAGE = 0x0020; const Int32 HDI_DI_SETITEM = 0x0040; const Int32 HDI_ORDER = 0x0080; const Int32 HDI_FILTER = 0x0100; const Int32 HDF_LEFT = 0x0000; const Int32 HDF_RIGHT = 0x0001; const Int32 HDF_CENTER = 0x0002; const Int32 HDF_JUSTIFYMASK = 0x0003; const Int32 HDF_RTLREADING = 0x0004; const Int32 HDF_OWNERDRAW = 0x8000; const Int32 HDF_STRING = 0x4000; const Int32 HDF_BITMAP = 0x2000; const Int32 HDF_BITMAP_ON_RIGHT = 0x1000; const Int32 HDF_IMAGE = 0x0800; const Int32 HDF_SORTUP = 0x0400; const Int32 HDF_SORTDOWN = 0x0200; const Int32 LVM_FIRST = 0x1000; // List messages const Int32 LVM_GETHEADER = LVM_FIRST + 31; const Int32 HDM_FIRST = 0x1200; // Header messages const Int32 HDM_SETIMAGELIST = HDM_FIRST + 8; const Int32 HDM_GETIMAGELIST = HDM_FIRST + 9; const Int32 HDM_GETITEM = HDM_FIRST + 11; const Int32 HDM_SETITEM = HDM_FIRST + 12; [DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", EntryPoint = "SendMessage")] private static extern IntPtr SendMessageLVCOLUMN(IntPtr hWnd, Int32 Msg, IntPtr wParam, ref LVCOLUMN lPLVCOLUMN); // This method used to set arrow icon public static void SetSortIcon(this ListView listView, int columnIndex, SortOrder order) { IntPtr columnHeader = SendMessage(listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); for (int columnNumber = 0; columnNumber <= listView.Columns.Count - 1; columnNumber++) { IntPtr columnPtr = new IntPtr(columnNumber); LVCOLUMN lvColumn = new LVCOLUMN {mask = HDI_FORMAT}; SendMessageLVCOLUMN(columnHeader, HDM_GETITEM, columnPtr, ref lvColumn); if (order != SortOrder.None && columnNumber == columnIndex) { switch (order) { case SortOrder.Ascending: lvColumn.fmt &= ~HDF_SORTDOWN; lvColumn.fmt |= HDF_SORTUP; break; case SortOrder.Descending: lvColumn.fmt &= ~HDF_SORTUP; lvColumn.fmt |= HDF_SORTDOWN; break; } lvColumn.fmt |= (HDF_LEFT | HDF_BITMAP_ON_RIGHT); } else { lvColumn.fmt &= ~HDF_SORTDOWN & ~HDF_SORTUP & ~HDF_BITMAP_ON_RIGHT; } SendMessageLVCOLUMN(columnHeader, HDM_SETITEM, columnPtr, ref lvColumn); } } } }