Recently, a reader asked how to detect filter changes with the code in Building a Drop-Down Filter List for a DataGridView Column Header Cell. That article describes how to derive from DataGridViewColumnHeaderCell, so it seems like a trivial matter to add a FilterChanged event that occurs whenever the user changes a value. However, this solution has a number of drawbacks, the primary one being that it doesn't work. The DataGridView control uses cloning in many of its internal operations, so the cell that the user interacts with is not the cell that you've attached an event handler to.

Besides, why should you attach handlers to all those cells when the changes affects the entire DataGridView? The example in the article uses a DataGridView.DataBindingComplete event handler in the Form code to update the filter status label (the label that says things like "13 of 42 records found"). This could work, but the DataBindingComplete event occurs whenever the data source changes in any way and the grid needs updating. This is no problem for a filter status label that automatically ignores irrelevant changes, but a filter changed event needs more precision, and you would have to add code to ignore things like sorting.

The BindingSource class does not have a FilterChanged event, but fortunately, its Filter property is virtual, so we can add a FilterChanged event to a derived class, as shown in the following code. Note that the code checks the RaiseListChangedEvents property before raising the event; this is necessary to avoid problems when the filter is temporarily changed in order to calculate values like the status text.

        class BindingSourceWithFilterChanged : BindingSource

        {

            public BindingSourceWithFilterChanged(object dataSource, string dataMember) :

                base(dataSource, dataMember) { }

 

            public event EventHandler FilterChanged;

 

            protected virtual void OnFilterChanged(EventArgs e)

            {

                if (FilterChanged != null && RaiseListChangedEvents)

                {

                    FilterChanged(this, e);

                }

            }

 

            public override string Filter

            {

                get

                {

                    return base.Filter;

                }

                set

                {

                    OnFilterChanged(EventArgs.Empty);

                    base.Filter = value;

                }

            }

        }

So the solution is to replace the BindingSource object in your Form code with a BindingSourceWithFilterChanged object, and then handle its FilterChanged event. (And in case event ordering is import for your project, the FilterChanged event occurs before the DataGridView.RowsAdded and DataBindingComplete events that also occur when the user changes a filter value.)