Grungondola
Grungondola

Reputation: 678

Setting ListViewItem's Checked state using WinAPI

I have the following code that I am trying to use to check and uncheck items in a list view. Underneath everything, the checking functionality seems to be working. The problem is that the uncheck functionality does not work, and checking the item that is already checked simply makes the check box visible without firing the checked event or unchecking the box. Everything I've found has shown me that this should work, but I am just not able to come up with a working solution here. Can anybody explain why 0x1000 is not unchecking the items and why the check boxes go invisible on me?

Thank you

  public class NativeMethods
  {
    private const int LVM_FIRST = 0x1000;
    private const int LVM_SETITEMSTATE = LVM_FIRST + 43;

    private const int LVIF_STATE = 0x8;

    private const int LVIS_UNCHECKED = 0x1000;
    private const int LVIS_CHECKED = 0x2000;
    private const int LVIS_CHECKEDMASK = 0x3000;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct LVITEM
    {
      public int mask;
      public int iItem;
      public int iSubItem;
      public int state;
      public int stateMask;
      [MarshalAs(UnmanagedType.LPTStr)]
      public string pszText;
      public int cchTextMax;
      public int iImage;
      public IntPtr lParam;
      public int iIndent;
      public int iGroupId;
      public int cColumns;
      public IntPtr puColumns;
    };

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessageLVItem(IntPtr hWnd, int msg, int wParam, ref LVITEM lvi);

    /// <summary>
    /// Check all rows on the given listview
    /// </summary>
    /// <param name="list">The listview whose items are to be checked</param>
    public static void CheckAllItems(ListView list)
    { // -1 index means all items in the list
      SetItemState(list, -1, LVIS_CHECKED, LVIS_CHECKEDMASK);
    }

    /// <summary>
    /// Uncheck all rows on the given listview
    /// </summary>
    /// <param name="list">The listview whose items are to be unchecked</param>
    public static void UncheckAllItems(ListView list)
    { // -1 index means all items in the list
      SetItemState(list, -1, LVIS_UNCHECKED, LVIS_CHECKEDMASK);
    }

    /// <summary>
    /// Set the item state on the given item
    /// </summary>
    /// <param name="list">The listview whose item's state is to be changed</param>
    /// <param name="itemIndex">The index of the item to be changed</param>
    /// <param name="mask">Which bits of the value are to be set?</param>
    /// <param name="value">The value to be set</param>
    public static void SetItemState(ListView list, int itemIndex, int mask, int value)
    {
      LVITEM lvItem = new LVITEM();
      lvItem.mask = LVIF_STATE;
      lvItem.stateMask = mask;
      lvItem.state = value;
      SendMessageLVItem(list.Handle, LVM_SETITEMSTATE, itemIndex, ref lvItem);
    }
  }

EDIT

I double checked behavior here, and this happens when I select "Check All" for items that are unchecked and "Uncheck All" for items that are checked. They become invisible. "Uncheck All" does not change the item from checked to unchecked, but it is clearly the correct state, as selecting "Unselect All" with no items checked results in no changes at all. The same is true for "Select All" when all items are checked already.

Upvotes: 2

Views: 1855

Answers (1)

Grungondola
Grungondola

Reputation: 678

I apologize for my error here. I switched the mask and value around, so here is the correct solution:

public class NativeMethods
{
  private const int LVM_FIRST = 0x1000;
  private const int LVM_SETITEMSTATE = LVM_FIRST + 43;

  private const int LVIS_UNCHECKED = 0x1000;
  private const int LVIS_CHECKED = 0x2000;
  private const int LVIS_STATEIMAGEMASK = 0x3000;

  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  public struct LVITEM
  {
    public int mask;
    public int iItem;
    public int iSubItem;
    public int state;
    public int stateMask;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pszText;
    public int cchTextMax;
    public int iImage;
    public IntPtr lParam;
    public int iIndent;
    public int iGroupId;
    public int cColumns;
    public IntPtr puColumns;
  };

  [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
  public static extern IntPtr SendMessageLVItem(IntPtr hWnd, int msg, int wParam, ref LVITEM lvi);

  /// <summary>
  /// Select all rows on the given listview
  /// </summary>
  /// <param name="list">The listview whose items are to be selected</param>
  public static void CheckAllItems(ListView list)
  {
    SetItemState(list, -1, LVIS_STATEIMAGEMASK, LVIS_CHECKED);
  }

  /// <summary>
  /// Deselect all rows on the given listview
  /// </summary>
  /// <param name="list">The listview whose items are to be deselected</param>
  public static void UncheckAllItems(ListView list)
  {
    SetItemState(list, -1, LVIS_STATEIMAGEMASK, LVIS_UNCHECKED);
  }

  /// <summary>
  /// Set the item state on the given item
  /// </summary>
  /// <param name="list">The listview whose item's state is to be changed</param>
  /// <param name="itemIndex">The index of the item to be changed</param>
  /// <param name="mask">Which bits of the value are to be set?</param>
  /// <param name="value">The value to be set</param>
  public static void SetItemState(ListView list, int itemIndex, int mask, int value)
  {
    LVITEM lvItem = new LVITEM();
    lvItem.stateMask = mask;
    lvItem.state = value;
    SendMessageLVItem(list.Handle, LVM_SETITEMSTATE, itemIndex, ref lvItem);
  }
}

Upvotes: 5

Related Questions