Click here to Skip to main content
Click here to Skip to main content

DataGridView Filter Popup

By , 20 Mar 2009
 

DataGridViewColumnSelector

Introduction

I was looking for an easy and flexible grid filtering mechanism to use with new and old applications. I've found no preexisting solutions that fully satisfy my needs. So, I decided to make my own filtering library. The goals I've tried to reach are:

  • Easy to integrate: Availability of a "few lines of code" usage to satisfy the needs of rapid integration.
  • User Friendly: Nice to look, easy to use.
  • Not code pervasive: Using a DataGridView derivation would constrain people to re-declare their instances. Moreover, this could be in conflict with existing grid derivations.
  • Flexible: There are never enough filters in the world!

Using the Code

This is for eager people. The promised "few lines of code" are just one. Add the DgvFilterPopup.dll to your references. Write somewhere a line like this:

DgvFilterManager filterManager = new DgvFilterManager(dataGridView1);

That's all. Your grid is now able to filter the column values. Right-click on the column headers to see a popup with different filtering features, based on the clicked column data type.

Note: Your grid must be data-bound to a DataView, DataTable, or a BindingSource resolving to one of these two.

Class Architecture

The three main classes are exposed in the following diagram:

DataGridViewColumnSelector

The DgvFilterManager Class

The DgvFilterManager class doesn't provide a user interface. Its work is to coordinate the interaction between the DataGridView and the visual filtering elements. When you assign a DataGridView to a DgvFilterManager, the latter attaches some handlers to respond to right click on column headers and to perform some custom painting on the grid. When the user right clicks a column header, the DgvFilterManager shows a popup near the column. This popup is a control that serves as a host for other controls, one for each column. Only one of these child controls is visible at a time, based on the clicked column. We have one filter host control and many column filter child controls.

The filter host control must be a derivation of the DgvBaseFilterHost class, while filter controls must be derived from the DgvBaseColumnFilter class. These two classes don't provide any user interface themselves.

As a default, the DgvFilterManager uses a standard host implementation, named DgvFilterHost, and depending on each column type and data type, one of the filter standard implementations (see below): DgvTextBoxColumnFilter, DgvCheckBoxColumnFilter, DgvComboBoxColumnFilter, or DgvDateColumnFilter.

When a DataGridView is attached to the manager, the latter performs the following actions:

  • It creates a filter host that is an instance of the DgvFilterHost class. If you have already provided a filter host, this step is skipped.
  • It creates a list of DgvBaseColumnFilters, one per column, and initializes each element to a specialization of DgvBaseColumnFilter. If AutoCreateFilters is false, this step is skipped.

You can force a specific column filter type for a certain column, intervening in this process through the ColumnFilterAdding event. You can also intervene, after the auto-creation process, accessing the filter instances through one of the two indexers provided by the manager, and replacing them with user-chosen instances.

The DgvBaseFilterHost Class

The purpose of the filter host control is to show a popup near a right-clicked column and to host child column filter controls. When the popup is shown, only the column filter control related to the right-clicked column is visible. DgvBaseFilterHost is a derivation of UserControl, and provides functionalities to cooperate with DgvFilterManager.

Note: This class is intended as an abstract class. However, declaring it as abstract would generate errors within the designer when designing derived classes.

In your derivation, you have to provide a host area (such as a panel) and override the FilterClientArea to return it. Also, create visual elements for remove filter, remove all filters, apply filter, and use the DgvFilterManager methods ActivateFilter and ActivateAllFilters to make them alive.

The DgvBaseColumnFilter Class

The purpose of a column filter control is to contain visual elements, allowing the end user to construct a filter. When inheriting from it, you can work just like creating any other user control. This class is a derivation of UserControl, and provides functionalities to cooperate with DgvFilterManager.

Note: This class is intended as an abstract class. However, declaring it as abstract would generate errors within the designer when designing derived classes.

You should override OnFilterExpressionBuilding to provide a filter expression construction logic and to set the values of the FilterExpression and FilterCaption properties.

Standard Implementations

DataGridViewColumnSelector

The DgvFilterHost Class

DataGridViewColumnSelector

This is the standard implementation of DgvBaseFilterHost. This class does nothing special. Most of the logic is in its base class. It just contains visual elements such as buttons and graphics, and a panel acting as the client area for child column filter controls.

The DgvTextBoxColumnFilter Class

DataGridViewColumnSelector

This is one of the DgvBaseColumnFilter standard implementations. It's composed of a combobox containing a list of operators and a textbox in which to type the value of the filter. This column filter is used by default with DataGridViewTextBoxColumns, except when the bound data type is DateTime. The list of available operators is different between string types and numeric types.

The DgvTextBoxColumnFilter Class

DataGridViewColumnSelector

A standard implementation for the filtering of checkbox columns. The only available operators are the equal and the general null and not null operators.

The DgvDateColumnFilter Class

DataGridViewColumnSelector

A standard implementation for the filtering of date columns.

The DgvComboBoxColumnFilter Class

DataGridViewColumnSelector

A standard implementation for the filtering of combobox columns. By default, on textbox columns, the filter manager uses DgvTextBoxColumnFilter instances. However, you can force an instance of DgvComboBoxColumnFilter on such columns. In this case, the DgvComboBoxColumnFilter instance automatically creates a distinct list of values from the column data. You should do an explicit call to the RefreshValues() method when the underlying data changes.

Customizing

If "one line usage" is not sufficient for your needs, you can control the process of adding and showing filters in different ways.

Using a DgvComboBoxColumnFilter for Non-Combobox Columns

Use one of the manager indexers to access the filter, and assign it an instance of the DgvComboBoxColumnFilter class.

DgvFilterManager fm = new DgvFilterManager(dataGridView1);
fm["CustomerID"] = new DgvComboBoxColumnFilter();

Using Events

ColumnFilterAdding

Using this manager event, you may force your preferred filter before the manager creates the predefined filter. The event is raised for each column in the grid when you set the DataGridView property.

  ...
  DgvFilterManager fm = new DgvFilterManager();
  fm.ColumnFilterAdding += new ColumnFilterEventHandler(fm_ColumnFilterAdding);
  fm.DataGridView = dataGridView1; // this raises ColumnFilterAdding events
  ...

void fm_ColumnFilterAdding(object sender, ColumnFilterEventArgs e) {
  if (e.Column.Name == "CustomerID") {
    e.ColumnFilter = new DgvComboBoxColumnFilter();
  }
}

PopupShowing

This manager event allows you to customize the filter host position when popped up.

  ...
  DgvFilterManager fm = new DgvFilterManager();
  fm.DataGridView = dataGridView1;
  // Customize the popup positioning.
  fm.PopupShowing += new ColumnFilterEventHandler(fm_PopupShowing);
  ...

void fm_PopupShowing(object sender, ColumnFilterEventArgs e) {
  DgvFilterManager fm = ((DgvFilterManager)sender);
  Rectangle HeaderRectangle = 
    fm.DataGridView.GetCellDisplayRectangle(e.Column.Index,-1,true);
  //Show the popup under the column header
  fm.FilterHost.Popup.Show(fm.DataGridView, HeaderRectangle.Left, 
                           HeaderRectangle.Bottom);
  e.Handled = true;
}

FilterExpressionBuilding

DataGridViewColumnSelector

Using the DgvBaseColumnFilter event, you can customize the filter expression building process. In the following code example, we add new operators and then manage them in the event handler. The FilterExpression and FilterCaption properties will be used by the manager to build the whole filter and to set the column caption.

  ...
  DgvDateColumnFilter OrderDate;
  ...
  DgvFilterManager fm = new DgvFilterManager(dataGridView1);
  fm.DataGridView = dataGridView1; //after this line, column filters are created

  // Get the created column filter for OrderDate column
  OrderDate = ((DgvDateColumnFilter)fm["OrderDate"]);

  //Add some new operators
  OrderDate.ComboBoxOperator.Items.Insert(0, "This year");
  OrderDate.ComboBoxOperator.Items.Insert(1, "1 year ago");
  OrderDate.ComboBoxOperator.Items.Insert(2, "2 years ago");

  //Add an handler
  OrderDate.FilterExpressionBuilding += 
    new CancelEventHandler(OrderDate_FilterExpressionBuilding);
  
  ...
  
void OrderDate_FilterExpressionBuilding(object sender, CancelEventArgs e) {
  int index = OrderDate.ComboBoxOperator.SelectedIndex;
  if (index < 3) { // the first 3 are the new operators
    int year = (DateTime.Today.Year - index);
    OrderDate.FilterExpression = "(OrderDate>='" + year.ToString() + "-1-1' " 
                               + "AND OrderDate<='" + year.ToString() + "-12-31') ";
    OrderDate.FilterCaption = OrderDate.OriginalDataGridViewColumnHeaderText 
                            + "\n = year " + year.ToString();
    e.Cancel = true;
  }
}

Subclassing

A more powerful way to customize your filters is through subclassing. You should think of the proposed standard implementations of DgvBaseFilterHost and DgvBaseColumnFilter as just some possible implementations.

Creating Your Own Host

DataGridViewColumnSelector

As said above, derive from DgvBaseFilterHost and provide some visual elements. Add a container within your control to host the child filter controls, and return it by an override of the FilterClientArea property. The base class provides the necessary logic to cooperate with the manager, and provides some facilities helping to position the child filter controls and to adjust the host size. Another facility simplifies the creation of transparent skinned hosts, thanks to the method BitmapToRegion I've found in a very nice article by John O'Byrne.

Note: A skinned host must be constrained to a fixed size. Be sure to inhibit the resize logic by overriding the DoAutoFit method. Also, keep in mind this limitation when designing your own host and your filters.

DgvFilterManager fm = new DgvFilterManager();
fm.FilterHost = new CustomizedFilterHost();
fm.DataGridView = dataGridView1;

Creating Your Own Column Filters

DataGridViewColumnSelector

Creating new column filters is simple. Derive from DgvBaseColumnFilter and add your visual elements. Override OnFilterExpressionBuilding to provide filter building logic and, using DataView.RowFilter rules, assign a value to the FilterExpression property and a title to the FilterCaption property.

Remember that the filter is applied when the user clicks on the OK button of the host. However, you can obtain an immediate filter application doing a call to the RebuildFilter method of the filter manager.

New Filters

To satisfy some requests, in the 1.1.0.0 update, I've introduced three new filter implementations:

DataGridViewColumnSelector

The DgvMonthYearColumnFilter Class

DataGridViewColumnSelector

This filter allows the user to select a month and a year. By setting the YearMin and YearMax properties, you can control the shown years range. Month names default to English, but you may provide culture-specific names by once setting the value of the static property MonthCsvList with a comma separated list of month names.

The DgvNumRangeColumnFilter Class

DataGridViewColumnSelector

Use this filter to allow the user to specify a range filter on numeric columns.

The DgvDateRangeColumnFilter Class

DataGridViewColumnSelector

Use this filter to allow the user to specify a range filter on date columns.

Conclusions

In this article, I've exposed the class architecture and common usage scenarios. This conceptual overview, I hope, will help you understand how it works. For detailed explanations and references, you can see the attached documentation.

Note: To those interested in documenting their works, I've used these materials:

History

  • 1.1.0.0 (19 Mar 2009)
    • Added three new embedded filters: DgvDateRangeColumnFilter, DgvMonthYearColumnFilter, and DgvNumRangeColumnFilter.
    • It's now possible to dynamically change the DataSource.
  • 1.0.0.1 (04 Mar 2009)
    • Fix: Filters are now applied when the grid is bound to a BindingSource which uses a DataSet as the first source.
  • 1.0.0.0 (01 Mar 2009)
    • Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Vincenzo Rossi
Software Developer (Senior)
Italy Italy
Member
I'm a graduate in Computer Science.
I work with C++, Visual Basic 6, C#, asp, asp.Net, Windows Forms, SQL Server, Access, Flash.
 


"Short code, good code"



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDgvComboBoxColumnFilter on Column with zero rows-> ExceptionmemberMember 420174413 May '13 - 21:23 
Hi there little issue perhaps.
Clicking on the OkayButton of a DgvComboBoxColumnFilter in a column without
entries, throws an Exception.
How can i avoid this.
Thanks for reading
QuestionHow to Remove the link between dgvgridview filter popmemberVyas Darshan10 May '13 - 19:33 
Hi Vicenzo,
 
I am using in one my application for filtering data for list of items, one my internal user asked me that can he make the filtering on the fly.
 
I mean at the Form_Load Event i have added as code below.
 
new dgvFilterManager(gvItemList);
 
It works fine but as per the feedback it gives error when trying to use data from grid in the application, i tried analysing the issue and found that user is applying the filter but which clicking OK (Green Tick) on FilterPopup window he trying to use the data which is not matching with the column name hence it is generating the error.
 
I had question that can i have CheckBox making the dgvFilterPopup enable and disable on grid.
 
Please reply.
 
Awaiting..
 
Thanks
Darshan
QuestionNeed Help to Complete My Project With your Filter ?memberKalaiPondy3 May '13 - 1:47 
i tried ur concept in my project, but it doesn't work for me. Problem is while right click in datagridview it doesn't calls any user interface for filter reference.what is the problem also i can't find in my project if u like to see my project . I can attach and sent mail to ur after getting reply for this message . can u help me . I'm new to datagridview ....
 
Kindly Regards.
kalaipondy22@gmail.com

QuestionGet dates from DgvDateRangeColumnFilter?memberNaumHN4 Apr '13 - 2:49 
Hi,
does enyone knows how I can get values from datetimePickers from DgvDateRangeColumnFilter.
Thanks
QuestionHow to update datasource?memberNaumHN3 Apr '13 - 4:31 
Hi,
I'm new here,
i found this control an I'm very happy with.
I'm wondering how to update datasource (dataTable) based on filters I apply on dataGridView. Because I won't to use that datatable as source for printing.?
Thank You very much
QuestionVS 2012 ErrorsmemberMember 995409931 Mar '13 - 8:14 
Interop Assembly errors,
 
I'm using vb.net
 
The code I used is:
 
Dim DgvFilterManager as New DgvFilterPopup.DgvFilterManager(DataGridView1)
 
It works fine in VS2008 but in VS2012 I get Interop Assembly errors and Object Reference not set to an instance of an Object.
 
Why did this work in VS2008 without a hitch but not in VS2012?
GeneralMy vote of 5memberMember 994926828 Mar '13 - 2:44 
Awesomeness! Just what I needed.
GeneralMy vote of 5memberSonam K6 Mar '13 - 1:36 
Amazing!
GeneralMy Vote of 5memberbaek hum kyung14 Feb '13 - 19:21 
It's excellent!!!!
GeneralMy Vote of 5membermb-20125 Jan '13 - 23:01 
Excelent, thank you very much!
GeneralMy vote of 5memberpl_oliver21 Jan '13 - 3:11 
Works perfectly!
QuestionDefault valuesmemberScorerpl13 Jan '13 - 22:07 
Could someone tell me how to easy set default values of filter to not load all data on start of the program?
GeneralThank youmemberesperento5730 Dec '12 - 1:25 
Just for thank you. Really really Nice Job!
GeneralExcellent job !memberadinfo28 Dec '12 - 1:16 
Thanks a lot.
Just a reference, and one line, it's perfect.
 
One trick
If you have (as me) to reload grid with another datasource (more or less columns for example), you need to do this :
After first populate grid (and after change columns headers if you want to) :
FilterAuto = new DgvFilterManager(DGVDefaut);
When you change the datasource :
FilterAuto.ActivateAllFilters(false);
Change your source here
FilterAuto.RebuildFilter();
 
Best Regards
Frederic
QuestionExcellent and FantasticmemberInfyBalu3 Dec '12 - 22:50 
Dear Vincenzo, This is one of the most awesome ready to use feature that I have seen. You are rocking dude. Thank you very much, now that I can plug and play with this...
 
Hurray....
QuestionVS2010membermihai23420 Nov '12 - 18:15 
Hey , great job.
However, the filtering dones't work with VS2010 (C#). Any solution/though?
 
thanks
AnswerRe: VS2010membermihai23421 Nov '12 - 6:19 
Nevermind. It works with dataviews.I used a dataset before.
Question5+++memberqwerty7775719 Nov '12 - 18:02 
Excellent Filter Tool.
Thanks for your work.
GeneralMy vote of 5membernanmosiam12 Nov '12 - 16:26 
Very Good Idea,Helpfull
QuestionExcellent work. One QuestionmemberBill Dickinson19 Oct '12 - 9:45 
Thank you for putting together this wonderful extension. After adding the filter to a datagridview in one of my apps, I noticed that the text associated with a query replaces the column header in the datagridview after selecting the Ok button (with the filter on). Removing the filter does not remove the query text from the datagridview column header - in other words, the original column header text is not restored. Have you experienced something like this? I'm stepping through the code to determine why this is happening.
 
Either way, this is a great piece of code. Thanks.
Bill Dickinson

QuestionBrilliant!!!memberMember 949451115 Oct '12 - 20:42 
Big Grin | :-D Big Grin | :-D Big Grin | :-D Big Grin | :-D Big Grin | :-D
 
Excellent Work.
QuestionSo awesome!membermishamosherg11 Oct '12 - 7:40 
This thing is GREAT!
 
LOT of thanks for this!
Generalawosmemembersariqkhan7 Oct '12 - 2:47 
awsom man
thank you for this
you have solved many things of mine.
thank you
QuestionFilter for Unbound Data itemsmemberJpBaluChennai3 Oct '12 - 11:15 
Hi , as you mentioned, filter is working when we bind a grid with some datasource.
but i m creating custom columns and user can enter the data in gridview. in those cases. column filter is not showing at the top of the screen.
 
did u made any changes or written code for the same ?????????????
in my grid i have many combobox columns, link columns. based on combo box value, opening some pop-up window. if i convert my grid to datatable and set source. it is not allowing me to insert new rows. or datagridview.rows.clear().
QuestionCustom collections [modified]memberFernando Walter Gagni30 Sep '12 - 21:56 
I tried your great and usefull component but now i'm approaching to a serious problem. I will use CUSTOM COLLECTIONS in my app and I noticed that your component works with Datasets but no with Custom Collections. Have you got any suggestions in order to make component workable in such situation, please?
The same question is also related to your other component "DataGridView Column Selector".
Thanks for your attention.

modified 1 Oct '12 - 4:06.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 20 Mar 2009
Article Copyright 2009 by Vincenzo Rossi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid