tags:

views:

2968

answers:

2

How can I display a sort arrow in the header of the sorted column in a list view which follows the native look of the operating system?

+17  A: 

You can use the following extension method to set the sort arrow to a particular column:

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static class ListViewExtensions
{
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    private struct LVCOLUMN
    {
     public Int32 mask;
     public Int32 cx;
     [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
     public string pszText;
     public IntPtr hbm;
     public Int32 cchTextMax;
     public Int32 fmt;
     public Int32 iSubItem;
     public Int32 iImage;
     public Int32 iOrder;
    }

    private const Int32 HDI_FORMAT = 0x4;
    private const Int32 HDF_SORTUP = 0x400;
    private const Int32 HDF_SORTDOWN = 0x200;
    private const Int32 LVM_GETHEADER = 0x101f;
    private const Int32 HDM_GETITEM = 0x120b;
    private const Int32 HDM_SETITEM = 0x120c;

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SendMessage")]
    private static extern IntPtr SendMessageLVCOLUMN(IntPtr hWnd, Int32 Msg, IntPtr wParam, ref LVCOLUMN lPLVCOLUMN);

    public static void SetSortIcon(this System.Windows.Forms.ListView ListViewControl, int ColumnIndex, System.Windows.Forms.SortOrder Order)
    {
     IntPtr ColumnHeader = SendMessage(ListViewControl.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);

     for (int ColumnNumber = 0; ColumnNumber <= ListViewControl.Columns.Count - 1; ColumnNumber++)
     {
      IntPtr ColumnPtr = new IntPtr(ColumnNumber);
      LVCOLUMN lvColumn = new LVCOLUMN();
      lvColumn.mask = HDI_FORMAT;
      SendMessageLVCOLUMN(ColumnHeader, HDM_GETITEM, ColumnPtr, ref lvColumn);

      if (!(Order == System.Windows.Forms.SortOrder.None) && ColumnNumber == ColumnIndex)
      {
       switch (Order)
       {
        case System.Windows.Forms.SortOrder.Ascending:
         lvColumn.fmt &= ~HDF_SORTDOWN;
         lvColumn.fmt |= HDF_SORTUP;
         break;
        case System.Windows.Forms.SortOrder.Descending:
         lvColumn.fmt &= ~HDF_SORTUP;
         lvColumn.fmt |= HDF_SORTDOWN;
         break;
       }
      }
      else
      {
       lvColumn.fmt &= ~HDF_SORTDOWN & ~HDF_SORTUP;
      }

      SendMessageLVCOLUMN(ColumnHeader, HDM_SETITEM, ColumnPtr, ref lvColumn);
     }
    }
}

Then, you can call the extension method like such:

myListView.SetSortIcon(0, SortOrder.Ascending);
Andrew Moore
Note that Application.EnableVisualStyles(); must be called for the code above to work.
Rune
No it doesn't. It works as well for unstyled ListViews.
Andrew Moore
The LVCOLUMN you defined is actually HDITEM. I realize since you define it yourself that it doesn't matter, but when translating that into C++ it confused me for a few :).
Bob
+1: Almost a year and a half later, and I find this. Great work, many thanks!
Codesleuth
A: 

Works like a charm! thank you

Jerome Vineau