How To: Customize RadGridView's Default Filtering Control with Attached Behaviors (Silverlight & WPF)

Friday, January 22, 2010 by Rossen Hristov | Comments 16

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:

DefaultFilteringControl

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.

Enter Attached Behaviors

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.

16 Comments

  • PBL 14 May
    Great stuff.  Add code to change the caption and we are done!

     

    if (this.applyFilterButton != null)

     

    {

     

     

    this.applyFilterButton.Click += this.OnApplyFilter;

     

     

     

    this.applyFilterButton.Content = "Close";

     

    }

     

  • Varsha 02 Jun
    Hi,
    for me code is not getting compiled as it says

    Error 3 'System.Collections.Generic.IList<System.Windows.Controls.Button>' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Collections.Generic.IList<System.Windows.Controls.Button>' could be found (are you missing a using directive or an assembly reference?)

    Please advice
  • Are you missing the System.Linq using directive or an assembly reference?
  • BO 06 Jul
    Thanks for the post.
    Is it possible to override/customize the list of values which are inside the control? (and of-course the FilterOperator as well)

    Thanks!
  • Hello BO,

    Maybe this blog post is more suitable for your requirements.
  • BO 06 Jul

    Hi Rossen,

    Thanks for your reply - I've saw the above before – it is cool and useful, bur here comes the BUT :)

    I'm looking for a way to create my own filter (list of values and filtering logic) but want it to look exact like the existing default FilteringControl.

    The story is that my column is binding to a collection of values (I've used an ItemControl to render the cell), and I can't use it in the DataMemberBinding…

    Do you have any idea for solving this scenario?

  • Laurence 08 Jul
    Hi Rossen,

    Thanks for the post.

    Quick question though:

    To attached this behaviour to all the columns, is there a faster way than copying those 3lines under all the columns ?
    Thanks
  • You can always create another attached behavior. You will attach it to the whole grid. Inside the behavior, it will iterate over all of its columns and attach the original behavior.
  • Lauren 20 Jul
    Hi,

    I am trying to implement this behaviour on Visual Studio 2010 and silverlight 4. Do you know where i can find that System.Windows.Interactivity dll if i dont have Expression Blend 4 SDK ?
    Thanks very much.



  • Albert 07 Dec
    I want to remove "Select All" From default filter then How can I achieve that ?
  • Michael Gerfen 09 Dec
    @Albert

    Use the following behavior:

     public class HideSelectAllCheckBoxBehavior : Behavior<GridViewBoundColumnBase> 
        { 
            private FilteringControl customFilteringControl; 
            
            private CheckBox selectAllCheckBox; 
      
            /// <summary> 
            /// Called after the behavior is attached to an AssociatedObject. 
            /// </summary> 
            /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks> 
            protected override void OnAttached() 
            { 
                // This is the control that RadGridView uses internally if you 
                // don't specify a custom filtering control through the 
                // GridViewBoundColumnBase.FilteringControl property. 
                // We will create an instance of this default filtering control 
                // and use it as a "custom" filtering control in order to extend 
                // just a little bit with our custom logic. Everything else will 
                // be the same. 
                this.customFilteringControl = new FilteringControl(); 
                this.customFilteringControl.Loaded += this.OnFilteringControlLoaded; 
      
                // Tell the column to use our new "custom" filtering control. 
                // It will never now that this is the default one but spicied up a little. 
                this.AssociatedObject.FilteringControl = customFilteringControl; 
            } 
      
            private void OnFilteringControlLoaded(object sender, RoutedEventArgs e) 
            { 
                // When it loads and all of its children are alive find the "Filter" 
                // button which is the only button on the control. 
                // You can find out what its name is from the FilteringControl template. 
                this.selectAllCheckBox = this.customFilteringControl 
                    .ChildrenOfType<CheckBox>() 
                    .Where(b => b.Name == "PART_SelectAllCheckBox"
                    .FirstOrDefault(); 
                if (selectAllCheckBox != null
                { 
                    selectAllCheckBox.Visibility = Visibility.Collapsed; 
                } 
            } 
      
           
      
            /// <summary> 
            /// Called when the behavior is being detached from its AssociatedObject, 
            /// but before it has actually occurred. 
            /// </summary> 
            /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks> 
            protected override void OnDetaching() 
            { 
               this.customFilteringControl.Loaded -= this.OnFilteringControlLoaded; 
            } 
        } 

    --Michael

  • Brian 24 Mar

    How can I apply this behaviors to every column in my GridView? Especially my GridView is a customized gridview with dependencyProperties implemented, e.g.

     

     

     

    public sealed class MyGridView : 

     

     

    RadGridView {

     

     

     

    public static readonly DependencyProperty EditRowCommandProperty = DependencyProperty.Register("EditRowCommand", typeof(ICommand), typeof(MYGridView), null);

     

     ...

     

     

    Any idea? Thank you!

  • Brian 24 Mar

    How can I apply this behaviors to every column in my GridView? Especially my GridView is a customized gridview with dependencyProperties implemented, e.g.

     

     

     

    public sealed class MyGridView : 

     

     

    RadGridView {

     

     

     

    public static readonly DependencyProperty EditRowCommandProperty = DependencyProperty.Register("EditRowCommand", typeof(ICommand), typeof(MYGridView), null);

     

     ...

     

     

    Any idea? Thank you!

  • Jhonathan 13 Jun
    How can I apply this behaviors to checkboxes filter (Distinct Filter) in PART_DistinctValuesList.
    im using:  
              

    this.ContainerapplyFilterCheck = this.customFilteringControl
    .ChildrenOfType<System.Windows.Controls.ListBox>()
    .Where(b => b.Name == "PART_DistinctValuesList")
    .FirstOrDefault();
    and
    this.applyFilterCheck = this.ContainerapplyFilterCheck
    .ChildrenOfType<CheckBox>()
    .FirstOrDefault();

    this.applyFilterCheck dont work

    for Check all Filter its easy:

    this.applyFilterCheckAll = this.customFilteringControl
    .ChildrenOfType<CheckBox>()
    .Where(b => b.Name == "PART_SelectAllCheckBox")
    .FirstOrDefault();
  • Jhonathan 13 Jun
    How can I apply this behaviors to checkboxes filter (Distinct Filter) in PART_DistinctValuesList.
    im using:  
              

    this.ContainerapplyFilterCheck = this.customFilteringControl
    .ChildrenOfType<System.Windows.Controls.ListBox>()
    .Where(b => b.Name == "PART_DistinctValuesList")
    .FirstOrDefault();
    and
    this.applyFilterCheck = this.ContainerapplyFilterCheck
    .ChildrenOfType<CheckBox>()
    .FirstOrDefault();

    this.applyFilterCheck dont work

    for Check all Filter its easy:

    this.applyFilterCheckAll = this.customFilteringControl
    .ChildrenOfType<CheckBox>()
    .Where(b => b.Name == "PART_SelectAllCheckBox")
    .FirstOrDefault();
  • Apparao.P 17 Oct
    Hi , How can I Add 'X' Close button in the Upper right Corner in the default Filter Control in Rad Grid View?

Add comment

  1. Formatting options
       
     
     
     
     
       
  2. (optional, emails won't be shown on public pages)
  3. (optional)