Tuesday, May 17, 2011

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

In my previous posts I have described, how to create synchronized (or linked) ListBox and ListView objects. Now I continue in investigation in this area and I have found another interesting example which describes synchronization of two RichTextBox objects. First step is to create class which derives from RichTextBox class.
public class SynchronizedScrollRichTextBox : System.Windows.Forms.RichTextBox
{
    public event vScrollEventHandler vScroll;
    public delegate void vScrollEventHandler(System.Windows.Forms.Message message);

    public const int WM_VSCROLL = 0x115;

    protected override void WndProc(ref System.Windows.Forms.Message msg)
    {
        if (msg.Msg == WM_VSCROLL)
        {
            if (vScroll != null)
            {
                vScroll(msg);
            }
        }
        base.WndProc(ref msg);
    }

    public void PubWndProc(ref System.Windows.Forms.Message msg)
    {
        base.WndProc(ref msg);
    }
}
I have created two SynchronizedScrollRichTextBox controls and placed them on the form. Then I have added following code (vScroll event):
private void synchronizedScrollRichTextBox1_vScroll(Message message)
{
    message.HWnd = synchronizedScrollRichTextBox2.Handle;
    synchronizedScrollRichTextBox2.PubWndProc(ref message);
}
Here you can find original post.

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

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

Before couple of days I had a task where I must synchronize two ListBox objects. I have fount out, that ListBox object doesn't have any property which allow to accomplish this. After a few hours of googling I found code of class that inherited from ListBox class. Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace SyncLists
{
    class SyncListBox : System.Windows.Forms.ListBox
    {
        [Category("Action")]

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

        public event ScrollEventHandler OnHorizontalScroll;
        public event ScrollEventHandler OnVerticalScroll;


        private const int SB_LINEUP = 0;
        private const int SB_LINELEFT = 0;
        private const int SB_LINEDOWN = 1;
        private const int SB_LINERIGHT = 1;
        private const int SB_PAGEUP = 2;
        private const int SB_PAGELEFT = 2;
        private const int SB_PAGEDOWN = 3;
        private const int SB_PAGERIGHT = 3;
        private const int SB_THUMBPOSITION = 4;
        private const int SB_THUMBTRACK = 5;
        private const int SB_PAGETOP = 6;
        private const int SB_LEFT = 6;
        private const int SB_PAGEBOTTOM = 7;
        private const int SB_RIGHT = 7;
        private const int SB_ENDSCROLL = 8;

        private const int SIF_TRACKPOS = 0x10;
        private const int SIF_RANGE = 0x1;
        private const int SIF_POS = 0x4;
        private const int SIF_PAGE = 0x2;
        private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int GetScrollInfo(
        IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo);


        private struct ScrollInfoStruct
        {
            public int cbSize;
            public int fMask;
            public int nMin;
            public int nMax;
            public int nPage;
            public int nPos;
            public int nTrackPos;
        }

        protected override void WndProc(ref System.Windows.Forms.Message msg)
        {
            if (msg.Msg == WM_HSCROLL)
            {
                if (OnHorizontalScroll != null)
                {
                    ScrollInfoStruct si = new ScrollInfoStruct();
                    si.fMask = SIF_ALL;
                    si.cbSize = Marshal.SizeOf(si);
                    GetScrollInfo(msg.HWnd, 0, ref si);

                    if (msg.WParam.ToInt32() == SB_ENDSCROLL)
                    {
                        ScrollEventArgs sargs = new ScrollEventArgs(
                        ScrollEventType.EndScroll,
                        si.nPos);
                        OnHorizontalScroll(this, sargs);
                    }
                }
            }
            if (msg.Msg == WM_VSCROLL)
            {
                if (OnVerticalScroll != null)
                {
                    ScrollInfoStruct si = new ScrollInfoStruct();
                    si.fMask = SIF_ALL;
                    si.cbSize = Marshal.SizeOf(si);
                    GetScrollInfo(msg.HWnd, 0, ref si);

                    if (msg.WParam.ToInt32() == SB_ENDSCROLL)
                    {
                        ScrollEventArgs sargs = new ScrollEventArgs(
                        ScrollEventType.EndScroll,
                        si.nPos);
                        OnVerticalScroll(this, sargs);
                    }
                }
            }
            base.WndProc(ref msg);
        }


        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // scrolled
            // 
            this.Size = new System.Drawing.Size(120, 95);
            this.ResumeLayout(false);
        }
    }
}

add two SyncListBox objects on the form and add following code:

private void syncListView2_OnVerticalScroll(object sender, ScrollEventArgs e)
{
    syncListView1.TopIndex = syncListView2.TopIndex;
}

private void syncListView1_OnVerticalScroll(object sender, ScrollEventArgs e)
{
    syncListView2.TopIndex = syncListView1.TopIndex;
}