|
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.CodeDom;
using System.Collections;
using System.Diagnostics;
namespace RR.Windows.Forms
{
/// <summary>
/// Interface which specialized sorters must implement.
/// </summary>
public interface IListViewComparer
{
int OnCompare(string x, string y);
}
/// <summary>
/// Provides a standard Winform ListView with sorting capabilities.
/// </summary>
[DesignTimeVisible(true),ToolboxItem(true),]
public class ListViewExSort : System.ComponentModel.Component
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public ListViewExSort(System.ComponentModel.IContainer container)
{
///
/// Required for Windows.Forms Class Composition Designer support
///
container.Add(this);
InitializeComponent();
}
public ListViewExSort()
{
///
/// Required for Windows.Forms Class Composition Designer support
///
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
#endregion
#region private
private IListViewComparer[] comparer=null;
private ListView listView;
private ListViewWindow window;
private static StringComparer defaultComparer=new StringComparer();
// accessing the sorting property from ListView resets column ordering to default.
// thats why we work with a copy here.
private SortOrder sorting=SortOrder.None;
private int sortColumn = -1;
private Color sortBkgnColor = Color.WhiteSmoke;
private bool sortShaded=true;
private void DrawSortGlyph(int column, SortOrder sortOrder)
{
if (this.listView==null) return;
IntPtr header = Win32.User32.GetDlgItem(this.listView.Handle, 0);
Win32.HDITEM hdItem = new Win32.HDITEM();
// only the format member is valid
hdItem.mask = Win32.HDI.FORMAT;
// get header item info for column
Win32.User32.SendMessage(header, Win32.HDM.GETITEMW, column, ref hdItem);
hdItem.fmt &= Win32.HDF.NOSORT;
if (sortOrder == SortOrder.Ascending)
{
hdItem.fmt |= Win32.HDF.SORTUP;
}
else if (sortOrder == SortOrder.Descending)
{
hdItem.fmt |= Win32.HDF.SORTDOWN;
}
// write header info back with changed formatting flags
Win32.User32.SendMessage(header, Win32.HDM.SETITEMW, column, ref hdItem);
}
private Win32.CDRF NotifyCustomDraw( Win32.NMLVCUSTOMDRAW d )
{
// determine the draw stage and set return value
switch ( d.nmcd.dwDrawStage )
{
// first request, ask for each item notification if we shade columns
case (int)Win32.CDDS.PREPAINT:
if ( sortShaded && ( this.listView.View == View.Details ) )
return Win32.CDRF.NOTIFYITEMDRAW;
break;
// next request, ask for each sub item notification for this item
case (int)Win32.CDDS.ITEMPREPAINT:
return Win32.CDRF.NOTIFYSUBITEMDRAW;
// here is the real work, and it is simply ensuring that the
// correct backcolor is set for the shaded column. we let the
// regular windows control drawing do the real work.
case (int)Win32.CDDS.SUBITEMPREPAINT:
// ensure that this is a valid item/subitem request
if ( d.nmcd.dwItemSpec < this.listView.Items.Count )
{
// get a reference to the item to be rendered
ListViewItem item = this.listView.Items[d.nmcd.dwItemSpec];
// is this for a a base item set it's backcolor
if ( d.iSubItem == 0 )
{
item.BackColor = ( d.iSubItem == sortColumn ) ? sortBkgnColor : this.listView.BackColor;
}
// ensure that the subitem exits before changing it
else if ((d.iSubItem < item.SubItems.Count) && (item.SubItems[d.iSubItem]!=null))
{
item.SubItems[d.iSubItem].BackColor = (d.iSubItem==sortColumn)? sortBkgnColor:this.listView.BackColor;
}
}
break;
}
// let default drawing do the actual rendering
return Win32.CDRF.DODEFAULT;
}
private void WndProc(ref Message m)
{
if (m.Msg==(int)Win32.WM.ERASEBKGND)
{
// shade background if in details mode
if (sortShaded && (this.listView.View == View.Details) && (this.sortColumn>=0))
{
// get bounds of sorted column (only left/right used)
Win32.RECT rect = new Win32.RECT();
Win32.User32.SendMessage(Win32.User32.GetDlgItem(this.listView.Handle, 0), Win32.HDM.GETITEMRECT, this.sortColumn, ref rect);
Rectangle r=new Rectangle(rect.left, this.listView.Top, (rect.right - rect.left), this.listView.Height);
// shift-x with bounds of first item (may be negative if y-scroll)
r.X=r.X+((this.listView.Items.Count>0)?this.listView.GetItemRect(0).X:0);
// and fill using the selected background color
using (Brush b = new SolidBrush(sortBkgnColor))
{
Graphics.FromHdc(m.WParam).FillRectangle(b, r);
}
}
}
else if (m.Msg==(int)Win32.OCM.NOTIFY)
{
Win32.NMHEADER nmHdr = (Win32.NMHEADER)m.GetLParam(typeof(Win32.NMHEADER));
if ((nmHdr.hdr.hwndFrom == this.listView.Handle) && (nmHdr.hdr.code== (int)Win32.NM.CUSTOMDRAW))
{
Win32.NMLVCUSTOMDRAW d = (Win32.NMLVCUSTOMDRAW)m.GetLParam(typeof(Win32.NMLVCUSTOMDRAW));
m.Result = (IntPtr)NotifyCustomDraw(d);
}
}
}
#endregion
#region public
[Description("The listview this extender works with. If the listview is not set, the extender has no function.")]
public ListView ListView
{
get
{
return listView;
}
set
{
listView=value;
if (listView!=null)
{
window=new ListViewWindow(this);
listView.ColumnClick += new ColumnClickEventHandler(ColumnClick);
listView.ListViewItemSorter = new ListViewItemComparer(this);
}
}
}
[Browsable(false)]
public IListViewComparer[] Comparer
{
get
{
return comparer;
}
set
{
comparer=value;
}
}
[Description("The column on which sorting takes place."), Category("Behavior"), DefaultValue(-1)]
public int SortColumn
{
get
{
return this.sortColumn;
}
set
{
if (this.sortColumn!=value)
{
DrawSortGlyph(this.sortColumn, SortOrder.None);
this.sortColumn=value;
DrawSortGlyph(this.sortColumn, this.sorting);
if (this.listView!=null)
{
this.listView.Invalidate();
}
}
}
}
[Description("Indicates the manner in which items are to be sorted"), Category("Behavior"), DefaultValue(SortOrder.None)]
public SortOrder Sorting
{
get
{
return this.sorting;
}
set
{
sorting=value;
DrawSortGlyph(this.sortColumn, this.Sorting);
this.listView.Sort();
this.listView.Refresh(); // paints selected column background
this.listView.Invalidate();
}
}
[Description("Color of the shaded column in details mode"), Category("Appearance")]
public Color ShadeColor
{
get
{
return sortBkgnColor;
}
set
{
sortBkgnColor = value;
if (this.listView!=null) this.listView.Refresh();
}
}
[Description("If true, the selected column will be shaded when in detail mode."), Category("Appearance"), DefaultValue(true)]
public bool Shaded
{
get
{
return sortShaded;
}
set
{
sortShaded = value;
this.listView.Refresh();
}
}
#endregion
#region listview event handler
private void ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine whether the column is the same as the last column clicked.
if (e.Column != sortColumn)
{
DrawSortGlyph(sortColumn, SortOrder.None);
// Set the sort column to the new column.
sortColumn = e.Column;
// Set the sort order to ascending by default.
this.Sorting = SortOrder.Ascending;
}
else
{
// Determine what the last sort order was and change it.
this.Sorting=(this.Sorting == SortOrder.Ascending)?SortOrder.Descending:SortOrder.Ascending;
}
this.listView.Sort();
}
#endregion
#region private classes
private class StringComparer : IListViewComparer
{
public int OnCompare(string x, string y)
{
return string.Compare(x, y);
}
}
private class ListViewItemComparer : IComparer
{
private ListViewExSort listView;
public ListViewItemComparer(ListViewExSort listView)
{
this.listView=listView;
}
public int Compare(object x, object y)
{
if (listView.SortColumn<0) return -1;
IListViewComparer comparer;
if ((listView.comparer!=null)&&(listView.SortColumn<listView.comparer.Length)&&(listView.comparer[listView.SortColumn]!=null))
{
comparer=listView.comparer[listView.SortColumn];
}
else
{
comparer=ListViewExSort.defaultComparer;
}
Debug.Assert((x as ListViewItem)!=null);
Debug.Assert((y as ListViewItem)!=null);
if (listView.SortColumn>=((ListViewItem)x).SubItems.Count) return -1;
if (listView.SortColumn>=((ListViewItem)y).SubItems.Count) return -1;
int result=comparer.OnCompare(((ListViewItem)x).SubItems[listView.SortColumn].Text, ((ListViewItem)y).SubItems[listView.SortColumn].Text);
return (listView.Sorting==SortOrder.Descending)?-result:result;
}
}
private class ListViewWindow : NativeWindow
{
public ListViewWindow (ListViewExSort parent)
{
this.parent=parent;
this.parent.listView.HandleCreated += new EventHandler(this.OnHandleCreated);
this.parent.listView.HandleDestroyed+= new EventHandler(this.OnHandleDestroyed);
}
private ListViewExSort parent;
internal void OnHandleCreated(object sender, EventArgs e)
{
// Window is now created, assign handle to NativeWindow.
AssignHandle(this.parent.listView.Handle);
}
internal void OnHandleDestroyed(object sender, EventArgs e)
{
// Window was destroyed, release hook.
ReleaseHandle();
}
protected override void WndProc(ref Message m)
{
base.WndProc (ref m);
this.parent.WndProc (ref m);
}
}
#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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.