WinForms TreeView: check if Checkbox is hidden

2019-09-18 09:25发布

问题:

I am using the following solution to show or hide checkboxes in a TreeView:

https://stackoverflow.com/a/22230299/1583649

However, now i would like to know how to check if the checkbox is hidden or not for the node.

For example, i want to be able to set checkbox.checked to true (or false) on only the children of a top-level node that have a checkbox visible when the top-level node is checked. Is there a way to do this?

回答1:

TVM_GETITEM or TVM_GETITEMSTATE can be used to get the state, which can then be used to deduce which checkbox image (none, checked or unchecked) is on the node. Here's an extension class that provides the two methods you need.

USAGE:

// something like this
var treeNode = ...;
if (treeNode.IsCheckBoxVisible())
    treeNode.SetIsCheckBoxVisible(false);
else
    treeNode.SetIsCheckBoxVisible(true);

EXTENSIONS:

public static class TreeViewExtensions
{
    /// <summary>
    /// Gets a value indicating if the checkbox is visible on the tree node.
    /// </summary>
    /// <param name="node">The tree node.</param>
    /// <returns><value>true</value> if the checkbox is visible on the tree node; otherwise <value>false</value>.</returns>
    public static bool IsCheckBoxVisible(this TreeNode node)
    {
        if (node == null)
            throw new ArgumentNullException("node");
        if (node.TreeView == null)
            throw new InvalidOperationException("The node does not belong to a tree.");
        var tvi = new TVITEM
            {
                hItem = node.Handle,
                mask = TVIF_STATE
            };
        var result = SendMessage(node.TreeView.Handle, TVM_GETITEM, node.Handle, ref tvi);
        if (result == IntPtr.Zero)
            throw new ApplicationException("Error getting TreeNode state.");
        var imageIndex = (tvi.state & TVIS_STATEIMAGEMASK) >> 12;
        return (imageIndex != 0);
    }

    /// <summary>
    /// Sets a value indicating if the checkbox is visible on the tree node.
    /// </summary>
    /// <param name="node">The tree node.</param>
    /// <param name="value"><value>true</value> to make the checkbox visible on the tree node; otherwise <value>false</value>.</param>
    public static void SetIsCheckBoxVisible(this TreeNode node, bool value)
    {
        if (node == null)
            throw new ArgumentNullException("node");
        if (node.TreeView == null)
            throw new InvalidOperationException("The node does not belong to a tree.");
        var tvi = new TVITEM
            {
                hItem = node.Handle,
                mask = TVIF_STATE,
                stateMask = TVIS_STATEIMAGEMASK,
                state = (value ? node.Checked ? 2 : 1 : 0) << 12
            };
        var result = SendMessage(node.TreeView.Handle, TVM_SETITEM, IntPtr.Zero, ref tvi);
        if (result == IntPtr.Zero)
            throw new ApplicationException("Error setting TreeNode state.");
    }

    private const int TVIF_STATE = 0x8;
    private const int TVIS_STATEIMAGEMASK = 0xF000;
    private const int TV_FIRST = 0x1100;
    private const int TVM_GETITEM = TV_FIRST + 62;
    private const int TVM_SETITEM = TV_FIRST + 63;

    [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
    private struct TVITEM
    {
        public int mask;
        public IntPtr hItem;
        public int state;
        public int stateMask;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpszText;
        public int cchTextMax;
        public int iImage;
        public int iSelectedImage;
        public int cChildren;
        public IntPtr lParam;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref TVITEM lParam);
}