Pregunta

Estoy trabajando en un control para vincular la vista de un ListView a otro, de modo que cuando se desplaza el ListView maestro, la vista ListView secundaria se actualiza para que coincida.

Hasta ahora he podido hacer que las ListViews secundarias actualicen su vista cuando se hace clic en los botones maestros de la barra de desplazamiento. El problema es que al hacer clic y arrastrar la barra de desplazamiento, las ListViews secundarias no se actualizan. He visto los mensajes que se envían usando Spy ++ y se están enviando los mensajes correctos.

Aquí está mi código actual:

public partial class LinkedListViewControl : ListView
{
    [DllImport("User32.dll")]
    private static extern bool SendMessage(IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

    [DllImport("User32.dll")]
    private static extern bool ShowScrollBar(IntPtr hwnd, int wBar, bool bShow);

    [DllImport("user32.dll")]
    private static extern int SetScrollPos(IntPtr hWnd, int wBar, int nPos, bool bRedraw);

    private const int WM_HSCROLL = 0x114;

    private const int SB_HORZ = 0;
    private const int SB_VERT = 1;
    private const int SB_CTL = 2;
    private const int SB_BOTH = 3;
    private const int SB_THUMBPOSITION = 4;
    private const int SB_THUMBTRACK = 5;
    private const int SB_ENDSCROLL = 8;

    public LinkedListViewControl()
    {
        InitializeComponent();
    }

    private readonly List<ListView> _linkedListViews = new List<ListView>();

    public void AddLinkedView(ListView listView)
    {
        if (!_linkedListViews.Contains(listView))
        {
            _linkedListViews.Add(listView);

            HideScrollBar(listView);
        }
    }

    public bool RemoveLinkedView(ListView listView)
    {
        return _linkedListViews.Remove(listView);
    }

    private void HideScrollBar(ListView listView)
    {
        //Make sure the list view is scrollable
        listView.Scrollable = true;

        //Then hide the scroll bar
        ShowScrollBar(listView.Handle, SB_BOTH, false);
    }

    protected override void WndProc(ref Message msg)
    {
        if (_linkedListViews.Count > 0)
        {
            //Look for WM_HSCROLL messages
            if (msg.Msg == WM_HSCROLL)
            {
                foreach (ListView view in _linkedListViews)
                {
                    SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero);
                }
            }
        }
    }
}

Basado en esta publicación en los foros de MS Tech Traté de capturar y procesar el evento SB_THUMBTRACK:

    protected override void WndProc(ref Message msg)
    {
        if (_linkedListViews.Count > 0)
        {
            //Look for WM_HSCROLL messages
            if (msg.Msg == WM_HSCROLL)
            {
                Int16 hi = (Int16)((int)msg.WParam >> 16);
                Int16 lo = (Int16)msg.WParam;

                foreach (ListView view in _linkedListViews)
                {
                    if (lo == SB_THUMBTRACK)
                    {
                        SetScrollPos(view.Handle, SB_HORZ, hi, true);

                        int wParam = 4 + 0x10000 * hi;
                        SendMessage(view.Handle, WM_HSCROLL, (IntPtr)(wParam), IntPtr.Zero);
                    }
                    else
                    {
                        SendMessage(view.Handle, WM_HSCROLL, msg.WParam, IntPtr.Zero);
                    }
                }
            }
        }

        // Pass message to default handler.
        base.WndProc(ref msg);
    }

Esto actualizará la ubicación del elemento secundario ListView ScrollBar pero no cambia la vista real en el elemento secundario.

Entonces mis preguntas son:

  1. ¿Es posible actualizar ListViews secundarias cuando se arrastra la barra de desplazamiento ListView maestra?
  2. Si es así, ¿cómo?
¿Fue útil?

Solución

Quería hacer lo mismo, y después de buscar, encontré su código aquí, lo que ayudó, pero por supuesto no resolvió el problema. Pero después de jugar con él, he encontrado una solución.

La clave vino cuando me di cuenta de que, dado que los botones de desplazamiento funcionan, puedes usar eso para hacer que el control deslizante funcione. En otras palabras, cuando entra el evento SB_THUMBTRACK, emito eventos repetidos SB_LINELEFT y SB_LINERIGHT hasta que mi hijo ListView se acerca al lugar donde está el maestro. Sí, esto no es perfecto, pero funciona lo suficientemente cerca.

En mi caso, mi ListView maestro se llama " reportView " ;, mientras que mi ListView hijo se llama " summaryView " ;. Aquí está mi código pertinente:

public class MyListView : ListView
{
    public event ScrollEventHandler HScrollEvent;

    protected override void WndProc(ref System.Windows.Forms.Message msg) 
    {
        if (msg.Msg==WM_HSCROLL && HScrollEvent != null)
            HScrollEvent(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, (int)msg.WParam));

        base.WndProc(ref msg);
    }
}

Y luego el controlador de eventos en sí:

reportView.HScrollEvent += new ScrollEventHandler((sender,e) => {
    if ((ushort) e.NewValue != SB_THUMBTRACK)
        SendMessage(summaryView.Handle, WM_HSCROLL, (IntPtr) e.NewValue, IntPtr.Zero);
    else {
        int newPos = e.NewValue >> 16;
        int oldPos = GetScrollPos(reportView .Handle, SB_HORZ);                 
        int pos    = GetScrollPos(summaryView.Handle, SB_HORZ);
        int lst;

        if (pos != newPos)
            if      (pos<newPos && oldPos<newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINERIGHT,IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) < newPos && pos!=lst);
            else if (pos>newPos && oldPos>newPos) do { lst=pos; SendMessage(summaryView.Handle,WM_HSCROLL,(IntPtr)SB_LINELEFT, IntPtr.Zero); } while ((pos=GetScrollPos(summaryView.Handle,SB_HORZ)) > newPos && pos!=lst);
        }
    });

Perdón por el formato extraño de los bucles while allí, pero así es como prefiero codificar cosas así.

El siguiente problema fue deshacerse de las barras de desplazamiento en el ListView secundario. Noté que tenías un método llamado HideScrollBar. Esto realmente no funcionó para mí. Encontré una mejor solución en mi caso: dejar la barra de desplazamiento allí, pero "cubrir". en su lugar. Hago esto también con el encabezado de columna. Simplemente deslizo mi control secundario hacia arriba debajo del control maestro para cubrir el encabezado de la columna. Y luego estiro al niño para que se caiga del panel que lo contiene. Y luego, para proporcionar un poco de borde a lo largo del borde de mi panel de contención, agrego un control para cubrir el borde inferior visible de mi ListView secundario. Termina luciendo bastante agradable.

También agregué un controlador de eventos para sincronizar los anchos de columna cambiantes, como en:

reportView.ColumnWidthChanging += new ColumnWidthChangingEventHandler((sender,e) => {
    summaryView.Columns[e.ColumnIndex].Width = e.NewWidth;
    });         

Si bien todo esto parece un poco un error, funciona para mí.

Otros consejos

Esta es una conjetura solo para hacer fluir los jugos mentales, así que tómalo como quieras: En el controlador de desplazamiento para la lista maestra, ¿puede llamar al controlador de desplazamiento para la lista secundaria (pasando el remitente y los eventos del maestro)?

Agregue esto a su carga de Formulario:

masterList.Scroll += new ScrollEventHandler(this.masterList_scroll);

Que hace referencia a esto:

private void masterList_scroll(Object sender, System.ScrollEventArgs e)
{
    childList_scroll(sender, e);
}

private void childList_scroll(Object sender, System.ScrollEventArgs e)
{
   childList.value = e.NewValue
}

Crearía mi propia clase, heredando de ListView para exponer los eventos de desplazamiento vertical y horizontal.

Entonces crearía controladores de desplazamiento en mi formulario para sincronizar los dos controles

Este es un código de muestra que debería permitir una vista de lista para publicar eventos de desplazamiento:

public class MyListView : System.Windows.Forms.ListView
{
    const int WM_HSCROLL = 0x0114;
    const int WM_VSCROLL = 0x0115;

    private ScrollEventHandler evtHScroll_m;
    private ScrollEventHandler evtVScroll_m;

    public event ScrollEventHandler OnHScroll
    {
        add
        {
            evtHScroll_m += value;
        }
        remove
        {
            evtHScroll_m -= value;
        }
    }

    public event ScrollEventHandler OnHVcroll
    {
        add
        {
            evtVScroll_m += value;
        }
        remove
        {
            evtVScroll_m -= value;
        }
    }

    protected override void WndProc(ref System.Windows.Forms.Message msg) 
    { 
        if (msg.Msg == WM_HSCROLL && evtHScroll_m != null) 
            {
            evtHScroll_m(this,new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32()));
            }

        if (msg.Msg == WM_VSCROLL && evtVScroll_m != null)  
        {
            evtVScroll_m(this, new ScrollEventArgs(ScrollEventType.ThumbTrack, msg.WParam.ToInt32()));
        }
        base.WndProc(ref msg); 
    }

Ahora maneja los eventos de desplazamiento en tu formulario:

Configure un método PInvoke para poder enviar un mensaje de Windows a un control:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int SendMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int iMsg, int iWParam, int iLParam);

Configure sus controladores de eventos (lstMaster y lstChild son dos cuadros de lista):

lstMaster.OnVScroll += new ScrollEventHandler(this.lstMaster_OnVScroll);
lstMaster.OnHScroll += new ScrollEventHandler(this.lstMaster_OnHScroll);

const int WM_HSCROLL = 0x0114;      
const int WM_VSCROLL = 0x0115;  

private void lstMaster_OnVScroll(Object sender, System.ScrollEventArgs e)
{    
    SendMessage(lstChild.Handle,WM_VSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); 
}

private void  lstMaster_OnHScroll(Object sender, System.ScrollEventArgs e)
{   
    SendMessage(lstChild.Handle,WM_HSCROLL,(IntPtr)e.NewValue, IntPtr.Zero); 
}

Una solución ingenua a su problema puede ser manejar el mensaje de pintura en la vista de la lista principal y verificar si las vistas de la lista vinculada muestran los datos correctos. Si no lo hacen, actualícelos para mostrar los datos correctos llamando al método GuaranteeVisible.

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