views:

108

answers:

1

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.

+2  A: 

public Int32 lParam; should be public IntPtr lParam;

The problem is that when you make it an Int32, the x64 OS is expecting it to by 8 bytes, but you are only giving it 4 bytes, misaligning everything that follows. So in the place where the OS is expecting an lParam, you are giving it an lParam and an iImage; where the OS is expecting an iImage, you are giving it an iOrder, and where the OS is expecting an iOrder, you are giving it uninitialized random bits.

You thought you were telling the OS which image and where it should go, but the OS heard nonsense.

Gabe
Spot on! +5 for that! I would have never guessed to look there. Now if i only knew why it behaved like that would be superb :-) Why would it work on 32bit and not on 64bit. Long way ahead of me :(
MadBoy
Will it work fine on 32bit os after that change without problems?
MadBoy
Yes, it will work on either 32-bit or 64-bit OS with the change.
Gabe