If you are familiar with the RadGridView‘s Custom Filtering Functionality you probably know that you can create any kind of user control to replace the default one which looks like this:
Very often, however, you may be perfectly happy with the stock filtering control, but you wish you could modify and adapt it just a little bit to match your particular requirements. What should you do then? Maybe build an entirely new filtering control from scratch that looks just like RadGridView’s default one and adds this tiny bit of functionality? No, that would be insane. There is an easier way to do this. Read on.
A couple of our customers have recently asked the same question. How can I close the filtering popup when the user clicks the Filter Button?
By design, the filtering popup closes when the user clicks outside it or on the little funnel in the header cell. We have decided to implement it this way because clicking the Filter Button does not necessarily mean that the user has finished filtering. He or she might want to continue after seeing what the results are. So we decided not to close the the popup until the user explicitly decides that he or she is done filtering by clicking somewhere else with the mouse.
But this can be changed in a matter of seconds and I will try to explain how.
I have developed a simple attached behavior that makes the filtering popup close when the button is clicked. You attach it to a GridViewBoundColumnBase to let it know it should behave in this new way. To make a behavior work you simply need to override its OnAttached and OnDetaching methods. Let’s take a look at my behavior:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using Telerik.Windows.Controls;
6: using System.Windows;
7: using Telerik.Windows.Controls.GridView;
8: using System.Windows.Controls;
9: using System.Windows.Interactivity;
10:
11: namespace ClosePopupOnApplyFilter
12: {
13: /// <summary>
14: /// A behavior that closes the filtering popup of a column when the Apply Filter
15: /// button is clicked.
16: /// </summary>
17: public class ClosePopupOnApplyFilterBehavior : Behavior<GridViewBoundColumnBase>
18: {
19: FilteringControl customFilteringControl;
20: Button applyFilterButton;
21:
22: /// <summary>
23: /// Called after the behavior is attached to an AssociatedObject.
24: /// </summary>
25: /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
26: protected override void OnAttached()
27: {
28: // This is the control that RadGridView uses internally if you
29: // don't specify a custom filtering control through the
30: // GridViewBoundColumnBase.FilteringControl property.
31: // We will create an instance of this default filtering control
32: // and use it as a "custom" filtering control in order to extend
33: // just a little bit with our custom logic. Everything else will
34: // be the same.
35: this.customFilteringControl = new FilteringControl();
36: this.customFilteringControl.Loaded += this.OnFilteringControlLoaded;
37:
38: // Tell the column to use our new "custom" filtering control.
39: // It will never now that this is the default one but spicied up a little.
40: this.AssociatedObject.FilteringControl = customFilteringControl;
41: }
42:
43: void OnFilteringControlLoaded(object sender, RoutedEventArgs e)
44: {
45: // When it loads and all of its children are alive find the "Filter"
46: // button which is the only button on the control.
47: // You can find out what its name is from the FilteringControl template.
48: this.applyFilterButton = this.customFilteringControl
49: .ChildrenOfType<Button>()
50: .Where(b => b.Name == "PART_ApplyFilterButton")
51: .FirstOrDefault();
52:
53: if (this.applyFilterButton != null)
54: {
55: this.applyFilterButton.Click += this.OnApplyFilter;
56: }
57: }
58:
59: void OnApplyFilter(object sender, RoutedEventArgs e)
60: {
61: // And when clicked find the parent popup and close it.
62: var popup = applyFilterButton.ParentOfType<System.Windows.Controls.Primitives.Popup>();
63: if (popup != null)
64: {
65: popup.IsOpen = false;
66: }
67: }
68:
69: /// <summary>
70: /// Called when the behavior is being detached from its AssociatedObject,
71: /// but before it has actually occurred.
72: /// </summary>
73: /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
74: protected override void OnDetaching()
75: {
76: if (this.applyFilterButton != null)
77: {
78: this.applyFilterButton.Click -= this.OnApplyFilter;
79: }
80:
81: this.customFilteringControl.Loaded -= this.OnFilteringControlLoaded;
82: }
83: }
84: }
What it does is the following. It creates a new instance of RadGridView’s default FilteringControl. Then it attaches to its Loaded event. The Loaded event will be fired when the user clicks the filtering funnel. When this happens, I locate the Filter Button and attach to its Click event. When the user clicks the button, I find the popup that this control is in and close. That’s it. On detaching I simply clear the event handlers. To use the Behavior class you will need a reference to the System.Windows.Interactivity assembly.
To learn about behaviors in greater detail you can follow Shawn Wildremuth’s great blog post.
You can turn on this behavior entirely in XAML. You do not have to write in the code-behind file. Here is how to do it:
1: <UserControl x:Class="ClosePopupOnApplyFilter.MainPage"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6: xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
7: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
8: xmlns:my="clr-namespace:ClosePopupOnApplyFilter"
9: mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
10: <Grid>
11: <Grid>
12: <telerik:RadGridView Name="clubsGrid"
13: AutoGenerateColumns="False"
14: ColumnsWidthMode="Auto">
15: <telerik:RadGridView.Columns>
16: <telerik:GridViewDataColumn Header="Name"
17: DataMemberBinding="{Binding Name}">
18: <i:Interaction.Behaviors>
19: <my:ClosePopupOnApplyFilterBehavior />
20: </i:Interaction.Behaviors>
21: </telerik:GridViewDataColumn>
22:
23: <telerik:GridViewDataColumn Header="Est."
24: DataMemberBinding="{Binding Established}"
25: DataFormatString="{}{0:yyyy}"/>
26: <telerik:GridViewDataColumn Header="Stadium"
27: DataMemberBinding="{Binding StadiumCapacity}"
28: DataFormatString="{}{0:N0}"/>
29: </telerik:RadGridView.Columns>
30: </telerik:RadGridView>
31: </Grid>
32: </Grid>
33: </UserControl>
In my sample I have attached the behavior to the Name column only. Of course you can do this in the code-behind too if you want to. You can also do it for a specific column, for all columns, when a new column is auto-generated, etc. Use it anyway you want to.
You can even extend this behavior to do something else, for example close the popup when the Clear Filter Button is clicked. That is the beauty of attached behaviors – they are general purpose and you got to love them.
Here are the links to the full source code of my sample project:
Close Popup on Apply Filter for Silverlight
Close Popup on Apply Filter for WPF
Let me know if you come up with other cool ways to use attached behaviors with RadGridView.