From ca992832951c586d2cad7f1785b4609f239e03fd Mon Sep 17 00:00:00 2001 From: Sam Izzo Date: Fri, 1 Apr 2016 16:02:10 +1100 Subject: [PATCH 1/2] Sort ListView when clicking on column headers. --- SteamCloudFileManager/MainForm.Designer.cs | 1 + SteamCloudFileManager/MainForm.cs | 55 +++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/SteamCloudFileManager/MainForm.Designer.cs b/SteamCloudFileManager/MainForm.Designer.cs index 104414c..e82cdde 100644 --- a/SteamCloudFileManager/MainForm.Designer.cs +++ b/SteamCloudFileManager/MainForm.Designer.cs @@ -64,6 +64,7 @@ private void InitializeComponent() this.remoteListView.TabIndex = 0; this.remoteListView.UseCompatibleStateImageBehavior = false; this.remoteListView.View = System.Windows.Forms.View.Details; + this.remoteListView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.remoteListView_ColumnClick); this.remoteListView.SelectedIndexChanged += new System.EventHandler(this.remoteListView_SelectedIndexChanged); // // columnHeader1 diff --git a/SteamCloudFileManager/MainForm.cs b/SteamCloudFileManager/MainForm.cs index 1aff118..bf486f3 100644 --- a/SteamCloudFileManager/MainForm.cs +++ b/SteamCloudFileManager/MainForm.cs @@ -7,12 +7,14 @@ using System.Text; using System.Windows.Forms; using System.IO; +using System.Collections; namespace SteamCloudFileManager { public partial class MainForm : Form { IRemoteStorage storage; + int sortColumn; public MainForm() { @@ -59,7 +61,14 @@ private void refreshButton_Click(object sender, EventArgs e) remoteListView.Items.Clear(); foreach (IRemoteFile file in files) { - ListViewItem itm = new ListViewItem(new string[] { file.Name, file.Timestamp.ToString(), file.Size.ToString(), file.IsPersisted.ToString(), file.Exists.ToString() }) { Tag = file }; + ListViewItem itm = new ListViewItem(); + itm.SubItems[0].Text = file.Name; + itm.SubItems[0].Tag = file.Name; + itm.SubItems.Add(new ListViewItem.ListViewSubItem(itm, file.Timestamp.ToString()) { Tag = file.Timestamp }); + itm.SubItems.Add(new ListViewItem.ListViewSubItem(itm, file.Size.ToString()) { Tag = file.Size }); + itm.SubItems.Add(new ListViewItem.ListViewSubItem(itm, file.IsPersisted.ToString()) { Tag = file.IsPersisted }); + itm.SubItems.Add(new ListViewItem.ListViewSubItem(itm, file.Exists.ToString()) { Tag = file.Exists }); + itm.Tag = file; remoteListView.Items.Add(itm); } updateQuota(); @@ -154,5 +163,49 @@ private void remoteListView_SelectedIndexChanged(object sender, EventArgs e) { downloadButton.Enabled = deleteButton.Enabled = (storage != null && remoteListView.SelectedIndices.Count > 0); } + + private class ListViewItemComparer : IComparer + { + private int column; + private SortOrder sortOrder; + + public ListViewItemComparer(int column, SortOrder sortOrder) + { + this.column = column; + this.sortOrder = sortOrder; + } + + public int Compare(object x, object y) + { + var xx = ((ListViewItem)x).SubItems[column]; + var yy = ((ListViewItem)y).SubItems[column]; + var a = xx.Tag as IComparable; + var b = yy.Tag as IComparable; + var order = (sortOrder == SortOrder.Ascending ? 1 : -1); + if (a != null && b != null) + return a.CompareTo(b) * order; + + // If the userdata isnt IComparable just fall back to string compare. + return String.Compare(xx.Text, yy.Text) * order; + } + }; + + private void remoteListView_ColumnClick(object sender, ColumnClickEventArgs e) + { + if (sortColumn != e.Column) + { + // If column is different to last column just sort ascending. + sortColumn = e.Column; + remoteListView.Sorting = SortOrder.Ascending; + } + else + { + // Otherwise toggle between ascending/descending. + remoteListView.Sorting = remoteListView.Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; + } + + remoteListView.ListViewItemSorter = new ListViewItemComparer(e.Column, remoteListView.Sorting); + remoteListView.Sort(); + } } } From 15c175e39d11cb2d3c724ee0e69a019dcb35f4d6 Mon Sep 17 00:00:00 2001 From: Sam Izzo Date: Fri, 1 Apr 2016 16:05:49 +1100 Subject: [PATCH 2/2] Set the ListView column header icon based on sort order. --- SteamCloudFileManager/ListViewExtensions.cs | 99 +++++++++++++++++++ SteamCloudFileManager/MainForm.cs | 1 + .../SteamCloudFileManager.csproj | 1 + 3 files changed, 101 insertions(+) create mode 100644 SteamCloudFileManager/ListViewExtensions.cs diff --git a/SteamCloudFileManager/ListViewExtensions.cs b/SteamCloudFileManager/ListViewExtensions.cs new file mode 100644 index 0000000..1c00765 --- /dev/null +++ b/SteamCloudFileManager/ListViewExtensions.cs @@ -0,0 +1,99 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace SteamCloudFileManager +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public static class ListViewExtensions + { + [StructLayout(LayoutKind.Sequential)] + public struct HDITEM + { + public Mask mask; + public int cxy; + [MarshalAs(UnmanagedType.LPTStr)] public string pszText; + public IntPtr hbm; + public int cchTextMax; + public Format fmt; + public IntPtr lParam; + // _WIN32_IE >= 0x0300 + public int iImage; + public int iOrder; + // _WIN32_IE >= 0x0500 + public uint type; + public IntPtr pvFilter; + // _WIN32_WINNT >= 0x0600 + public uint state; + + [Flags] + public enum Mask + { + Format = 0x4, // HDI_FORMAT + }; + + [Flags] + public enum Format + { + SortDown = 0x200, // HDF_SORTDOWN + SortUp = 0x400, // HDF_SORTUP + }; + }; + + public const int LVM_FIRST = 0x1000; + public const int LVM_GETHEADER = LVM_FIRST + 31; + + public const int HDM_FIRST = 0x1200; + public const int HDM_GETITEM = HDM_FIRST + 11; + public const int HDM_SETITEM = HDM_FIRST + 12; + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, ref HDITEM lParam); + + public static void SetSortIcon(this ListView listViewControl, int columnIndex, SortOrder order) + { + IntPtr columnHeader = SendMessage(listViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); + for (int columnNumber = 0; columnNumber <= listViewControl.Columns.Count - 1; columnNumber++) + { + var columnPtr = new IntPtr(columnNumber); + var item = new HDITEM + { + mask = HDITEM.Mask.Format + }; + + if (SendMessage(columnHeader, HDM_GETITEM, columnPtr, ref item) == IntPtr.Zero) + { + throw new Win32Exception(); + } + + if (order != SortOrder.None && columnNumber == columnIndex) + { + switch (order) + { + case SortOrder.Ascending: + item.fmt &= ~HDITEM.Format.SortDown; + item.fmt |= HDITEM.Format.SortUp; + break; + case SortOrder.Descending: + item.fmt &= ~HDITEM.Format.SortUp; + item.fmt |= HDITEM.Format.SortDown; + break; + } + } + else + { + item.fmt &= ~HDITEM.Format.SortDown & ~HDITEM.Format.SortUp; + } + + if (SendMessage(columnHeader, HDM_SETITEM, columnPtr, ref item) == IntPtr.Zero) + { + throw new Win32Exception(); + } + } + } + } +} diff --git a/SteamCloudFileManager/MainForm.cs b/SteamCloudFileManager/MainForm.cs index bf486f3..27deced 100644 --- a/SteamCloudFileManager/MainForm.cs +++ b/SteamCloudFileManager/MainForm.cs @@ -204,6 +204,7 @@ private void remoteListView_ColumnClick(object sender, ColumnClickEventArgs e) remoteListView.Sorting = remoteListView.Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; } + remoteListView.SetSortIcon(e.Column, remoteListView.Sorting); remoteListView.ListViewItemSorter = new ListViewItemComparer(e.Column, remoteListView.Sorting); remoteListView.Sort(); } diff --git a/SteamCloudFileManager/SteamCloudFileManager.csproj b/SteamCloudFileManager/SteamCloudFileManager.csproj index 129038f..507f822 100644 --- a/SteamCloudFileManager/SteamCloudFileManager.csproj +++ b/SteamCloudFileManager/SteamCloudFileManager.csproj @@ -70,6 +70,7 @@ + Form