using System;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
namespace SI.Controls
{
public class LvSort : System.Windows.Forms.ListView
{
#region Description
//
//#######################################################################################
//
// class LvSort
// Author Jan Schröder
//
Schröder Informatik
//
www.SchroederInformatik.de
// Version 1.0.4
// Date 05/05/24
//
// This class is adding sort functionality to the
inherited class "ListView" by
// implementing a non case sensitive IComparer and
visualizes the sort order by drawing
// an appropriate triangle in the sorting column
header.
//
// It also adds the missing events "Scroll" and
"Paint".
//
// Use this class as follows:
//
// 1. Copy "LvSort.cs" to your project.
// 2. Add a "System.Windows.Forms.ListView" to your
form.
// 3. Replace "System.Windows.Forms.ListView" with
"SI.Controls.LvSort" in
// the generated Code.
//
// The advantage of this kind of usage is, you will not
have to deploy an additional DLL,
// all code will be part of your executable.
//
//
// Versions:
// 1.0.0 05/03/02 First Version
// 1.0.1
05/03/05 LblMeasureHeader.Font = MyListViewValue.Font in
//
ListViewColumnSorter.VisualizeOrder
// 1.0.2 05/03/08 Invoking sort
method when setting SortColum or Order, so setting one
//
of these properties (i.e. in a forms load event) will cause sorting.
//
Tip: To avoid an unnecessary sort, set Order to "none" before
//
setting the SortColumn and then Order to "ascending" or
//
"descending".
//
If you want to sort the list view by the fist two columns, notice
//
the following sequence:
//
1. ListView1.Order = SortOrder.None;
//
2. ListView1.SortColumn = 1;
//
3. ListView1.Order = SortOrder.Ascending;
//
4. ListView1.SortColumn = 0;
//
After step 2 there will be no sorting, because Order = None. After
//
step 3 the list view will be sorted by column 1 in ascending order.
//
After step 4 the list view will be sorted by column 0 in ascending
//
order and if there are items with identical text in column 0, they
//
will be sorted by column 1.
// 1.0.3
05/03/08 base.ListViewItemSorter = null in properties
Order and
//
SortColumn to avoid unnecessary sorting when loading the view.
//
To do so, Order has to be set to none or SortColumn to -1.
// 1.0.4 05/05/24 Translation
from VB into C#
//
//#######################################################################################
//
#endregion
#region Constructors
public LvSort()
{
base.ColumnClick += new
ColumnClickEventHandler(MyBase_ColumnClick);
LvwColumnSorter = new
ListViewColumnSorter(this);
}
#endregion
#region Data definition
public string FixedStringForLastItem
{
//
// Fixed string for the last item, so it
will stay the last item
//
get {
return
LvwColumnSorter.FixedStringForLastItem;
}
set {
//
// The IComparer has to know
that
//
LvwColumnSorter.FixedStringForLastItem
= value;
}
}
public int SortColumn
{
//
// Index of sort column
//
get {
return
LvwColumnSorter.SortColumn;
}
set {
//
// Put it through to IComparer
//
LvwColumnSorter.SortColumn =
value;
//
// Let's do it
//
if ( LvwColumnSorter.Order ==
SortOrder.None ) {
base.ListViewItemSorter
= null;
} else {
if (
base.ListViewItemSorter == null ) {
base.ListViewItemSorter
= LvwColumnSorter;
} else {
base.Sort();
}
}
}
}
public SortOrder Order
{
//
// Sort order (none, ascending or
descending)
//
get
{
return LvwColumnSorter.Order;
}
set
{
//
// Last information needed for
IComparer
//
LvwColumnSorter.Order = value;
if ( value == SortOrder.None )
{
if (
LvwColumnSorter.SortColumn > -1 )
{
//
//
Setting the text causes a repaint of the header
//
base.Columns[LvwColumnSorter.SortColumn].Text
=
base.Columns[LvwColumnSorter.SortColumn].Text;
//
// if
there has been a triangle from a previous sorting,
// it
is now deleted
//
}
LvwColumnSorter.SortColumn
= -1;
}
//
// Let's do it
//
if ( LvwColumnSorter.SortColumn
== -1 )
{
base.ListViewItemSorter
= null;
}
else
{
if (
base.ListViewItemSorter == null )
{
base.ListViewItemSorter
= LvwColumnSorter;
}
else
{
base.Sort();
}
}
}
}
private LvlListenerClass LvlListener;
private HdrListenerClass HdrListener;
private ListViewColumnSorter LvwColumnSorter;
#endregion
#region event handling
public event EventHandler Scroll;
public event EventHandler MyPaint;
private class LvlListenerClass
: NativeWindow
{
//
//
Listen for operating system messages to raise the events
//
"Scroll" or "Paint" for the ListView
//
public
event EventHandler Scroll;
public
event EventHandler MyPaint;
private
const int WM_HSCROLL = 0x114;
private
const int WM_VSCROLL = 0x115;
private
const int WM_PAINT = 0xF;
private Control CtrlValue;
public LvlListenerClass(Control Ctrl)
{
AssignHandle(Ctrl.Handle);
CtrlValue = Ctrl;
}
protected override void WndProc(ref Message
m)
{
base.WndProc(ref m);
if ( m.Msg == WM_HSCROLL ||
m.Msg == WM_VSCROLL ) {
Scroll(CtrlValue,
new EventArgs());
}
if ( m.Msg == WM_PAINT ) {
MyPaint(CtrlValue,
new EventArgs());
}
}
~LvlListenerClass()
{
ReleaseHandle();
}
}
private class HdrListenerClass : NativeWindow {
//
//
Listen for operating system messages to raise the event
//
"HaederPaint", when the column headers are to be painted.
// On
this event, a triangle, symbolizing sort order and
//
column is to be drawn.
//
public
event EventHandler HaederPaint;
private
const int WM_PAINT = 0xF;
private
Control CtrlValue;
public HdrListenerClass( Control
Ctrl, System.IntPtr HeaderHandle)
{
AssignHandle(HeaderHandle);
CtrlValue
= Ctrl;
}
protected override void WndProc(ref Message
m)
{
base.WndProc(ref m);
if ( m.Msg == WM_PAINT ) {
HaederPaint(CtrlValue,
new EventArgs());
}
}
~HdrListenerClass()
{
ReleaseHandle();
}
}
protected override void OnHandleCreated( EventArgs e)
{
//
// Now it's the right time to do some
initializations
//
base.OnHandleCreated(e);
if (! this.DesignMode ) {
LvlListener = new
LvlListenerClass(this);
LvlListener.MyPaint += new
EventHandler(LvlListener_Paint);
LvlListener.Scroll +=
new EventHandler(LvlListener_Scroll);
LvwColumnSorter.GetHeaderHandle();
HdrListener = new
HdrListenerClass(this, LvwColumnSorter.HeaderHandle);
HdrListener.HaederPaint += new
EventHandler(HdrListener_HaederPaint);
}
}
private void LvlListener_Paint(object sender, EventArgs
e)
{
//
// Make this event public
//
if (MyPaint != null){MyPaint(sender, e);}
}
private void LvlListener_Scroll(object sender,
EventArgs e)
{
//
// Make
this event public
//
if (Scroll != null){Scroll(sender, e);}
}
private void HdrListener_HaederPaint(object sender,
System.EventArgs e)
{
//
// The
column headers has been painted, so draw the sort triangle
//
LvwColumnSorter.VisualizeOrder();
}
private void MyBase_ColumnClick(object sender,
System.Windows.Forms.ColumnClickEventArgs
e)
{
//
// The
user wants to sort the list view items
//
if
((e.Column == SortColumn) ) {
//
//
The column has been clicked twice, so switch the sort order
//
if
((Order == SortOrder.Ascending) ) {
Order
= SortOrder.Descending;
}
else {
Order
= SortOrder.Ascending;
}
} else
{
//
//
It has to be sort ascending by the column
//
SortColumn
= e.Column;
Order
= SortOrder.Ascending;
}
}
#endregion
#region API stuff
//
// The API function
ChildWindowFromPoint is used to find out the window handle of
// the columns header.
//
[DllImport("user32")] public static extern
System.IntPtr
ChildWindowFromPoint(System.IntPtr hwnd,
int xPoint, int yPoint);
#endregion
#region Subroutines and functions
private class
ListViewColumnSorter : System.Collections.IComparer
{
//
// This
class implements an non case sensitve IComparer for
//
sorting items in a ListView
//
private
int ColumnToSort;
private
SortOrder OrderOfSort;
private
CaseInsensitiveComparer ObjectCompare;
private
string FixedStringForLastItemValue;
private
LvSort MyListViewValue;
public ListViewColumnSorter(LvSort
MyListView)
{
ColumnToSort
= 0;
OrderOfSort
= SortOrder.None;
ObjectCompare
= new CaseInsensitiveComparer();
MyListViewValue
= MyListView;
}
public string FixedStringForLastItem
{
//
//
Fixed string for the last item, so it will stay the last item
//
get
{
return
FixedStringForLastItemValue;
}
set
{
FixedStringForLastItemValue
= value;
}
}
public int SortColumn
{
//
//
Index of sort column
//
set
{
ColumnToSort
= value;
}
get
{
return
ColumnToSort;
}
}
public SortOrder Order
{
//
//
Sort order (none, ascending or descending)
//
set
{
OrderOfSort
= value;
VisualizeOrder();
}
get
{
return
OrderOfSort;
}
}
public System.IntPtr HeaderHandle
{
//
//
The handle of the columns header
//
get
{
return
HeaderHandleValue;
}
}
public void GetHeaderHandle()
{
//
//
Find out the handle of the columns header
//
HeaderHandleValue
=
ChildWindowFromPoint(MyListViewValue.Handle,
5, 5);
}
public int Compare(object a, object b)
{
//
//
Using a case insensitive comparer to compare the Text of
//
two list view items
//
if
( OrderOfSort == SortOrder.None ) {
//
//
return 0 means that both items are equal, so no
//
change of the sequence will occure
//
return
0;
}
int
Result;
ListViewItem
LvIa = (ListViewItem) a;
ListViewItem
LvIb = (ListViewItem) b;
string Ca
= LvIa.SubItems[ColumnToSort].Text;
string Cb
= LvIb.SubItems[ColumnToSort].Text;
//
//
Compare the two strings
//
Result
= ObjectCompare.Compare(Ca, Cb);
if
( FixedStringForLastItemValue == null ) {
//
nothing
}
else {
if
( Cb == FixedStringForLastItemValue ) {
return
-1;
}
}
//
//
if ( the strings contains numbers, correct the sequence. for example:
//
3, 20, 1 has to be sorted as 1, 3, 20 and not 1, 20, 3
//
Double Da, Db;
try
{
Da =
Double.Parse(Ca);
Db =
Double.Parse(Cb);
if ( Result != 0 )
{
if ( Da
> Db )
{
Result
= 1;
}
else
{
Result
= -1;
}
}
}
catch
{
// Ca or Cb are not
numeric ==> nothing to do
}
//
//
The return value depends on the sort order
//
if ( OrderOfSort ==
SortOrder.Descending)
{
// Descending sort
is desired, compare result is to be
// turned in the
negative
Result = -Result;
}
return Result;
}
public void VisualizeOrder()
{
//
//
The sort column and order are visualized by a little triangle,
//
which is to be drawn right to the columns name
//
if
( ColumnToSort == -1 ) {
return;
}
//
//
Draw the triangle in the header. Before that,
//
make theold triangle invisible. After that, store the
//
position of the triangle for usage at the next time.
//
if
( SortColumnOld > -1 && SortColumnOld != ColumnToSort ) {
//
//
Setting the text causes a repaint of the header
//
MyListViewValue.Columns[SortColumnOld].Text
=
MyListViewValue.Columns[SortColumnOld].Text.TrimEnd();
//
}
if
( OrderOfSort == SortOrder.None ) {
SortColumnOld
= -1;
}
else {
Graphics
Grx;
Grx
= Graphics.FromHwnd(HeaderHandle);
Label LblMeasureHeader
= new Label();
LblMeasureHeader.Font
= MyListViewValue.Font;
LblMeasureHeader.AutoSize
= true;
if
( MyListViewValue.Columns[ColumnToSort].TextAlign !=
HorizontalAlignment.Left
) {
if
( MyListViewValue.Columns[ColumnToSort].Text.TrimEnd() ==
MyListViewValue.Columns[ColumnToSort].Text
) {
MyListViewValue.Columns[ColumnToSort].Text
+= " ";
}
}
LblMeasureHeader.Text
= MyListViewValue.Columns[ColumnToSort].Text;
int
x, y, i;
Point[]
Triangle = new Point[3];
//
//
Evaluate the length of the Name
//
x
= LblMeasureHeader.Width + 13;
//
//
Keep a minimum distance of the right bounce
//
if
( x + 15 > MyListViewValue.Columns[ColumnToSort].Width ) {
x
= MyListViewValue.Columns[ColumnToSort].Width - 13;
}
//
//
Sum the width of all columns left to the sort column
//
for
(i = 0; (i < ColumnToSort); i++)
{
x
+= MyListViewValue.Columns[i].Width;
}
//
//
Top is determined by the text height
//
y
= LblMeasureHeader.Height / 2 + 2;
//
//
The three points of the triangle are depending on
//
the sort order
//
if
( OrderOfSort == SortOrder.Ascending ) {
Triangle[0].X
= x - 1;
Triangle[0].Y
= y + 4;
Triangle[1].X
= x + 9;
Triangle[1].Y
= y + 4;
Triangle[2].X
= x + 4;
Triangle[2].Y
= y - 2;
}
else {
Triangle[0].X
= x;
Triangle[0].Y
= y - 1;
Triangle[1].X
= x + 9;
Triangle[1].Y
= y - 1;
Triangle[2].X
= x + 4;
Triangle[2].Y
= y + 4;
}
Grx.FillPolygon(SystemBrushes.ControlDark,
Triangle);
SortColumnOld
= ColumnToSort;
Grx.Dispose();
}
}
private
int SortColumnOld = -1;
private
System.IntPtr HeaderHandleValue;
}
#endregion
}
}
|