Domanda

Sto lavorando a un controllo per collegare la vista da una ListView a un'altra in modo tale che quando si scorre lo ListView principale, la vista ListView figlio viene aggiornata in modo che corrisponda.

Finora sono stato in grado di far aggiornare ListView al figlio quando fanno clic sui pulsanti della barra di scorrimento principale. Il problema è che quando si fa clic e si trascina la barra di scorrimento stessa, le ListView viste secondarie non vengono aggiornate. Ho esaminato i messaggi inviati tramite Spy ++ e vengono inviati i messaggi corretti.

Ecco il mio codice attuale:

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);
                }
            }
        }
    }
}

Basato su questo post sui forum tecnici di MS Ho provato a catturare ed elaborare l'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);
    }

Ciò aggiornerà la posizione della barra di scorrimento ListView figlio ma non modifica la vista effettiva nel figlio.

Quindi le mie domande sono:

  1. È possibile aggiornare ListView viste secondarie quando si trascina la barra di scorrimento ListView principale?
  2. Se sì, come?
È stato utile?

Soluzione

Volevo fare la stessa cosa, e dopo aver cercato in giro ho trovato il tuo codice qui, il che mi ha aiutato, ma ovviamente non ha risolto il problema. Ma dopo averci provato, ho trovato una soluzione.

La chiave è arrivata quando mi sono reso conto che, poiché i pulsanti di scorrimento funzionano, puoi usarlo per far funzionare il dispositivo di scorrimento. In altre parole, quando arriva l'evento SB_THUMBTRACK, invio eventi SB_LINELEFT e SB_LINERIGHT ripetuti fino a quando mio figlio ListView non si avvicina a dove si trova il master. Sì, questo non è perfetto, ma funziona abbastanza vicino.

Nel mio caso, il mio ListView principale è chiamato " reportView " ;, mentre mio figlio ListView è chiamato " SummaryView " ;. Ecco il mio codice 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);
    }
}

E quindi il gestore dell'evento stesso:

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);
        }
    });

Mi dispiace per la strana formattazione dei cicli while presenti, ma è così che preferisco codificare cose del genere.

Il problema successivo era eliminare le barre di scorrimento in ListView secondario. Ho notato che avevi un metodo chiamato HideScrollBar. Questo non ha funzionato davvero per me. Ho trovato una soluzione migliore nel mio caso, lasciando lì la barra di scorrimento, ma "copertura". invece. Lo faccio anche con l'intestazione di colonna. Scorro il controllo figlio sotto il controllo principale per coprire l'intestazione della colonna. E poi allungo il bambino per cadere dal pannello che lo contiene. E quindi per fornire un po 'di bordo lungo il bordo del mio pannello contenitore, lancio un controllo per coprire il bordo inferiore visibile di mio figlio ListView. Sembra piuttosto carino.

Ho anche aggiunto un gestore eventi per sincronizzare la modifica delle larghezze delle colonne, come in:

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

Anche se tutto questo sembra un po 'complicato, funziona per me.

Altri suggerimenti

Questa è congettura solo per far fluire i succhi mentali, quindi prendilo come vuoi: Nel gestore di scorrimento per l'elenco principale, puoi chiamare il gestore di scorrimento per l'elenco figlio (passando il mittente e gli eventi dal master)?

Aggiungi questo al tuo caricamento del modulo:

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

Che fa riferimento a questo:

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
}

Vorrei creare la mia classe, ereditando da ListView per esporre gli eventi di scorrimento verticale e orizzontale.

Quindi vorrei creare gestori di scorrimento nel mio modulo per sincronizzare i due controlli

Questo è un codice di esempio che dovrebbe consentire a una visualizzazione elenco di pubblicare eventi di scorrimento:

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); 
    }

Ora gestisci gli eventi di scorrimento nel tuo modulo:

Imposta un metodo PInvoke per poter inviare un messaggio Windows a un controllo:

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

Imposta i gestori di eventi (lstMaster e lstChild sono due caselle di riepilogo):

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 soluzione ingenua al problema può essere la gestione del messaggio di disegno nella vista dell'elenco padre e la verifica se le viste dell'elenco collegato visualizzano i dati corretti. In caso contrario, aggiornali per visualizzare i dati corretti chiamando il metodo SecureVisible.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top