I want to sort the items inside column of ListView, i already made it, but... i can\'t made it with the type of data in column (see picture), someone knows the way for do it
Create listViewSizeSorter class extending Icomparer interface as below.
using System.Collections;
using System.Windows.Forms;
namespace MyNameSpace
{
/// <summary>
/// This class is an implementation of the 'IComparer' interface.
/// </summary>
public class ListViewSizeSorter : 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>
/// Class constructor. Initializes various elements
/// </summary>
public ListViewSizeSorter()
{
// Initialize the column to '0'
SortColumn = 0;
// Initialize the sort order to 'none'
Order = SortOrder.None;
}
/// <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)
{
var listviewX = (ListViewItem)x;
var listviewY = (ListViewItem)y;
string strX = listviewX.SubItems[_columnToSort].Text;
string strY = listviewY.SubItems[_columnToSort].Text;
// Nulls first (null means less, since it's blank)
if (strX == null)
{
if (strY == null)
return 0;
return -1;
}
if (strY == null)
return 1;
// Convert the non-KB part to a number
double numX = 0;
double numY = 0;
if (strX.EndsWith("KB") || strX.EndsWith("GB") || strX.EndsWith("MB"))
double.TryParse(strX.Substring(0, strX.Length - 3), out numX);
if (strX.EndsWith("Bytes"))
double.TryParse(strX.Substring(0, strX.Length - 6), out numX);
if (strY.EndsWith("KB") || strY.EndsWith("GB") || strY.EndsWith("MB"))
double.TryParse(strY.Substring(0, strY.Length - 3), out numY);
if (strY.EndsWith("Bytes"))
double.TryParse(strX.Substring(0, strY.Length - 6), out numY);
long bytesX;
long bytesY;
if (strX.EndsWith("KB"))
bytesX = (long)numX * 1024;
else if (strX.EndsWith("MB"))
bytesX = (long)numX * 1048576;
else if (strX.EndsWith("GB"))
bytesX = (long)numX * 1073741824;
else
bytesX = (long) numX;
if (strY.EndsWith("KB"))
bytesY = (long)numY * 1024;
else if (strY.EndsWith("MB"))
bytesY = (long)numY * 1048576;
else if (strY.EndsWith("GB"))
bytesY = (long)numY * 1073741824;
else
bytesY = (long) numY;
var compareResult = bytesX.CompareTo(bytesY);
if (_orderOfSort == SortOrder.Ascending)
{
// Ascending sort is selected, return normal result of compare operation
return compareResult;
}
if (_orderOfSort == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation
return (-compareResult);
}
// 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;
}
}
}
}
Change namespace name with yours.
Add following line in your form.
private readonly ListViewSizeSorter _listViewSizeSorter;
public Form1()
{
InitializeComponent();
_listViewSizeSorter = new ListViewSizeSorter {SortColumn = 1};
}
private void ListViewFilesColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine if clicked column is already the column that is being sorted.
if (e.Column == _listViewColumnSorter.SortColumn)
{
if(e.Column.Equals(1))
{
listViewFiles.ListViewItemSorter = _listViewSizeSorter;
// Reverse the current sort direction for this column.
_listViewSizeSorter.Order = _listViewSizeSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
}
}
else
{
// Set the column number that is to be sorted; default to ascending.
_listViewColumnSorter.SortColumn = e.Column;
_listViewColumnSorter.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
listViewFiles.Sort();
}
Write a custom comparator for the sort function, like this:
/// <summary>
/// Comparator for values like 123 KB
/// </summary>
/// <param name="x">First value to compare</param>
/// <param name="y">Second value to compare</param>
/// <returns>0 for equal, 1 for x > y, -1 for x < y</returns>
int Compare(object x, object y)
{
// Convert to strings
string strX = null;
if (x is string)
strX = (string)x;
else if (x != null)
strX = x.ToString();
string strY = null;
if (y is string)
strY = (string)y;
else if (y != null)
strY = y.ToString();
// Nulls first (null means less, since it's blank)
if (strX == null)
{
if (strY == null)
return 0;
return -1;
}
else if (strY == null)
return 1;
// Convert the non-KB part to a number
double numX;
double numY;
if (strX.EndsWith("KB") || strX.EndsWith("GB") || strX.EndsWith("MB"))
strX = strX.Substring(0, strX.Length - 2);
if (strX.EndsWith("Bytes"))
strX = strX.Substring(0, strX.Length - 5);
strX = strX.Trim();
double.TryParse(strX, out numX);
if (strY.EndsWith("KB") || strY.EndsWith("GB") || strY.EndsWith("MB"))
strY = strY.Substring(0, strY.Length - 2);
if (strY.EndsWith("Bytes"))
strY = strX.Substring(0, strY.Length - 5);
strY = strY.Trim();
double.TryParse(strY, out numY);
// Compare the numbers
return numX.CompareTo(numY);
}
Have you tried what Microsoft has to say for this task? Basically, you'll have to implement a custom comparer, and handle ColumnClick
event. But since items in listview are stored as strings, you'll most likely need to do some parsing (assuming you want to sort by size), or implement workaround - sorting datasource and reasign items (which may be easier).
Quick example of pseudocode for workaround (as implementing custom comparers with parsing from string involved seems too cumbersome for such simple task). In ColumnClick
event handler, you could do this:
// I assume you got list of raw dll sizes somewhere
dllSizes.Sort();
List<ListViewItem> dllSizesForDisplay = new List<ListViewItem>();
foreach (var dll in dllSizes)
{
dllSizes.Add(new ListViewItem(GetSize(dll.Bytes)));
}
// reasign items
listView.Items.Clear();
listView.Items.AddRange(dllSizesForDisplay);