Click here to Skip to main content
15,896,348 members
Articles / Web Development / ASP.NET

Zeta Enterprise Library

Rate me:
Please Sign up or sign in to vote.
4.97/5 (14 votes)
16 Jan 2010CPOL3 min read 50.4K   2.3K   48  
A small set of general-purpose classes for using in .NET applications (2.0 or higher)
namespace Zeta.EnterpriseLibrary.Windows.Controls
{
	#region Using directives.
	// ----------------------------------------------------------------------

	using System;
	using System.Collections;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Windows.Forms;
	using Common;
	using EnterpriseLibrary.Common;
	using Tools.Storage;

	// ----------------------------------------------------------------------
	#endregion

	/////////////////////////////////////////////////////////////////////////

	/// <summary>
	/// List view control that allows sorting and stores its sort state to 
	/// persistent storage.
	/// </summary>
	public partial class SortingListViewControl :
		ExtendedListViewControl,
		ISaveRestoreState
	{
		#region Public methods.
		// ------------------------------------------------------------------

		/// <summary>
		/// Initializes a new instance of the 
		/// <see cref="SortingListViewControl"/> class.
		/// </summary>
		public SortingListViewControl()
		{
			Sorting = SortOrder.None;
		}

		/// <summary>
		/// Apply the current sorting again.
		/// </summary>
		public void RefreshSort()
		{
			doRefreshSort();
		}

		/// <summary>
		/// Manually persist the sort state. Also done automatically, but
		/// for more control, call it here manually.
		/// </summary>
		public void PersistSortState()
		{
			OnPersistingSortState(null);
		}

		/// <summary>
		/// Manually restore the state. Also done automatically, but
		/// for more control, call it here manually.
		/// </summary>
		public void RestoreSortState()
		{
			OnRestoringSortState(null);
		}

		// ------------------------------------------------------------------
		#endregion

		#region Public events and event arguments.
		// ------------------------------------------------------------------

		/// <summary>
		/// 
		/// </summary>
		public delegate void PersistingSortStateEventHandler(
			object sender,
			PersistingSortStateEventArgs args);

		/// <summary>
		/// 
		/// </summary>
		public delegate void RestoringSortStateEventHandler(
			object sender,
			RestoringSortStateEventArgs args);

		/// <summary>
		/// 
		/// </summary>
		public class PersistingSortStateEventArgs :
			EventArgs
		{
			#region Private variables.

			/// <summary>
			/// Default.
			/// </summary>
			private IPersistentPairStorage _storage =
				FormHelper.Storage;

			#endregion

			#region Public properties.

			/// <summary>
			/// Gets or sets the storage.
			/// </summary>
			/// <value>The storage.</value>
			public IPersistentPairStorage Storage
			{
				get
				{
					return _storage;
				}
				set
				{
					_storage = value;
				}
			}

			#endregion
		}

		/// <summary>
		/// 
		/// </summary>
		public class RestoringSortStateEventArgs :
			EventArgs
		{
			#region Private variables.

			/// <summary>
			/// Default.
			/// </summary>
			private IPersistentPairStorage _storage =
				FormHelper.Storage;

			#endregion

			#region Public properties.

			/// <summary>
			/// Gets or sets the storage.
			/// </summary>
			/// <value>The storage.</value>
			public IPersistentPairStorage Storage
			{
				get
				{
					return _storage;
				}
				set
				{
					_storage = value;
				}
			}

			#endregion
		}

		/// <summary>
		/// Called before persisting the sort state.
		/// </summary>
		[Description(@"Called before persisting the sort state.")]
		public event PersistingSortStateEventHandler PersistingSortState;

		/// <summary>
		/// Called before restoring the sort state.
		/// </summary>
		[Description(@"Called before restoring the sort state.")]
		public event RestoringSortStateEventHandler RestoringSortState;

		// ------------------------------------------------------------------
		#endregion

		#region Public properties.
		// ------------------------------------------------------------------

		/// <summary>
		/// Get or set whether to automatically restore the state of
		/// the list view when creating.
		/// </summary>
		/// <value><c>true</c> if [auto restore state]; otherwise, <c>false</c>.</value>
		[
		Category(@"Behavior"),
		DefaultValue(false),
		Description(@"Get or set whether to automatically restore the state (column widths, sort order, selection info) of the list view when creating.")
		]
		public bool AutoRestoreState
		{
			get
			{
				return _autoRestoreState;
			}
			set
			{
				_autoRestoreState = value;
			}
		}

		/// <summary>
		/// Tell this object which columns this item has. Useful for sorting,
		/// but optionally, the sorting works without setting this, too.
		/// </summary>
		/// <value>The column types.</value>
		[Browsable(false)]
		public Type[] ColumnTypes
		{
			get
			{
				return _columnTypes;
			}
			set
			{
				if (value == null)
				{
					_columnTypes = null;
				}
				else
				{
					Debug.Assert(
						(value == null && Columns == null) ||
						(value.Length == Columns.Count),
						string.Format(
						@"The number of column types ({0}) to set to the sorting " +
						@"list view control differs from the actual column count ({1}).",
						value.Length,
						Columns == null ? 0 : Columns.Count));

					_columnTypes = value;
				}
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether [auto sorting].
		/// </summary>
		/// <value><c>true</c> if [auto sorting]; otherwise, <c>false</c>.</value>
		[
		Category(@"Behavior"),
		DefaultValue(false),
		Description(@"Get or set whether the control should auto sort items.")
		]
		public bool AutoSorting
		{
			get
			{
				return _autoSorting;
			}
			set
			{
				_autoSorting = value;
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Private sorting helper class.
		// ------------------------------------------------------------------

		/// <summary>
		/// Helper class for sorting.
		/// </summary>
		private class Sorter :
			IComparer
		{
			#region Public methods.

			/// <summary>
			/// Initializes a new instance of the <see cref="Sorter"/> class.
			/// </summary>
			/// <param name="owner">The owner.</param>
			/// <param name="columnIndex">Index of the column.</param>
			/// <param name="sortAscending">if set to <c>true</c> [sort ascending].</param>
			public Sorter(
				SortingListViewControl owner,
				int columnIndex,
				bool sortAscending)
			{
				_owner = owner;
				_columnIndex = columnIndex;
				_sortAscending = sortAscending;

				// 2009-01-31, Uwe Keim: Limit.
				if (owner.Columns.Count <= 0)
				{
					_columnIndex = -1;
				}
				else if (_columnIndex >= owner.Columns.Count)
				{
					_columnIndex = 0;
				}
			}

			/// <summary>
			/// Compares two objects and returns a value indicating whether one 
			/// is less than, equal to, or greater than the other.
			/// </summary>
			/// <param name="x">The first object to compare.</param>
			/// <param name="y">The second object to compare.</param>
			/// <returns>
			/// Value Condition 
			/// Less than zero x is less than y. 
			/// Zero x equals y. 
			/// Greater than zero x is greater than y.
			/// </returns>
			/// <exception cref="T:System.ArgumentException">Neither x nor y 
			/// implements the <see cref="T:System.IComparable"></see> interface.
			/// -or- x and y are of different types and neither one can handle 
			/// comparisons with the other. </exception>
			public int Compare(
				object x,
				object y)
			{
				// Added 2009-01-30, Uwe Keim: Only sort if valid column range.
				if (_columnIndex >= 0)
				{
					return _owner.onSortInternal(
						_columnIndex,
						_sortAscending,
						(ListViewItem)x,
						(ListViewItem)y);
				}
				else
				{
					return 0;
				}
			}

			#endregion

			#region Private variables.

			private readonly SortingListViewControl _owner;
			private readonly int _columnIndex;
			private readonly bool _sortAscending;

			#endregion
		}

		/// <summary>
		/// Called when sorting.
		/// </summary>
		/// <param name="columnIndex">Index of the column.</param>
		/// <param name="sortAscending">if set to <c>true</c> [sort ascending].</param>
		/// <param name="x">The x.</param>
		/// <param name="y">The y.</param>
		/// <returns></returns>
		private int onSortInternal(
			int columnIndex,
			bool sortAscending,
			ListViewItem x,
			ListViewItem y)
		{
			// 2009-08-25, Uwe Keim.
			if (columnIndex < 0 || _columnTypes == null || columnIndex >= _columnTypes.Length ||
				columnIndex >= Columns.Count)
			{
				columnIndex = 0;
			}

			// --

			var columnType =
				_columnTypes == null || columnIndex >= _columnTypes.Length ?
				typeof(string) :
				_columnTypes[columnIndex];

			int result;

			if (columnType == typeof(DateTime))
			{
				var valueX = DateTime.MinValue;
				var valueY = DateTime.MinValue;

				if (ConvertHelper.IsDateTime(
					getSubItemTagOrText(x.SubItems[columnIndex])))
				{
					valueX = Convert.ToDateTime(
						getSubItemTagOrText(x.SubItems[columnIndex]));
				}
				if (ConvertHelper.IsDateTime(
					getSubItemTagOrText(y.SubItems[columnIndex])))
				{
					valueY = Convert.ToDateTime(
						getSubItemTagOrText(y.SubItems[columnIndex]));
				}

				result = valueX.CompareTo(valueY);
			}
			else if (columnType == typeof(bool))
			{
				var valueX = false;
				var valueY = false;

				if (ConvertHelper.IsBoolean(
					getSubItemTagOrText(x.SubItems[columnIndex])))
				{
					valueX = Convert.ToBoolean(
						getSubItemTagOrText(x.SubItems[columnIndex]));
				}
				if (ConvertHelper.IsBoolean(
					getSubItemTagOrText(y.SubItems[columnIndex])))
				{
					valueY = Convert.ToBoolean(
						getSubItemTagOrText(y.SubItems[columnIndex]));
				}

				result = valueX.CompareTo(valueY);
			}
			else if (columnType == typeof(double))
			{
				var valueX = 0.0;
				var valueY = 0.0;

				if (ConvertHelper.IsDouble(
					getSubItemTagOrText(x.SubItems[columnIndex])))
				{
					valueX = Convert.ToDouble(
						getSubItemTagOrText(x.SubItems[columnIndex]));
				}
				if (ConvertHelper.IsDouble(
					getSubItemTagOrText(y.SubItems[columnIndex])))
				{
					valueY = Convert.ToDouble(
						getSubItemTagOrText(y.SubItems[columnIndex]));
				}

				result = valueX.CompareTo(valueY);
			}
			else if (columnType == typeof(int))
			{
				var valueX = 0;
				var valueY = 0;

				if (ConvertHelper.IsInt32(
					getSubItemTagOrText(x.SubItems[columnIndex])))
				{
					valueX = Convert.ToInt32(
						getSubItemTagOrText(x.SubItems[columnIndex]));
				}
				if (ConvertHelper.IsInt32(
					getSubItemTagOrText(y.SubItems[columnIndex])))
				{
					valueY = Convert.ToInt32(
						getSubItemTagOrText(y.SubItems[columnIndex]));
				}

				result = valueX.CompareTo(valueY);
			}
			else
			{
				Debug.Assert(columnType == typeof(string));

				result = string.Compare(
					getSubItemTagOrText(x.SubItems[columnIndex]).ToString(),
					getSubItemTagOrText(y.SubItems[columnIndex]).ToString());
			}

			if (sortAscending)
			{
				result *= -1;
			}

			return result;
		}

		/// <summary>
		/// Gets the sub item tag or text.
		/// </summary>
		/// <param name="subItem">The sub item.</param>
		/// <returns></returns>
		private static object getSubItemTagOrText(
			ListViewItem.ListViewSubItem subItem)
		{
			if (subItem.Tag != null)
			{
				return subItem.Tag;
			}
			else
			{
				return subItem.Text;
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Private overrides.
		// ------------------------------------------------------------------

		/// <summary>
		/// Called when [persisting sort state].
		/// </summary>
		/// <param name="storage">The storage.</param>
		protected virtual void OnPersistingSortState(
			IPersistentPairStorage storage)
		{
			if (!_insidePersisting)
			{
				_insidePersisting = true;
				try
				{
					var args = new PersistingSortStateEventArgs();

					if (storage != null)
					{
						args.Storage = storage;
					}

					if (PersistingSortState != null)
					{
						PersistingSortState(this, args);
					}

					FormHelper.SaveState(args.Storage, this);

					FormHelper.SaveValue(
						args.Storage,
						string.Format(
						@"SortingListViewControl.{0}.LastSortColumnIndex",
						Name),
						_lastSortColumnIndex);
					FormHelper.SaveValue(
						args.Storage,
						string.Format(
						@"SortingListViewControl.{0}.SortAscending",
						Name),
						_sortAscending);
				}
				finally
				{
					_insidePersisting = false;
				}
			}
		}

		/// <summary>
		/// Called when [restoring sort state].
		/// </summary>
		/// <param name="storage">The storage.</param>
		protected virtual void OnRestoringSortState(
			IPersistentPairStorage storage)
		{
			if (!_insideRestoring)
			{
				_insideRestoring = true;
				try
				{
					var args = new RestoringSortStateEventArgs();

					if (storage != null)
					{
						args.Storage = storage;
					}

					if (RestoringSortState != null)
					{
						RestoringSortState(this, args);
					}

					FormHelper.RestoreState(args.Storage, this);

					_lastSortColumnIndex = Convert.ToInt32(FormHelper.RestoreValue(
						args.Storage,
						string.Format(
						@"SortingListViewControl.{0}.LastSortColumnIndex",
						Name),
						_lastSortColumnIndex));
					_sortAscending = Convert.ToBoolean(FormHelper.RestoreValue(
						args.Storage,
						string.Format(
						@"SortingListViewControl.{0}.SortAscending",
						Name),
						_sortAscending));
				}
				finally
				{
					_insideRestoring = false;
				}
			}
		}

		/// <summary>
		/// The control is being created.
		/// </summary>
		/// <param name="e">The <see cref="System.EventArgs"/> instance 
		/// containing the event data.</param>
		protected override void OnHandleCreated(
			EventArgs e)
		{
			base.OnHandleCreated(e);

			if (_autoRestoreState)
			{
				OnRestoringSortState(null);
			}

			// Initially sort.
			if (_autoSorting)
			{
				RefreshSort();
			}
		}

		/// <summary>
		/// The control is destroyed.
		/// </summary>
		/// <param name="e">The <see cref="System.EventArgs"/> instance 
		/// containing the event data.</param>
		protected override void OnHandleDestroyed(
			EventArgs e)
		{
			if (_autoRestoreState)
			{
				OnPersistingSortState(null);
			}

			base.OnHandleDestroyed(e);
		}

		/// <summary>
		/// Raises the <see cref="E:System.Windows.Forms.ListView.ColumnClick"></see>
		/// event.
		/// </summary>
		/// <param name="e">A <see cref="T:System.Windows.Forms.ColumnClickEventArgs"></see> 
		/// that contains the event data.</param>
		protected override void OnColumnClick(
			ColumnClickEventArgs e)
		{
			base.OnColumnClick(e);

			applySort(e.Column);
		}

		/// <summary>
		/// Sort by the given column.
		/// </summary>
		/// <param name="columnIndex">Index of the column.</param>
		private void applySort(
			int columnIndex)
		{
			BeginUpdate();
			try
			{
				if (columnIndex == _lastSortColumnIndex)
				{
					_sortAscending = !_sortAscending;
				}
				else
				{
					ShowHeaderIcon(_lastSortColumnIndex, SortOrder.None);
					_sortAscending = true;
				}

				_lastSortColumnIndex = columnIndex;
				ShowHeaderIcon(
					_lastSortColumnIndex,
					_sortAscending ? SortOrder.Ascending : SortOrder.Descending);

				// Set to sort. This also immediately sorts.
				ListViewItemSorter = new Sorter(
					this,
					columnIndex,
					_sortAscending);
			}
			finally
			{
				EndUpdate();
			}
		}

		/// <summary>
		/// Does the refresh sort.
		/// </summary>
		private void doRefreshSort()
		{
			BeginUpdate();
			try
			{
				var columnIndex = Math.Max(0, _lastSortColumnIndex);

				_lastSortColumnIndex = columnIndex;
				ShowHeaderIcon(
					_lastSortColumnIndex,
					_sortAscending ? SortOrder.Ascending : SortOrder.Descending);

				// Set to sort. This also immediately sorts.
				ListViewItemSorter = new Sorter(
					this,
					columnIndex,
					_sortAscending);
			}
			finally
			{
				EndUpdate();
			}
		}

		// ------------------------------------------------------------------
		#endregion

		#region Private variables.
		// ------------------------------------------------------------------

		private bool _autoRestoreState;
		private int _lastSortColumnIndex = -1;
		private bool _sortAscending = true;

		private Type[] _columnTypes;

		private bool _autoSorting;

		private bool _insidePersisting;
		private bool _insideRestoring;

		// ------------------------------------------------------------------
		#endregion

		#region ISaveRestoreState members.
		// ------------------------------------------------------------------

		/// <summary>
		/// Called after saving values from the control to the storage.
		/// </summary>
		/// <param name="storage">The storage.</param>
		/// <param name="prefix">The prefix.</param>
		void ISaveRestoreState.OnSaveState(
			IPersistentPairStorage storage,
			string prefix)
		{
			storage.PersistValue(
				string.Format(@"{0}.{1}.LastSortColumnIndex", prefix, Name),
				_lastSortColumnIndex);
			storage.PersistValue(
				string.Format(@"{0}.{1}.SortAscending", prefix, Name),
				_sortAscending);
		}

		/// <summary>
		/// Called after restoring values from the storage to the control.
		/// </summary>
		/// <param name="storage">The storage.</param>
		/// <param name="prefix">The prefix.</param>
		void ISaveRestoreState.OnRestoreState(
			IPersistentPairStorage storage,
			string prefix)
		{
			_lastSortColumnIndex = ConvertHelper.ToInt32(
				storage.RetrieveValue(
					string.Format(@"{0}.{1}.LastSortColumnIndex", prefix, Name),
					_lastSortColumnIndex));
			_sortAscending = ConvertHelper.ToBoolean(
				storage.RetrieveValue(
					string.Format(@"{0}.{1}.SortAscending", prefix, Name),
					_sortAscending));

			if (Columns.Count > 0)
			{
				RefreshSort();
			}
		}

		// ------------------------------------------------------------------
		#endregion
	}

	/////////////////////////////////////////////////////////////////////////
}

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
Chief Technology Officer Zeta Software GmbH
Germany Germany
Uwe does programming since 1989 with experiences in Assembler, C++, MFC and lots of web- and database stuff and now uses ASP.NET and C# extensively, too. He has also teached programming to students at the local university.

➡️ Give me a tip 🙂

In his free time, he does climbing, running and mountain biking. In 2012 he became a father of a cute boy and in 2014 of an awesome girl.

Some cool, free software from us:

Windows 10 Ereignisanzeige  
German Developer Community  
Free Test Management Software - Intuitive, competitive, Test Plans.  
Homepage erstellen - Intuitive, very easy to use.  
Offline-Homepage-Baukasten

Comments and Discussions