如何禁用 WinForms TreeView 节点复选框?

发布于 2024-07-16 06:28:56 字数 247 浏览 10 评论 0原文

我需要能够禁用 WinForms 应用程序的 TreeView 控件中的某些复选框,但标准的 TreeView 控件中没有内置此类功能。

我已经在使用 TreeView.BeforeCheck 事件,如果节点被禁用并且工作得很好,则取消它。

我还将禁用节点的 ForeColor 更改为 GrayText

有人有一个简单而强大的解决方案吗?

I need to be able to disable some of the checkboxes in a TreeView control of a WinForms application, but there's no such functionality built-in to the standard TreeView control.

I am already using the TreeView.BeforeCheck event and cancel it if the node is disabled and that works perfectly fine.

I also change the ForeColor of the disabled nodes to GrayText.

Does anyone have a simple and robust solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

负佳期 2024-07-23 06:28:56

由于 C++ 中有支持,我们可以使用 p/invoke 来解决它。

这是 p/invoke 部分的设置,只需将其提供给调用类即可。

    // constants used to hide a checkbox
    public const int TVIF_STATE = 0x8;
    public const int TVIS_STATEIMAGEMASK = 0xF000;
    public const int TV_FIRST = 0x1100;
    public const int TVM_SETITEM = TV_FIRST + 63;

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

    // struct used to set node properties
    public 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;

    } 

我们希望逐个节点地确定。 最简单的方法是在绘制节点事件上。 为了这个事件,我们必须将树设置为所有者绘制,因此请务必将其设置为默认设置以外的其他设置。

this.tree.DrawMode = TreeViewDrawMode.OwnerDrawText;
this.tree.DrawNode += new DrawTreeNodeEventHandler(tree_DrawNode);

在您的 tree_DrawNode 函数中确定正在绘制的节点是否应该有一个复选框,并在适​​当时隐藏它。 然后将默认绘制属性设置为 true,因为我们不想担心绘制所有其他细节。

void tree_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
    if (e.Node.Level == 1)
    {
        HideCheckBox(e.Node);
        e.DrawDefault = true;
    }
    else 
    {
        e.Graphics.DrawString(e.Node.Text, e.Node.TreeView.Font,
           Brushes.Black, e.Node.Bounds.X, e.Node.Bounds.Y);
    }
}

最后,实际调用我们定义的函数:

private void HideCheckBox(TreeNode node)
{
    TVITEM tvi = new TVITEM();
    tvi.hItem = node.Handle;
    tvi.mask = TVIF_STATE;
    tvi.stateMask = TVIS_STATEIMAGEMASK;
    tvi.state = 0;
    IntPtr lparam = Marshal.AllocHGlobal(Marshal.SizeOf(tvi));
    Marshal.StructureToPtr(tvi, lparam, false);
    SendMessage(node.TreeView.Handle, TVM_SETITEM, IntPtr.Zero, lparam);
}

Since there's support in C++ we can resolve it using p/invoke.

Here's the setup for the p/invoke part, just make it available to the calling class.

    // constants used to hide a checkbox
    public const int TVIF_STATE = 0x8;
    public const int TVIS_STATEIMAGEMASK = 0xF000;
    public const int TV_FIRST = 0x1100;
    public const int TVM_SETITEM = TV_FIRST + 63;

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

    // struct used to set node properties
    public 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;

    } 

We want to determine on a node by node basis. The easiest way to do that is on the draw node event. We have to set our tree to be set as owner drawn in order for this event, so be sure to set that to something other than the default setting.

this.tree.DrawMode = TreeViewDrawMode.OwnerDrawText;
this.tree.DrawNode += new DrawTreeNodeEventHandler(tree_DrawNode);

In your tree_DrawNode function determine if the node being drawn is supposed to have a checkbox, and hide it when approriate. Then set the Default Draw property to true since we don't want to worry about drawing all the other details.

void tree_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
    if (e.Node.Level == 1)
    {
        HideCheckBox(e.Node);
        e.DrawDefault = true;
    }
    else 
    {
        e.Graphics.DrawString(e.Node.Text, e.Node.TreeView.Font,
           Brushes.Black, e.Node.Bounds.X, e.Node.Bounds.Y);
    }
}

Lastly, the actual call to the function we defined:

private void HideCheckBox(TreeNode node)
{
    TVITEM tvi = new TVITEM();
    tvi.hItem = node.Handle;
    tvi.mask = TVIF_STATE;
    tvi.stateMask = TVIS_STATEIMAGEMASK;
    tvi.state = 0;
    IntPtr lparam = Marshal.AllocHGlobal(Marshal.SizeOf(tvi));
    Marshal.StructureToPtr(tvi, lparam, false);
    SendMessage(node.TreeView.Handle, TVM_SETITEM, IntPtr.Zero, lparam);
}
伤痕我心 2024-07-23 06:28:56

这是 PowerShell 版本,非常感谢 @sam-trost 的救星代码!

P/调用:

$TypeDefinition = @'
using System;
using System.Runtime.InteropServices;
namespace Win32Functions {
    public class Win32TreeView {
        // constants used to hide a checkbox
        public const int TVIF_STATE = 0x8;
        public const int TVIS_STATEIMAGEMASK = 0xF000;
        public const int TV_FIRST = 0x1100;
        public const int TVM_SETITEM = TV_FIRST + 63;

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        // struct used to set node properties
        public 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;
        }
    }
}
'@

Add-Type -TypeDefinition $TypeDefinition -PassThru

事件处理程序:

$TreeView1_DrawNode = [System.Windows.Forms.DrawTreeNodeEventHandler]{
    #Event Argument: $_ = [System.Windows.Forms.DrawTreeNodeEventArgs]
    if ($null -ne $_.Node) {

        # P/invoke hack to hide Node CheckBox
        if ($_.Node.Level -eq 0) {
            Hide-NodeCheckBox($_.Node)
        }

        $_.DrawDefault = $true
    }
}

TreeView:

$TreeView1.DrawMode = [TreeViewDrawMode]::OwnerDrawText
$TreeView1.add_DrawNode($TreeView1_DrawNode)

功能:

function Hide-NodeCheckBox([TreeNode]$node) {
    # P/invoke hack to hide Node CheckBox
    if ($node.TreeView.CheckBoxes) {
        $tvi = [Win32Functions.Win32TreeView+TVITEM]::new()
        $tvi.hItem = $node.Handle
        $tvi.mask = [Win32Functions.Win32TreeView]::TVIF_STATE
        $tvi.stateMask = [Win32Functions.Win32TreeView]::TVIS_STATEIMAGEMASK
        $tvi.state = 0
        [IntPtr]$lparam = [Marshal]::AllocHGlobal([Marshal]::SizeOf($tvi))
        [Marshal]::StructureToPtr($tvi, $lparam, $false)
        [Win32Functions.Win32TreeView]::SendMessage($node.TreeView.Handle, [Win32Functions.Win32TreeView]::TVM_SETITEM, [IntPtr]::Zero, $lparam)
    }
}

This is PowerShell version, many thanks for @sam-trost for his life-savior code!

P/invoke:

$TypeDefinition = @'
using System;
using System.Runtime.InteropServices;
namespace Win32Functions {
    public class Win32TreeView {
        // constants used to hide a checkbox
        public const int TVIF_STATE = 0x8;
        public const int TVIS_STATEIMAGEMASK = 0xF000;
        public const int TV_FIRST = 0x1100;
        public const int TVM_SETITEM = TV_FIRST + 63;

        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        // struct used to set node properties
        public 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;
        }
    }
}
'@

Add-Type -TypeDefinition $TypeDefinition -PassThru

Event handler:

$TreeView1_DrawNode = [System.Windows.Forms.DrawTreeNodeEventHandler]{
    #Event Argument: $_ = [System.Windows.Forms.DrawTreeNodeEventArgs]
    if ($null -ne $_.Node) {

        # P/invoke hack to hide Node CheckBox
        if ($_.Node.Level -eq 0) {
            Hide-NodeCheckBox($_.Node)
        }

        $_.DrawDefault = $true
    }
}

TreeView:

$TreeView1.DrawMode = [TreeViewDrawMode]::OwnerDrawText
$TreeView1.add_DrawNode($TreeView1_DrawNode)

Function:

function Hide-NodeCheckBox([TreeNode]$node) {
    # P/invoke hack to hide Node CheckBox
    if ($node.TreeView.CheckBoxes) {
        $tvi = [Win32Functions.Win32TreeView+TVITEM]::new()
        $tvi.hItem = $node.Handle
        $tvi.mask = [Win32Functions.Win32TreeView]::TVIF_STATE
        $tvi.stateMask = [Win32Functions.Win32TreeView]::TVIS_STATEIMAGEMASK
        $tvi.state = 0
        [IntPtr]$lparam = [Marshal]::AllocHGlobal([Marshal]::SizeOf($tvi))
        [Marshal]::StructureToPtr($tvi, $lparam, $false)
        [Win32Functions.Win32TreeView]::SendMessage($node.TreeView.Handle, [Win32Functions.Win32TreeView]::TVM_SETITEM, [IntPtr]::Zero, $lparam)
    }
}
茶花眉 2024-07-23 06:28:56

TreeView.BeforeCheck——注册此事件,检查该节点是否是允许选中复选框的节点,如果不能选中,则可以通过设置 TreeViewCancelEventArgs 上的 Cancel 属性来取消该事件。 这有望阻止用户选中这些框,但不会带来最佳的用户体验。

要删除不可检查项目的复选框,您可以使用所有者绘制在复选框上绘制一个实心矩形以将其删除。

TreeView.BeforeCheck -- register for this event, check whether the node is one where the checkboxes are allowed to be checked or not and, if it cannot be checked then you can cancel the event by setting the Cancel property on the TreeViewCancelEventArgs. That should hopefully prevent the user from checking those boxes but will not make for the best user-experience.

To remove the checkboxes for the non-checkable items, you could possibly use owner-draw to draw a solid rectangle over the check-box to remove it.

煮酒 2024-07-23 06:28:56

没有内置任何东西可以做到这一点。 您可以使用 BeforeCheck 事件并为所需节点取消该事件。 如果复选框的外观很重要,那么您需要在那里放置一个图像以显示该复选框已禁用。

链接 可能会引起您的兴趣。

There is nothing inbuilt to do this. You can use the BeforeCheck event and cancel it for the desired nodes. In case the appearance of the checkbox matters, then you will need to place a image there to show the checkbox disabled.

This link might be of your interest.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文