/// <summary>
/// The class extends the functionality of a DataGridView with filtering
/// </summary>
[Serializable()]
[ToolboxBitmap(typeof(DataGridView))]
public partial class FilterableDataGridView : DataGridView
{
/// <summary>
/// A collection to store the filters
/// </summary>
/// <remarks>
/// ObservableCollection<T> requires .NET 4.0
/// </remarks>
private ObservableCollection<FilterItem> _filters;
/// <summary>
/// Property to get or set the collection of the filters
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ObservableCollection<FilterItem> Filters
{
get
{
return _filters;
}
set
{
_filters = value;
if (_filters != null)
{
// Subscribe to the CollectionChanged event, so the filtering can be done automatically
_filters.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_filters_CollectionChanged);
}
}
}
/// <summary>
/// The event handler of the filters CollectionChanged event,
/// so the filtering can be automatic when the collection changes
/// </summary>
/// <param name="sender">sender</param>
/// <param name="e">Event arguments</param>
void _filters_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//If a new element is added
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (FilterItem fi in e.NewItems)
{
//subscribe to its event, so filtering will be done automatically every time when the filter changes
fi.FilterChanged += Filter;
}
}
//If the filter is removed
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
foreach (FilterItem fi in e.OldItems)
{
//unsubscribe from its event
fi.FilterChanged -= Filter;
}
}
//Finally filter the list
Filter();
}
/// <summary>
/// Constructor
/// </summary>
public FilterableDataGridView()
{
InitializeComponent();
Filters = new ObservableCollection<FilterItem>();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="container"></param>
public FilterableDataGridView(IContainer container)
{
container.Add(this);
InitializeComponent();
Filters = new ObservableCollection<FilterItem>();
}
/// <summary>
/// Method to filter the DataGridView
/// It gets called whenever the filters collection changes or a filter changes.
/// Explicit call is possible, however not necessary.
/// </summary>
public void Filter()
{
//Set the selected cell to null, so every cell(/row) can be hidden
this.CurrentCell = null;
//Check every row in the DataGridView
foreach (DataGridViewRow row in Rows)
{
bool visible = true;
//Check every FilterItem
foreach (FilterItem fi in _filters)
{
//The char used to delimit the columns from each other
// it is also used to denote an OR relation ship between the filter texts
char c = '|';
//Split the string to column names
List<string> columns = new List<string>(fi.FilterColumn.Split(c));
List<string> filters = new List<string>(fi.Filter.Split(c));
bool atLeastOneContains = false;
//Check every columns
foreach (string column in columns)
{
foreach (string filter in filters)
{
if (row.Cells[column].Value != null && row.Cells[column].Value.ToString().ToUpper().Contains(filter.ToUpper()))
{
//If the column contains any of the filter texts, the filter is satisfied
atLeastOneContains = true;
break;
}
}
if (atLeastOneContains)
{
//If the column contains the filter text, the filter is satisfied
break;
}
}
//If none of the columns contain the text, the row can't be visible
if (!atLeastOneContains)
{
visible = false;
break;
}
}
//Set the Visible property the Row
row.Visible = visible;
}
}
}