Pregunta

Estoy atascado en .NET 2.0 Windows Forms.

No parece que la capacidad de seleccionar múltiples nodos exista en el control estándar TreeView .

Estoy intentando hacer esto para una selección del menú contextual. Así que las casillas de verificación no son un paradigma de UI aceptable aquí.

¿Cuál es la mejor manera de proporcionar esa funcionalidad tan necesaria?

¿Fue útil?

Solución

Hicimos esto en un proyecto WTL una vez, pero el trabajo básico necesario es el mismo para .NET. Para lograr un control de árbol de selección múltiple, deberá dibujar los elementos del árbol usted mismo y anular el manejo del teclado y el mouse. También deberá mantener su propia lista de elementos seleccionados.

No olvide considerar las reglas de selección (se permiten los padres y los hijos, por ejemplo), y no se olvide de implementar los métodos abreviados del teclado, incluida la selección con Ctrl, Shift y Ctrl + Shift, así como la barra espaciadora para seleccionando / deseleccionando.

Otros consejos

¿Las casillas de verificación son una opción? ¿O quieres la selección que obtienes en un cuadro de lista?

  • las casillas de verificación están integradas
  • seleccionar como se obtiene en un cuadro de lista requiere un control de árbol personalizado

Hay un control de árbol de selección múltiple disponible en CodeProject: Vista de árbol de selección múltiple

Puede ver una solución de terceros. El árbol Infragistics hace esto. No es gratis, pero el tiempo dedicado a tratar de encontrar una solución tampoco es realmente gratis.

El código de abajo te permitirá ajustar el color de fondo que utilizas para asegurarte de que todos los nodos seleccionados estén resaltados.

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;

La solución más sencilla sería extender el control TreeView que se incluye con el marco y anular los métodos OnBeforeSelect y OnAfterSelect con lógica para capturar varias selecciones.

Puede encontrar un ejemplo aquí: http://www.arstdesign.com/articles/treeviewms .html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top