Pergunta

Eu estou trabalhando em um controle para unir a visão de um ListView para outro modo que quando o ListView mestre é rolada, a visão ListView criança é atualizado para corresponder.

Até agora tenho sido capaz de obter a criança ListViews para atualizar seu ponto de vista quando os botões da barra de rolagem mestre são clicados. O problema é que quando clicando e arrastando o próprio ScrollBar, os ListViews criança não são atualizados. Eu olhei para as mensagens que estão sendo enviadas usando Spy ++ e as mensagens corretas estão sendo enviados.

Aqui está o meu código atual:

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

Com base este postar nos fóruns MS tecnologia Eu tentei capturar e processar o 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);
    }

Isto irá actualizar a localização da criança ListView ScrollBar mas não altera a visão real na criança.

Então, minhas perguntas são:

  1. É possível atualizar as crianças ListViews quando o mestre ListView ScrollBar é arrastado?
  2. Se sim, como?
Foi útil?

Solução

Eu queria fazer a mesma coisa, e depois de procurar em torno eu encontrei o seu código aqui, o que ajudou, mas é claro que não resolveu o problema. Mas depois de brincar com ele, eu encontrei uma solução.

A chave veio quando eu percebi que desde que os botões de deslocamento de trabalho, que você pode usar isso para fazer o trabalho slider. Em outras palavras, quando o evento SB_THUMBTRACK vem, eu emito repetido eventos SB_LINELEFT e SB_LINERIGHT até que meu ListView criança fica perto de onde o mestre é. Sim, este não é perfeito, mas funciona perto o suficiente.

No meu caso, meu mestre ListView é chamado de "ReportView", enquanto meu ListView criança é chamado de "summaryView". Aqui está o meu 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);
    }
}

E, em seguida, o manipulador de eventos em si:

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

Desculpe o estranho formatação do while lá, mas é assim que eu prefiro as coisas de código como esse.

O próximo problema foi se livrar das barras de rolagem na ListView criança. Eu percebi que você tinha um método chamado HideScrollBar. Isso realmente não funciona para mim. Eu encontrei uma solução melhor no meu caso estava deixando a barra de rolagem lá, mas "cobrindo"-lo em seu lugar. Eu faço isso com o cabeçalho da coluna também. Eu só deslizar meu controle criança sob o controle mestre para cobrir o cabeçalho da coluna. E então eu esticar a criança a cair do painel que contém. E, em seguida, para fornecer um pouco de uma fronteira ao longo da borda do meu painel contendo, eu jogo em um controle para cobrir a borda inferior visível do meu ListView criança. Ele acaba procurando bastante agradável.

Eu também adicionei um manipulador de eventos para sincronizar mudanças larguras de coluna, como em:

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

Enquanto tudo isso parece um pouco de um truque, ele funciona para mim.

Outras dicas

Esta é conjectura apenas para obter os sucos mentais que fluem para levá-lo como quiser: No manipulador de rolagem para a lista principal, você pode chamar o manipulador de rolagem para a lista de filhos (passando o remetente e EventArgs do mestre)?

Adicione isto a sua carga Forma:

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

que faz referência a seguinte:

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
}

Gostaria de criar minha própria classe, herdando de ListView para expor os eventos vertical e horizontal de rolagem.

Então eu faria criar manipuladores de rolagem na minha forma de sincronizar os dois controles

Este é o código da amostra que deverá permitir um listview para publicar eventos de rolagem:

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

Agora lidar com os eventos de rolagem em seu formulário:

Configurar um método PInvoke para ser capaz de enviar uma mensagem janelas para um controle:

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

Configurar seus manipuladores de eventos (lstMaster e lstChild são duas caixas de listagem):

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

Uma solução ingênua para o seu problema pode ser lidar com a mensagem de pintura na visualização da lista pai e verificar se as exibições de lista ligados estão exibindo os dados corretos. Se não o fizerem, então atualizá-los para exibir os dados corretos, chamando o método EnsureVisible.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top