Frage

Ich arbeite an einer Kontrolle, um die Ansicht von einem Listview zu einem anderen zusammenbinden, so dass, wenn der Master Listview gescrollt wird, wird das Kind Listview-Ansicht anzupassen aktualisiert.

Bisher habe ich in der Lage, das Kind Listviews, um ihre Ansicht zu aktualisieren, wenn die Master-Scrollbar Buttons angeklickt werden. Das Problem ist, dass beim Klicken und Ziehen mit der ScrollBar selbst wird das Kind Listviews nicht aktualisiert. Ich habe in den Nachrichten sieht Spy gesendet wird mit ++ und die richtigen Nachrichten gesendet zu werden.

Hier ist mein aktueller Code:

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

Basierend auf diesen Beitrag auf den MS Tech Foren ich habe versucht, zu erfassen und das SB_THUMBTRACK Ereignis zu verarbeiten:

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

Damit wird die Lage des Kindes Listview ScrollBar aktualisieren, aber in dem Kind nicht die tatsächliche Ansicht ändern.

Also meine Fragen sind:

  1. Ist es möglich, das Kind Listviews zu aktualisieren, wenn die Master-Listview ScrollBar gezogen wird?
  2. Wenn ja, wie?
War es hilfreich?

Lösung

Ich wollte das Gleiche tun, und nach der Suche um, fand ich hier Ihren Code eingeben, die geholfen haben, aber natürlich nicht das Problem lösen. Aber nachdem er mit diesem Herumspielen, ich habe eine Lösung gefunden.

Der Schlüssel kam, als ich erkennen, dass da die Scroll-Tasten arbeiten, dass Sie verwenden können, um den Schieber Arbeit zu machen. Mit anderen Worten, wenn das SB_THUMBTRACK Ereignis kommt, ich Ausgabe SB_LINELEFT und SB_LINERIGHT Ereignisse wiederholt, bis mein Kind Listview zu nahe kommt, wo der Meister ist. Ja, das ist nicht perfekt, aber es funktioniert nahe genug.

In meinem Fall mein Meister Listview wird „Report“ genannt, während mein Kind Listview „summaryView“ genannt wird. Hier ist mein einschlägiger Code:

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

Und dann der Event-Handler selbst:

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

Sorry über die ungerade Formatierung der While-Schleifen gibt, aber das ist, wie ich es vorziehen, Dinge zu codieren, wie das.

Das nächste Problem ist immer die Bildlaufleisten in dem Kind Listview befreien. Ich bemerkte eine Methode hatte genannt HideScrollBar. Das ist nicht wirklich für mich arbeiten. Ich fand eine bessere Lösung in meinem Fall war es die Bildlaufleiste zu verlassen, aber „bedeckt“ es stattdessen auf. Ich tue dies mit dem Spaltenkopf als auch. Ich rutsche nur Kontrolle meines Kindes unter der Master-Steuerung bis zu dem Spaltenkopf abzudecken. Und dann strecke ich das Kind aus der Tafel zu fallen, die er enthält. Und dann ein wenig eine Grenze entlang der Kante meines enthält Tafel zu schaffen, werfe ich in einer Kontrolle der sichtbaren unteren Rand meines Kindes Listview zu decken. Es endet sah ziemlich nett.

Ich habe auch einen Event-Handler zu ändern Spaltenbreite zu synchronisieren, wie in:

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

Während dies alles ein bisschen eine Flickschusterei scheint, es funktioniert für mich.

Andere Tipps

Dies ist Vermutung nur die geistigen Säfte fließen es so nehmen, wie man will: In der Scroll-Handler für die Master-Liste können Sie den Scroll-Handler für die Child-Liste aufrufen (vorbei an den Absender und EventArgs vom Master)?

Fügen Sie diese auf Ihrem Formular Last:

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

Welche verweist diese:

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
}

Ich würde meine eigene Klasse erstellen, von Listview erben die vertikale und horizontale Scroll-Ereignisse zu belichten.

Dann würde ich tun Scroll-Handler in meinem Formular erstellen, die beiden Kontrollen zu synchronisieren

Dies ist ein Beispielcode, der eine Listenansicht ermöglichen sollte, blättern Ereignisse zu veröffentlichen:

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

Sie nun die Scroll-Ereignisse in Ihrer Form behandeln:

Stellen Sie eine PInvoke Methode bis zu der Lage sein, eine Windows-Nachricht an eine Steuerung zu senden:

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

Richten Sie Ihre Event-Handler (lstMaster und lstChild sind zwei Listboxen):

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

Eine naive Lösung für Ihr Problem kann die Farbe Nachricht in der übergeordneten Listenansicht wird die Handhabung und die Überprüfung, ob die verknüpften Listenansichten, die richtigen Daten angezeigt werden. Wenn sie dies nicht tun, dann aktualisieren, um sie durch den Aufruf der Methode EnsureVisible die richtigen Daten angezeigt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top