Monday, May 16, 2011

Synchronized ListView objects (or how to scroll 2 ListView objects together)

In my previous post I have described how to synchronize two ListBox objects. Here I will show, how to synchronize two ListView objects.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace SyncLists
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.listView1.View = View.Details;
            this.listView2.View = View.Details;
            for (int j = 0; j < 10; j++)
            {
                this.listView1.Columns.Add("c" + j.ToString());
                this.listView2.Columns.Add("c" + j.ToString());
            }
            for (int j = 0; j < 300; j++)
            {
                ListViewItem item = new ListViewItem("item" + j.ToString());
                for (int k = 1; k < 10; k++)
                {
                    item.SubItems.Add("sub item" + k.ToString());
                }
                this.listView1.Items.Add(item);
                this.listView2.Items.Add(item.Clone() as ListViewItem);
            }

            this.listView1.Scroll += new ListViewEx.ScrollHandler(listView1_Scroll);
        }

        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll")]
        static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;
        private const int SBS_HORZ = 0;
        private const int SBS_VERT = 1;

        void listView1_Scroll(object sender, MyScrollEventArgs e)
        {
            ListViewEx listVw = sender as ListViewEx;

            Int16 hi = (Int16)((int)e.WParam >> 16);//position
            Int16 lo = (Int16)e.WParam;//scroll type

            if (e.Orientation == ScrollOrientation.VerticalScroll)
            {
                if (lo == 5) //SB_THUMBTRACK
                {
                    if (SetScrollPos(this.listView2.Handle, SBS_VERT, hi, true) != 0)
                    {
                        SendMessage(this.listView2.Handle, WM_VSCROLL,
                          (IntPtr)(4 + 0x10000 * hi), IntPtr.Zero);
                    }
                }
                else
                {
                    SendMessage(this.listView2.Handle, WM_VSCROLL, e.WParam, IntPtr.Zero);
                }
            }
            if (e.Orientation == ScrollOrientation.HorizontalScroll)
            {
                if (lo == 5)//SB_THUMBTRACK
                {
                    SetScrollPos(this.listView2.Handle, SBS_HORZ, hi, true);
                    SendMessage(this.listView2.Handle, WM_HSCROLL,
                       (IntPtr)(4 + 0x10000 * hi), IntPtr.Zero);
                }
                else
                {
                    SendMessage(this.listView2.Handle, WM_HSCROLL, e.WParam, IntPtr.Zero);
                }
            }
        }

    }

    public class ListViewEx : ListView
    {
        public delegate void ScrollHandler(object sender, MyScrollEventArgs e);
        public event ScrollHandler Scroll;
        public void OnScroll(MyScrollEventArgs e)
        {
            if (Scroll != null)
            {
                Scroll(this, e);
            }
        }

        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_VSCROLL)
            {
                MyScrollEventArgs e = new MyScrollEventArgs();
                e.WParam = m.WParam;
                e.Orientation = ScrollOrientation.VerticalScroll;
                OnScroll(e);
            }
            if (m.Msg == WM_HSCROLL)
            {
                MyScrollEventArgs e = new MyScrollEventArgs();
                e.WParam = m.WParam;
                e.Orientation = ScrollOrientation.HorizontalScroll;
                OnScroll(e);
            }
            base.WndProc(ref m);
        }
    }

    public class MyScrollEventArgs : EventArgs
    {
        private ScrollOrientation orientation;
        private IntPtr wParam;

        public IntPtr WParam
        {
            get { return wParam; }
            set { wParam = value; }
        }

        public ScrollOrientation Orientation
        {
            get { return orientation; }
            set { orientation = value; }
        }
    }
}

4 comments:

  1. I've tried your code and i got a problem, when i scrolled listView1's Scrollbar, listView2's ScrollBar was also scrolled too but the item in listView2 didn't scroll (only item in listView1 was scrolled). Can you help me?

    ReplyDelete
  2. Hi I have tried your code and getting error on line

    "this.listView1.Scroll += new ListViewEx.ScrollHandler(listView1_Scroll);"

    pls help

    ReplyDelete
  3. Same as Thang Tran but on top of it
    thumb on the listView2 jumps back as soon as the first one is released.
    Another problem is when the second handler (listView2_Scroll) is added -
    both fall into infinite loop and the app crashes.

    ReplyDelete