📄 listviewfilter.cs
字号:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ListViewFilter
{
#region Namespace specific enumerations
/// <summary>
/// Data type of column content
/// </summary>
public enum LVFDataType
{
String,
Number,
Date
}
/// <summary>
/// Type of comparison requested by the filter
/// </summary>
public enum LVFFilterType
{
Equal,
NotEqual,
Greater,
GreaterEqual,
Less,
LessEqual
}
#endregion
/// <summary>
/// This is a specialization of a ListView control that defaults to
/// details mode and includes the ability to filter data on column
/// headers and to specify column data sort comparison type.
/// </summary>
public class ListViewFilter : System.Windows.Forms.ListView
{
#region Private class data
private bool hdr_filter = false; // Display column filters
private int srt_column = 0; // Sort column
private bool srt_sorder = true; // Sort ascending/descending
private LVFDataType srt_datype = LVFDataType.String; // Sort data type comparison
private bool flt_ignore = false; // Ignore case on filter
private Color col_scolor = Color.WhiteSmoke; // Shade color
private bool col_shaded = true; // Display shade color
private ArrayList itm_filtrs = new ArrayList(); // Array of LVFFliters active
private ArrayList itm_filtrd = new ArrayList(); // Filtered out items
private ArrayList itm_holder = new ArrayList(); // Held out items
private int mnu_column = 0; // Filter button column
private float cmp_float1 = float.MaxValue; // float value for compare
private float cmp_float2 = float.MaxValue; // float value for compare
private DateTime cmp_datim1 = DateTime.MaxValue; // date value for compare
private DateTime cmp_datim2 = DateTime.MaxValue; // date value for compare
/// <summary>
/// Instance pointer to the header control interface
/// </summary>
private ListViewFilterHeader hdr_contrl = null;
/// <summary>
/// Filter data structure for individual column data.
/// </summary>
[StructLayout(LayoutKind.Sequential)] internal struct LVFFilter
{
internal int Column;
internal LVFFilterType Compare;
internal LVFDataType Type;
internal string Text;
}
/// <summary>
/// This is the context menu and items that appear when a filter
/// button is clicked. This allows customizations for a column.
/// </summary>
private ContextMenu mnu_filter = new ContextMenu();
private MenuItem mnu_datype = new MenuItem( "&Data type" );
private MenuItem mnu_alignt = new MenuItem( "&Alignment" );
private MenuItem mnu_clearf = new MenuItem( "&Clear filter" );
private MenuItem mnu_ignore = new MenuItem( "&Ignore case" );
private MenuItem mnu_strflt = new MenuItem( "&String" );
private MenuItem mnu_nbrflt = new MenuItem( "&Number" );
private MenuItem mnu_datflt = new MenuItem( "&Date" );
private MenuItem mnu_alignl = new MenuItem( "&Left" );
private MenuItem mnu_alignr = new MenuItem( "&Right" );
private MenuItem mnu_alignc = new MenuItem( "&Center" );
#endregion
#region Windows API interfaces
[DllImport("user32.dll")] internal static extern int SendMessage( IntPtr hWnd, W32_LVM msg, int wParam, int lParam );
[DllImport("user32.dll")] internal static extern int SendMessage( IntPtr hWnd, W32_LVM msg, int wParam, ref LVITEM hditem );
[DllImport("user32.dll")] internal static extern int SendMessage( IntPtr hWnd, W32_LVM msg, int wParam, ref RECT hditem );
#endregion
#region Constructor and Dispose methods
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
/// <summary>
/// Constructor, nothing done here but InitializeComponent as usual.
/// </summary>
public ListViewFilter()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">True if really disposing?</param>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#endregion
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// NOTE: We have modified this method to include the context
/// menu and items creation as if this was a form control.
/// </summary>
private void InitializeComponent()
{
//
// ListViewFilter
//
this.Name = "ListViewFilter";
this.View = View.Details;
this.ListViewItemSorter = new ListViewFilterSorter( this );
//
// mnu_filter
//
this.mnu_filter.MenuItems.AddRange( new System.Windows.Forms.MenuItem[]
{ this.mnu_datype, this.mnu_alignt, this.mnu_clearf, this.mnu_ignore } );
this.mnu_filter.Popup += new System.EventHandler( this.ContextMenuPopup );
//
// mnu_datype
//
this.mnu_datype.Index = 0;
this.mnu_datype.MenuItems.AddRange( new System.Windows.Forms.MenuItem[]
{ this.mnu_strflt, this.mnu_nbrflt, this.mnu_datflt } );
//
// mnu_alignt
//
this.mnu_alignt.Index = 1;
this.mnu_alignt.MenuItems.AddRange( new System.Windows.Forms.MenuItem[]
{ this.mnu_alignl, this.mnu_alignr, this.mnu_alignc } );
//
// mnu_clearf
//
this.mnu_clearf.DefaultItem = true;
this.mnu_clearf.Index = 2;
this.mnu_clearf.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_refrsh
//
this.mnu_ignore.Index = 3;
this.mnu_ignore.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_strflt
//
this.mnu_strflt.Index = 0;
this.mnu_strflt.RadioCheck = true;
this.mnu_strflt.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_nbrflt
//
this.mnu_nbrflt.Index = 1;
this.mnu_nbrflt.RadioCheck = true;
this.mnu_nbrflt.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_datflt
//
this.mnu_datflt.Index = 2;
this.mnu_datflt.RadioCheck = true;
this.mnu_datflt.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_alignl
//
this.mnu_alignl.Index = 0;
this.mnu_alignl.RadioCheck = true;
this.mnu_alignl.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_alignr
//
this.mnu_alignr.Index = 1;
this.mnu_alignr.RadioCheck = true;
this.mnu_alignr.Click += new System.EventHandler( this.MenuItemClick );
//
// mnu_alignc
//
this.mnu_alignc.Index = 2;
this.mnu_alignc.RadioCheck = true;
this.mnu_alignc.Click += new System.EventHandler( this.MenuItemClick );
}
#endregion
#region Overridden System.Windows.Forms.Control methods
/// <summary>
/// When the handle changes we need to recreate the Filter
/// Header instance. The previous (if any) header instance
/// was tied to a handle that is now gone.
/// </summary>
/// <param name="e">Event</param>
protected override void OnHandleCreated(System.EventArgs e)
{
base.OnHandleCreated(e);
// now that we have a handle, create the HeaderControl instance
hdr_contrl = new ListViewFilterHeader( this, hdr_filter,
srt_column, srt_sorder );
}
/// <summary>
/// When our handle is removed we give the hdr_contrl a little
/// help by telling him now is a good time to release its handle.
/// From now on, we do not have a hdr_contrl instance active
/// so we set hdr_contrl to null and don't use it until reset.
/// </summary>
/// <param name="e">Event</param>
protected override void OnHandleDestroyed(System.EventArgs e)
{
// free and then delete the ListViewFilterHeader control
if ( hdr_contrl != null )
{
hdr_contrl.ReleaseHandle();
hdr_contrl = null;
}
// do this AFTER we tell the ListViewFilterHeader to release
base.OnHandleDestroyed(e);
}
/// <summary>
/// Only three windows messages are trapped here: WM_ERASEBKGND
/// since the OnPaintBackground doesn't work for the ListView,
/// WM_NOTIFY so that we can get all the ListViewFilterHeader
/// notificiations, and OCM_NOTIFY for our own notifications.
/// </summary>
/// <param name="m">Message</param>
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
// determine if we want to process this message type
switch ( m.Msg )
{
// erase background causes us to repaint the shaded sort column.
case (int)W32_WM.WM_ERASEBKGND:
// there has to be a header and we have to be in Details mode
if ( col_shaded && ( hdr_contrl != null ) && ( this.View == View.Details ) )
{
// get the 'fake' size information from the header
// column. this is only the Width and Left (as Height).
Size z = hdr_contrl.SizeInfo[ srt_column ];
// get a temporary graphics object to fill the rectangle.
// NOTE: it would be nice to g.ReleaseHdc( m.WParam ) but
// it doesn't work for some reason, it may not be needed...
Graphics g = Graphics.FromHdc( m.WParam );
g.FillRectangle( new SolidBrush( col_scolor ),
z.Height, Top, z.Width, Height );
}
break;
// notify messages can come from the header and are used
// for column sorting and item filtering if wanted
case (int)W32_WM.WM_NOTIFY:
// get the notify message header from this message LParam
NMHEADER h1 = (NMHEADER)m.GetLParam( typeof( NMHEADER ) );
// process messages ONLY from our header control
if ( ( hdr_contrl != null ) && ( h1.hdr.hwndFrom == hdr_contrl.Handle ) )
NotifyHeaderMessage( h1 );
break;
// internal ListView notify messages are reflected to us via OCM
case (int)W32_OCM.OCM_NOTIFY:
// get the notify message header from this message LParam
NMHEADER h2 = (NMHEADER)m.GetLParam( typeof( NMHEADER ) );
// process ONLY messages that are to this ListView control
if ( h2.hdr.hwndFrom == Handle )
{
// process only specific ListView notify messages
switch ( h2.hdr.code )
{
// draw message, we return a result for the drawstate
case (int)W32_NM.NM_CUSTOMDRAW:
NMLVCUSTOMDRAW d = (NMLVCUSTOMDRAW)m.GetLParam( typeof( NMLVCUSTOMDRAW ) );
m.Result = (IntPtr)NotifyCustomDraw( d );
break;
}
}
break;
}
}
#endregion
#region Private methods
/// <summary>
/// NM_CUSTOMDRAW processing for the ListView control. Here all
/// we really want to do is redraw the shaded background for the
/// column that is currently sorted, let the base do the work.
/// </summary>
/// <param name="d">NMLVCUSTOMDRAW to process</param>
/// <returns>Return the result of the draw stage</returns>
private W32_CDRF NotifyCustomDraw( 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)W32_CDDS.CDDS_PREPAINT:
if ( col_shaded && ( this.View == View.Details ) )
return W32_CDRF.CDRF_NOTIFYITEMDRAW;
break;
// next request, ask for each sub item notification for this item
case (int)W32_CDDS.CDDS_ITEMPREPAINT:
return W32_CDRF.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)W32_CDDS.CDDS_SUBITEMPREPAINT:
// ensure that this is a valid item/subitem request
if ( d.nmcd.dwItemSpec < this.Items.Count )
{
// get a reference to the item to be rendered
ListViewItem i = this.Items[ d.nmcd.dwItemSpec ];
// is this for a a base item set it's backcolor
if ( d.iSubItem == 0 )
i.BackColor = ( d.iSubItem == srt_column )
? col_scolor : this.BackColor;
// ensure that the subitem exits before changing it
else if ( ( d.iSubItem < i.SubItems.Count ) &&
( i.SubItems[ d.iSubItem ] != null ) )
i.SubItems[ d.iSubItem ].BackColor =
( d.iSubItem == srt_column ) ? col_scolor : this.BackColor;
}
break;
}
// let default drawing do the actual rendering
return W32_CDRF.CDRF_DODEFAULT;
}
/// <summary>
/// When the header control sends a notification we need to
/// process the click to sort, button click for options menu,
/// and filter change to update the items currently visible.
/// </summary>
/// <param name="h">NMHEADER</param>
private void NotifyHeaderMessage( NMHEADER h )
{
// process only specific header notification messages
switch ( h.hdr.code )
{
// a header column was clicked, do the sort
case (int)W32_HDN.HDN_ITEMCLICKA:
case (int)W32_HDN.HDN_ITEMCLICKW:
hdr_contrl.SortColumn = srt_column = h.iItem;
srt_sorder = hdr_contrl.SortOrder;
srt_datype = hdr_contrl.DataType[ srt_column ];
this.Sort();
break;
// a filter button was clicked display the popup menu
// to handle setting filter options for the column
case (int)W32_HDN.HDN_FILTERBTNCLICK:
mnu_column = h.iItem;
mnu_filter.Show( this, PointToClient( MousePosition ) );
break;
// a filter content changed, update the items collection
case (int)W32_HDN.HDN_FILTERCHANGE:
// if this is for item -1 then this is a clear all filters
if ( h.iItem < 0 ) itm_filtrs.Clear();
// if we are filtered this is a real filter data change
else if ( hdr_filter ) FilterBuild( h.iItem );
// update the items array with new filters applied
FilterUpdate();
break;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -