Разрешить множественный выбор в .NET TreeView - PullRequest
20 голосов
/ 15 октября 2008

Я застрял в .NET 2.0 Windows Forms.

Не похоже, что в стандартном элементе управления TreeView существует возможность выбора нескольких узлов.

Я пытаюсь сделать это для выбора контекстного меню. Так что флажки здесь не являются приемлемой парадигмой пользовательского интерфейса.

Каков наилучший способ обеспечить столь необходимую функциональность?

Ответы [ 6 ]

10 голосов
/ 15 октября 2008

Мы делали это в WTL-проекте один раз, но основная необходимая работа такая же для .NET. Чтобы получить элемент управления множественным выбором дерева, вам нужно будет самостоятельно нарисовать элементы дерева и переопределить обработку клавиатуры и мыши. Вам также нужно будет вести собственный список выбранных предметов.

Не забудьте рассмотреть правила выбора (допустимы ли родители и дети, например), и не забудьте реализовать сочетания клавиш, включая выделение, используя Ctrl, Shift и Ctrl + Shift, а также пробел для включение / выключение.

6 голосов
/ 15 октября 2008
4 голосов
/ 16 октября 2008

Вы можете посмотреть на стороннее решение. Дерево Infragistics делает это. Не бесплатно, но время, потраченное на поиск решения, тоже не является бесплатным.

4 голосов
/ 15 октября 2008

Являются ли флажки опцией? или вы хотите выбрать, как вы получаете в окне списка?

  • флажки встроены
  • выберите, как вы получаете в списке, требуется пользовательский элемент управления дерева

В CodeProject доступен элемент управления множественным выбором дерева: Представление дерева множественного выбора

3 голосов
/ 26 марта 2012

Приведенный ниже код позволит вам настроить цвет фона, который вы используете, чтобы обеспечить выделение всех выбранных узлов.

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;
2 голосов
/ 03 февраля 2011

Самое простое решение - расширить существующий элемент управления TreeView, поставляемый с платформой, и переопределить методы OnBeforeSelect и OnAfterSelect с помощью логики для захвата нескольких выборок.

Пример можно найти здесь: http://www.arstdesign.com/articles/treeviewms.html

...