Click here to Skip to main content
15,896,453 members
Articles / Desktop Programming / Win32

Another Flexible ListView Control

Rate me:
Please Sign up or sign in to vote.
4.59/5 (12 votes)
17 Jul 2010CPOL5 min read 37.8K   2.3K   47  
A highly object-oriented ListView control with varying-height items and support of complex data types
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Forms.VisualStyles;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;

namespace Guide.Controls
{
	/// <summary>
	/// An enhanced ListBox which correctly handles and provides better control over horizontal scrolling.
	/// </summary>
	public class ProperListBox : ListBox
	{
		private int horzScrollBarValue = 0;

		public ProperListBox()
		{
			this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
			// this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			Region iRegion = new Region(e.ClipRectangle);
			e.Graphics.FillRegion(new SolidBrush(this.BackColor), iRegion);

			for (int i = 0; i < this.Items.Count; ++i)
			{
				System.Drawing.Rectangle irect = this.GetItemRectangle(i);
				if (e.ClipRectangle.IntersectsWith(irect))
				{
					if ((this.SelectionMode == SelectionMode.One && this.SelectedIndex == i)
					|| (this.SelectionMode == SelectionMode.MultiSimple && this.SelectedIndices.Contains(i))
					|| (this.SelectionMode == SelectionMode.MultiExtended && this.SelectedIndices.Contains(i)))
					{
						OnDrawItem(new DrawItemEventArgs(e.Graphics, this.Font,
							irect, i,
							DrawItemState.Selected, this.ForeColor,
							this.BackColor));
					}
					else
					{
						OnDrawItem(new DrawItemEventArgs(e.Graphics, this.Font,
							irect, i,
							DrawItemState.Default, this.ForeColor,
							this.BackColor));
					}

					iRegion.Complement(irect);
				}
			}

			base.OnPaint(e);
		}

		protected override void WndProc(ref Message m)
		{
			base.WndProc(ref m);

			if (this.HorizontalScrollbar && m.Msg == PInvokeHelp.WM_HSCROLL || m.Msg == PInvokeHelp.WM_MOUSEWHEEL)
			{
				//					Debug.WriteLine("WParam = " + m.WParam.ToInt32().ToString() + " LParam = " + m.LParam.ToInt32().ToString());

				// Update Scroll position
				PInvokeHelp.SCROLLINFO si = new PInvokeHelp.SCROLLINFO();
				si.cbSize = Marshal.SizeOf(si);
				si.fMask = PInvokeHelp.SIF_ALL;

				if (PInvokeHelp.GetScrollInfo(m.HWnd, PInvokeHelp.SB_HORZ, ref si))
					horzScrollBarValue = si.nPos;

				//					Debug.WriteLine("Position = " + si.nPos.ToString() + "; " + si.nTrackPos.ToString());

				// Callback
				if (this.HorizontalScrolled != null)
					this.HorizontalScrolled(this, EventArgs.Empty);
			}
		}

		public new bool HorizontalScrollbar
		{
			get
			{
				return base.HorizontalScrollbar;
			}
			set
			{
				base.HorizontalScrollbar = value;
				if (!base.HorizontalScrollbar)
				{
					base.HorizontalExtent = 0;
					this.HScrollPosition = 0;
				}

				ForceUpdateHScroll();
			}
		}

		/// <summary>
		/// Gets or sets the width by which the horizontal scroll bar of a ListBox
		/// </summary>
		/// <returns>
		/// The width, in pixels, that the horizontal scroll bar can scroll the control. The default is zero.
		/// </returns>
		[DefaultValue(0)]
		[Localizable(true)]
		public new int HorizontalExtent
		{
			get
			{
				return base.HorizontalExtent;
			}
			set
			{
				base.HorizontalExtent = value;

				ForceUpdateHScroll();
			}
		}

		/// <summary>
		/// Gets or sets the width by which the horizontal scroll bar of a ListBox can scroll.
		/// </summary>
		/// <returns>
		/// The width, in pixels, that the horizontal scroll bar can scroll the control. The default is zero.
		/// </returns>
		[DefaultValue(0)]
		[Localizable(true)]
		public int HScrollPosition
		{
			get
			{
				return horzScrollBarValue;
			}
			set
			{
				PInvokeHelp.SCROLLINFO si = new PInvokeHelp.SCROLLINFO();
				si.cbSize = Marshal.SizeOf(si);
				si.fMask = PInvokeHelp.SIF_POS;
				si.nPos = value;

				horzScrollBarValue = PInvokeHelp.SetScrollInfo(this.Handle, PInvokeHelp.SB_HORZ, ref si, true);

				ForceUpdateHScroll();
			}
		}

		protected void ForceUpdateHScroll()
		{
			PInvokeHelp.SendMessage(this.Handle, PInvokeHelp.WM_HSCROLL, PInvokeHelp.MakeDWord(PInvokeHelp.SB_THUMBPOSITION, this.HScrollPosition), 0);
		}

		/// <summary>
		/// Occurs when the horizontal scroll bar scrolls.
		/// </summary>
		public event EventHandler HorizontalScrolled;

		class PInvokeHelp
		{
			public const int WM_HSCROLL = 0x0114;
			public const int WM_MOUSEWHEEL = 0x20A;

			public static int HiWord(int value)
			{
				return (int)(value & 0xFFFF0000) >> 16;
			}

			public static int LoWord(int value)
			{
				return (int)(value & 0x0000FFFF);
			}

			public static long MakeDWord(int lo, int hi)
			{
				return (lo | (hi << 16));
			}

			[StructLayout(LayoutKind.Sequential)]
			public struct SCROLLINFO
			{
				public int cbSize;
				public uint fMask;
				public int nMin;
				public int nMax;
				public uint nPage;
				public int nPos;
				public int nTrackPos;
			}

			// ScrollBarDirection
			public const int SB_HORZ = 0;
			public const int SB_VERT = 1;
			public const int SB_CTL = 2;
			public const int SB_BOTH = 3;

			// ScrollInfoMask
			public const int SIF_RANGE = 0x1;
			public const int SIF_PAGE = 0x2;
			public const int SIF_POS = 0x4;
			public const int SIF_DISABLENOSCROLL = 0x8;
			public const int SIF_TRACKPOS = 0x10;
			public const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS;

			// ScrollBarCommands
			public const int SB_LINEUP = 0;
			public const int SB_LINELEFT = 0;
			public const int SB_LINEDOWN = 1;
			public const int SB_LINERIGHT = 1;
			public const int SB_PAGEUP = 2;
			public const int SB_PAGELEFT = 2;
			public const int SB_PAGEDOWN = 3;
			public const int SB_PAGERIGHT = 3;
			public const int SB_THUMBPOSITION = 4;
			public const int SB_THUMBTRACK = 5;
			public const int SB_TOP = 6;
			public const int SB_LEFT = 6;
			public const int SB_BOTTOM = 7;
			public const int SB_RIGHT = 7;
			public const int SB_ENDSCROLL = 8;

			[DllImport("user32.dll")]
			public static extern int SendMessage(IntPtr hWnd, uint Msg, long wParam, long lParam);

			[DllImport("user32.dll")]
			[return: MarshalAs(UnmanagedType.Bool)]
			public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);

			[DllImport("user32.dll")]
			public static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref SCROLLINFO lpsi, bool fRedraw);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Australia Australia
Master of IT, University of Technology Sydney

Bachelor Degree in Telecommunication, Hochiminh University of Technology, Vietnam

A few years of experience with .NET technology, computer graphics + animation, web development, algorithm design and many other things.

Comments and Discussions