简体   繁体   English

允许在.NET TreeView中进行多选

[英]Allow multi-select in a .NET TreeView

I'm stuck in .NET 2.0 Windows Forms. 我陷入了.NET 2.0 Windows Forms。

It doesn't look like the ability to select multiple nodes exists in the standard TreeView control. 它看起来不像标准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? 提供非常必要的功能的最佳方法是什么?

We did this in a WTL project once, but the basic work needed is the same for .NET. 我们曾在WTL项目中做过一次,但.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. 您还需要维护自己选择的项目列表。

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选择,以及用于选择/取消选择的空格键。

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上有一个多选树控件: 多选树视图

You might look at a 3rd party solution. 您可以查看第三方解决方案。 The Infragistics tree does this. Infragistics树就是这样做的。 Not free, but the time spent trying to find a solution isn't really free, either. 不是免费的,但尝试寻找解决方案的时间也不是免费的。

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;

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM