C# 允许在 .NET TreeView 中进行多选

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/206096/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 18:00:06  来源:igfitidea点击:

Allow multi-select in a .NET TreeView

c#.netwinforms.net-2.0treeview

提问by Darcy Casselman

I'm stuck in .NET 2.0 Windows Forms.

我被困在 .NET 2.0 Windows 窗体中。

It doesn't look like the ability to select multiple nodes exists in the standard TreeViewcontrol.

标准TreeView控件中似乎不存在选择多个节点的能力。

I'm trying to do this for a context menu selection. So check boxes aren't an acceptable UI paradigm here.

我正在尝试为上下文菜单选择执行此操作。因此,复选框在这里不是可接受的 UI 范例。

What's the best way to provide that very necessary functionality?

提供这种非常必要的功能的最佳方式是什么?

采纳答案by Jeff Yates

We did this in a WTL project once, but the basic work needed is the same for .NET. To achieve a multiple selection tree control, you will need to draw the tree items yourself and override the keyboard and mouse handling. You will also need to maintain your own list of items that are selected.

我们曾经在一个 WTL 项目中这样做过,但所需的基本工作对于 .NET 是相同的。要实现多选树控件,您需要自己绘制树项并覆盖键盘和鼠标处理。您还需要维护自己的所选项目列表。

Don't forget to consider selection rules (are parents and children allowed, for example), and don't forget to implement the keyboard shortcuts including selection using Ctrl, Shift, and Ctrl+Shift, as well as the Spacebar for selecting/deselecting.

不要忘记考虑选择规则(例如,父母和孩子是否允许),不要忘记实现键盘快捷键,包括使用 Ctrl、Shift 和 Ctrl+Shift 进行选择,以及用于选择/取消选择的空格键.

回答by Jay Mooney

Are check-boxes an option? or do you want the select like you get in a list box?

复选框是一个选项吗?或者你想要像你在列表框中得到的选择?

  • checkboxes are built in
  • select like you get in a list box requires a custom tree control
  • 复选框是内置的
  • 像在列表框中一样选择需要自定义树控件

There is a multi-select tree control available on CodeProject:Multi-Select Tree View

CodeProject 上有一个多选树控件:Multi-Select Tree View

回答by Jon B

You might look at a 3rd party solution. The Infragisticstree does this. Not free, but the time spent trying to find a solution isn't really free, either.

您可能会查看 3rd 方解决方案。该Infragistics的树做到这一点。不是免费的,但是花在寻找解决方案上的时间也不是免费的。

回答by timpeck

The simplest solution would be to extend the existing TreeView control shipped with the framework and override the OnBeforeSelect and OnAfterSelect methods with logic to capture multiple selections.

最简单的解决方案是扩展框架附带的现有 TreeView 控件,并使用捕获多个选择的逻辑覆盖 OnBeforeSelect 和 OnAfterSelect 方法。

An example can be found here: http://www.arstdesign.com/articles/treeviewms.html

一个例子可以在这里找到:http: //www.arstdesign.com/articles/treeviewms.html

回答by Oliver Bock

The below code will allow you to adjust the background colour you use, to ensure that all selected nodes are highlighted.

下面的代码将允许您调整您使用的背景颜色,以确保所有选定的节点都突出显示。

protected override void WndProc(ref Message m)
{
    switch (m.Msg) {
        // WM_REFLECT is added because WM_NOTIFY is normally sent just
        // to the parent window, but Windows.Form will reflect it back
        // to us, MFC-style.
        case Win32.WM_REFLECT + Win32.WM_NOTIFY: {
            Win32.NMHDR nmhdr = (Win32.NMHDR)m.GetLParam(typeof(Win32.NMHDR));
            switch((int)nmhdr.code) {
                case Win32.NM_CUSTOMDRAW:
                    base.WndProc(ref m);
                    Win32.NMTVCUSTOMDRAW nmTvDraw = (Win32.NMTVCUSTOMDRAW)m.GetLParam(typeof(Win32.NMTVCUSTOMDRAW));
                    switch (nmTvDraw.nmcd.dwDrawStage) {
                        case Win32.CDDS_ITEMPREPAINT:
                            // Find the node being painted.
                            TreeNode n = TreeNode.FromHandle(this, nmTvDraw.nmcd.lItemlParam);
                            if (allSelected.Contains(n))
                                // Override its background colour.
                                nmTvDraw.clrTextBk = ColorTranslator.ToWin32(SystemColors.Highlight);
                            m.Result = (IntPtr)Win32.CDRF_DODEFAULT;  // Continue rest of painting as normal
                            break;
                    }
                    Marshal.StructureToPtr(nmTvDraw, m.LParam, false);  // copy changes back
                    return;
            }
            break;
        }
    }
    base.WndProc(ref m);
}

// WM_NOTIFY notification message header.
[System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
public class NMHDR
{
    private IntPtr hwndFrom;
    public IntPtr idFrom;
    public uint code;
}

[StructLayout(LayoutKind.Sequential)]
public struct NMCUSTOMDRAW
{
    public NMHDR hdr;
    public int dwDrawStage;
    public IntPtr hdc;
    public RECT rc;
    public IntPtr dwItemSpec;
    public int uItemState;
    public IntPtr lItemlParam;
}

[StructLayout(LayoutKind.Sequential)]
public struct NMTVCUSTOMDRAW
{
    public NMCUSTOMDRAW nmcd;
    public int clrText;
    public int clrTextBk;
    public int iLevel;
}

public const int CDIS_SELECTED = 0x0001;
public const int CDIS_FOCUS = 0x0010;
public const int CDDS_PREPAINT = 0x00000001;
public const int CDDS_POSTPAINT = 0x00000002;
public const int CDDS_PREERASE = 0x00000003;
public const int CDDS_POSTERASE = 0x00000004;
public const int CDDS_ITEM = 0x00010000;  // item specific 
public const int CDDS_ITEMPREPAINT = (CDDS_ITEM | CDDS_PREPAINT);
public const int CDDS_ITEMPOSTPAINT = (CDDS_ITEM | CDDS_POSTPAINT);
public const int CDDS_ITEMPREERASE = (CDDS_ITEM | CDDS_PREERASE);
public const int CDDS_ITEMPOSTERASE = (CDDS_ITEM | CDDS_POSTERASE);
public const int CDDS_SUBITEM = 0x00020000;
public const int CDRF_DODEFAULT = 0x00000000;
public const int CDRF_NOTIFYITEMDRAW = 0x00000020;
public const int CDRF_NOTIFYSUBITEMDRAW = 0x00000020;  // flags are the same, we can distinguish by context

public const int WM_USER = 0x0400;
public const int WM_NOTIFY = 0x4E;
public const int WM_REFLECT = WM_USER + 0x1C00;