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