I've spent couple of hours now trying to work out where's the problem with adding icons to header of a listView. The code below works fine on Win 7 32Bit and Windows XP 32Bit. However I've now changed machine to Win 7 x64 and it's been hell with the code.
Below code works that when sorting is called on a column it adds icon up or down depending on order and resets any icons in header on other columns. However on Win7 x64 it doesn't change the icon up/down (it just uses the first one always) and never clears the icons on other columns (when sorting is taken out of it).
public class ListViewColumnSorter : IComparer {
[StructLayout(LayoutKind.Sequential)] public struct HDITEM {
public Int32 mask;
public Int32 cxy;
[MarshalAs(UnmanagedType.LPTStr)] public String pszText;
public IntPtr hbm;
public Int32 cchTextMax;
public Int32 fmt;
public Int32 lParam;
public Int32 iImage;
public Int32 iOrder;
} ;
public const Int32 HDI_FORMAT = 0x0004;
public const Int32 HDI_IMAGE = 0x0020;
public const Int32 HDI_DI_SETITEM = 0x0040;
public const Int32 HDF_LEFT = 0x0000;
public const Int32 HDF_RIGHT = 0x0001;
public const Int32 HDF_CENTER = 0x0002;
public const Int32 HDF_JUSTIFYMASK = 0x0003;
public const Int32 HDF_STRING = 0x4000;
public const Int32 HDF_BITMAP_ON_RIGHT = 0x1000;
public const Int32 HDF_IMAGE = 0x0800;
//Parameters for ListViews
public const Int32 LVM_FIRST = 0x1000;
public const Int32 LVM_GETHEADER = LVM_FIRST + 31;
//Messages for ListView-Headers
public const Int32 HDM_FIRST = 0x1200;
public const Int32 HDM_SETIMAGELIST = HDM_FIRST + 8;
public const Int32 HDM_GETIMAGELIST = HDM_FIRST + 9;
public const Int32 HDM_GETITEM = HDM_FIRST + 11;
public const Int32 HDM_SETITEM = HDM_FIRST + 12;
[DllImport("user32", EntryPoint = "SendMessage")] private static extern IntPtr SendMessageINT(IntPtr Handle, Int32 msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32", EntryPoint = "SendMessage")] private static extern IntPtr SendMessageITEM(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam);
public static IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam, IntPtr lParam) {
return SendMessageINT(Handle, msg, wParam, lParam);
}
public static IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam) {
return SendMessageITEM(Handle, msg, wParam, ref lParam);
}
/// <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 = (ListViewItem) x;
ListViewItem listviewY = (ListViewItem) y;
string s1 = listviewX.SubItems.Count > ColumnToSort ? listviewX.SubItems[ColumnToSort].Text : String.Empty;
string s2 = listviewY.SubItems.Count > ColumnToSort ? listviewY.SubItems[ColumnToSort].Text : String.Empty;
int i1;
int i2;
DateTime date1;
DateTime date2;
if (int.TryParse(s1, out i1) && int.TryParse(s2, out i2)) {
compareResult = ObjectCompare.Compare(i1, i2);
} else if (DateTime.TryParse(s1, out date1) && DateTime.TryParse(s2, out date2)) {
compareResult = ObjectCompare.Compare(date1, date2);
} else {
compareResult = ObjectCompare.Compare(s1, s2);
}
// Compare the two items
// try {
// // Parse the two objects passed as a parameter as a DateTime.
// System.DateTime firstDate = DateTime.Parse();
// System.DateTime secondDate = DateTime.Parse(((ListViewItem)y).SubItems[col].Text);
// // Compare the two dates.
// returnVal = DateTime.Compare(firstDate, secondDate);
//}
//// If neither compared object has a valid date format, compare
//// as a string.
//catch
//{
// // Compare the two items as a string.
// returnVal = String.Compare(((ListViewItem)x).SubItems[col].Text,
// ((ListViewItem)y).SubItems[col].Text);
//}
//try {
// } catch {
// compareResult = 0;
// }
// 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; }
}
public static void columnClick(ListView varListView, ref ListViewColumnSorter lvwColumnSorter, object sender, ColumnClickEventArgs e) {
ListViewHelper.enableDoubleBuffer(varListView);
if (e.Column == lvwColumnSorter.SortColumn) {
if (lvwColumnSorter.Order == SortOrder.Ascending) {
lvwColumnSorter.Order = SortOrder.Descending;
} else {
lvwColumnSorter.Order = SortOrder.Ascending;
}
} else {
lvwColumnSorter.SortColumn = e.Column;
lvwColumnSorter.Order = SortOrder.Ascending;
}
varListView.Sort();
}
public static void columnClick(ListView varListView, ref ListViewColumnSorter lvwColumnSorter, object sender, ColumnClickEventArgs e, ImageList headerImages) {
//get list view header
IntPtr hHeader = SendMessage(varListView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
SendMessage(hHeader, HDM_SETIMAGELIST, IntPtr.Zero, headerImages.Handle);
SortOrder Order = SortOrder.Descending;
//format icons
HDITEM hd = new HDITEM {
mask = HDI_IMAGE | HDI_FORMAT
};
for (int i = 0; i < varListView.Columns.Count; i++) {
// if (i != e.Column) {
hd.fmt = HDF_STRING;
hd.iImage = -1;
SendMessage(hHeader, HDM_SETITEM, new IntPtr(i), ref hd);
// }
}
hd.fmt = HDF_LEFT | HDF_STRING | HDF_BITMAP_ON_RIGHT;
ListViewHelper.enableDoubleBuffer(varListView);
if (Order != SortOrder.None) {
hd.fmt |= HDF_IMAGE;
}
if (e.Column == lvwColumnSorter.SortColumn) {
if (lvwColumnSorter.Order == SortOrder.Ascending) {
hd.iImage = 0;
lvwColumnSorter.Order = SortOrder.Descending;
} else {
hd.iImage = 1;
lvwColumnSorter.Order = SortOrder.Ascending;
}
} else {
hd.iImage = 1;
lvwColumnSorter.SortColumn = e.Column;
lvwColumnSorter.Order = SortOrder.Ascending;
}
SendMessage(hHeader, HDM_SETITEM, new IntPtr(e.Column), ref hd);
varListView.Sort();
}
}
I'm using it on ColumnClick event :
private void listViewKlienci_ColumnClick(object sender, ColumnClickEventArgs e) {
ListViewColumnSorter.columnClick(listViewKlienci, ref lvwColumnSorterKlienci, sender, e, headerIcons);
}
where headerIcons is ImageList which holds some icons.
I am also adding:
private ListViewColumnSorter lvwColumnSorterKlienci = new ListViewColumnSorter();
And in the MainForm initializer I do:
listViewKlienci.ListViewItemSorter = lvwColumnSorterKlienci;
So where do I go wrong on this? Why would it be diffrent on x64 :/
Hope someone can help.