Разрешить множественный выбор в .NET TreeView

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

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

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

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


person Darcy Casselman    schedule 15.10.2008    source источник


Ответы (6)


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

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

person Jeff Yates    schedule 15.10.2008
comment
Вы можете самостоятельно рисовать, перехватывая WM_NOTIFY и настраивая цвет фона для выбранных узлов. Я добавил ответ, поэтому могу включить код. - person Oliver Bock; 26.03.2012
comment
@OliverBock: Это, безусловно, самый простой способ, если все, что вам нужно, - это изменить цвет фона. - person Jeff Yates; 26.03.2012

TreeView.CheckBoxes

person Community    schedule 15.10.2008
comment
Это не совсем то, что я ищу. Любое другое древовидное представление в Windows позволяет вам, удерживая Ctrl, выбрать несколько записей. И убирать контекстное меню с флажками не имеет никакого смысла. - person Darcy Casselman; 15.10.2008
comment
Я не могу найти пример представления дерева с нажатой клавишей Ctrl где-либо в стандартной среде ОС Windows. Вы можете привести пример? Я согласен с тем, что контекстное меню из проверок не очень разумно, но я не думаю, что ожидать, что пользователи будут нажимать Ctrl + щелчок, тоже. (По моему опыту, у многих пользователей есть проблемы с этим.) - person John Rudy; 15.10.2008
comment
В стандартном древовидном представлении Windows Forms именно так вы выбираете более одного узла в дереве. - person ; 15.10.2008
comment
Хорошо, стандарт может быть неправильным словом. Возьмем, к примеру, обозреватель решений в Visual Studio. - person Darcy Casselman; 15.10.2008
comment
Проблема в том, что стандартные элементы управления, с которыми вы можете играть и использовать бесплатно (те, которые поставляются с VS), не обязательно те, которые вы видите, фактически используемые в Windows. У вас есть три варианта: 1) флажки 2) написать свой 3) купить один. Отстой, я знаю. В WPF это легко сделать! - person ; 15.10.2008

Можно ли использовать флажки? или вы хотите, чтобы выбор был таким же, как в поле со списком?

  • checkboxes are built in
  • select like you get in a list box requires a custom tree control

В CodeProject доступно дерево с множественным выбором: Дерево с множественным выбором

person Jay Mooney    schedule 15.10.2008

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

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

person timpeck    schedule 03.02.2011

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

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;
person Oliver Bock    schedule 25.03.2012

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

person Jon B    schedule 15.10.2008
comment
-1: мне потребовалось гораздо меньше 5 минут, чтобы найти простой бесплатный и реализовать его в моем решении. Вот два примера: codeproject.com/KB/tree/MWTreeViewv2010.aspx , arstdesign.com/articles/treeviewms.html. Не идеально, но работает и может быть улучшено, если вы знаете, как это обойтись. - person synhershko; 03.02.2010
comment
@synhershko: Статьи датированы 2004 и 2002 годами соответственно, я уверен, что есть более эффективные способы реализовать то же самое сегодня. - person Neolisk; 19.09.2013
comment
Несмотря на то, что она устарела (2004 г.), первая реализация надежна и хорошо закодирована. Он умещается в одном классе, наследующем TreeView, и его очень просто адаптировать. - person Larry; 15.10.2013