Click here to Skip to main content
15,884,628 members
Articles / Desktop Programming / WPF

Automatic WPF Toolkit DataGrid Filtering

Rate me:
Please Sign up or sign in to vote.
4.91/5 (109 votes)
21 May 2010BSD6 min read 1.4M   23.5K   167  
This article discusses a component that enables automated content filtering for the WPF Toolkit DataGrid.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using Microsoft.Windows.Controls;
using DataGridFilterLibrary.Support;
using System.Reflection;
using Microsoft.Windows.Controls.Primitives;
using DataGridFilterLibrary.Querying;
using System.ComponentModel;

namespace DataGridFilterLibrary
{
    public class DataGridColumnFilter : Control
    {
        static DataGridColumnFilter()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DataGridColumnFilter), new FrameworkPropertyMetadata(typeof(DataGridColumnFilter)));
        }

        #region Overrides
        protected override void OnPropertyChanged(
            DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == DataGridItemsSourceProperty
                && e.OldValue != e.NewValue
                && AssignedDataGridColumn != null && DataGrid != null && AssignedDataGridColumn is DataGridColumn)
            {
                initialize();

                FilterCurrentData.IsRefresh = true;//query optimization filed

                onFilterChanged();
            }

            base.OnPropertyChanged(e);
        }

        public override void OnApplyTemplate()
        {
            AddHandler("PART_ComboBoxFilterOperator", ComboBox.SelectionChangedEvent, new RoutedEventHandler(handleComboBoxFilterOperatorChanged));
            AddHandler("PART_TextBoxFilter", TextBox.TextChangedEvent, new RoutedEventHandler(handleTextBoxFilterChanged));
            AddHandler("PART_ComboBoxFilter", ComboBox.SelectionChangedEvent, new RoutedEventHandler(handleComboBoxFilterChanged));
            AddHandler("PART_CheckBoxFilter", CheckBox.ClickEvent, new RoutedEventHandler(handleCheckBoxFilterChanged));
            AddHandler("PART_ClearFilterButton", Button.ClickEvent, new RoutedEventHandler(handleClearFilterButton));
            AddHandler("PART_DatePickerFilter", DatePicker.SelectedDateChangedEvent, new RoutedEventHandler(handleSelectedDateChangedEvent));

            base.OnApplyTemplate();
        }
        #endregion

        #region Properties
        public FilterData FilterCurrentData
        {
            get { return (FilterData)GetValue(FilterCurrentDataProperty); }
            set { SetValue(FilterCurrentDataProperty, value); }
        }

        public static readonly DependencyProperty FilterCurrentDataProperty =
            DependencyProperty.Register("FilterCurrentData", typeof(FilterData), typeof(DataGridColumnFilter));

        public DataGridColumnHeader AssignedDataGridColumnHeader
        {
            get { return (DataGridColumnHeader)GetValue(AssignedDataGridColumnHeaderProperty); }
            set { SetValue(AssignedDataGridColumnHeaderProperty, value); }
        }

        public static readonly DependencyProperty AssignedDataGridColumnHeaderProperty =
            DependencyProperty.Register("AssignedDataGridColumnHeader", typeof(DataGridColumnHeader), typeof(DataGridColumnFilter));

        public DataGridColumn AssignedDataGridColumn
        {
            get { return (DataGridColumn)GetValue(AssignedDataGridColumnProperty); }
            set { SetValue(AssignedDataGridColumnProperty, value); }
        }

        public static readonly DependencyProperty AssignedDataGridColumnProperty =
            DependencyProperty.Register("AssignedDataGridColumn", typeof(DataGridColumn), typeof(DataGridColumnFilter));

        public DataGrid DataGrid
        {
            get { return (DataGrid)GetValue(DataGridProperty); }
            set { SetValue(DataGridProperty, value); }
        }

        public static readonly DependencyProperty DataGridProperty =
            DependencyProperty.Register("DataGrid", typeof(DataGrid), typeof(DataGridColumnFilter));

        public IEnumerable DataGridItemsSource
        {
            get { return (IEnumerable)GetValue(DataGridItemsSourceProperty); }
            set { SetValue(DataGridItemsSourceProperty, value); }
        }

        public static readonly DependencyProperty DataGridItemsSourceProperty =
            DependencyProperty.Register("DataGridItemsSource", typeof(IEnumerable), typeof(DataGridColumnFilter));

        public bool IsFilteringInProgress
        {
            get { return (bool)GetValue(IsFilteringInProgressProperty); }
            set { SetValue(IsFilteringInProgressProperty, value); }
        }

        public static readonly DependencyProperty IsFilteringInProgressProperty =
            DependencyProperty.Register("IsFilteringInProgress", typeof(bool), typeof(DataGridColumnFilter));

        public FilterType FilterType { get { return FilterCurrentData != null ? FilterCurrentData.Type : FilterType.Text; } }

        public bool IsTextFilterControl
        {
            get { return (bool)GetValue(IsTextFilterControlProperty); }
            set { SetValue(IsTextFilterControlProperty, value); }
        }

        public static readonly DependencyProperty IsTextFilterControlProperty =
            DependencyProperty.Register("IsTextFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsNumericFilterControl
        {
            get { return (bool)GetValue(IsNumericFilterControlProperty); }
            set { SetValue(IsNumericFilterControlProperty, value); }
        }
        public static readonly DependencyProperty IsNumericFilterControlProperty =
            DependencyProperty.Register("IsNumericFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsBooleanFilterControl
        {
            get { return (bool)GetValue(IsBooleanFilterControlProperty); }
            set { SetValue(IsBooleanFilterControlProperty, value); }
        }
        public static readonly DependencyProperty IsBooleanFilterControlProperty =
            DependencyProperty.Register("IsBooleanFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsListFilterControl
        {
            get { return (bool)GetValue(IsListFilterControlProperty); }
            set { SetValue(IsListFilterControlProperty, value); }
        }
        public static readonly DependencyProperty IsListFilterControlProperty =
            DependencyProperty.Register("IsListFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsDateTimeFilterControl
        {
            get { return (bool)GetValue(IsDateTimeFilterControlProperty); }
            set { SetValue(IsDateTimeFilterControlProperty, value); }
        }
        public static readonly DependencyProperty IsDateTimeFilterControlProperty =
            DependencyProperty.Register("IsDateTimeFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsFirstFilterControl
        {
            get { return (bool)GetValue(IsFirstFilterControlProperty); }
            set { SetValue(IsFirstFilterControlProperty, value); }
        }
        public static readonly DependencyProperty IsFirstFilterControlProperty =
            DependencyProperty.Register("IsFirstFilterControl", typeof(bool), typeof(DataGridColumnFilter));

        public bool IsControlInitialized
        {
            get { return (bool)GetValue(IsControlInitializedProperty); }
            set { SetValue(IsControlInitializedProperty, value); }
        }
        public static readonly DependencyProperty IsControlInitializedProperty =
            DependencyProperty.Register("IsControlInitialized", typeof(bool), typeof(DataGridColumnFilter));
        #endregion

        #region Initialization
        private void initialize()
        {
            if (DataGridItemsSource != null && AssignedDataGridColumn != null && DataGrid != null)
            {
                initFilterData();

                initControlType();

                handleListFilterType();

                IsControlInitialized = true;
            }
        }

        private void initFilterData()
        {
            if (FilterCurrentData == null || !FilterCurrentData.IsTypeInitialized)
            {
                string valuePropertyBindingPath = getValuePropertyBindingPath(AssignedDataGridColumn);

                bool typeInitialized;

                Type valuePropertyType = getValuePropertyType(
                    valuePropertyBindingPath, getItemSourceElementType(out typeInitialized));

                FilterType filterType = getFilterType(
                    valuePropertyType, isComboDataGridColumn(AssignedDataGridColumn));

                FilterOperator filterOperator = FilterOperator.Undefined;

                string queryString = String.Empty;

                FilterCurrentData = new FilterData(
                    filterOperator, filterType, valuePropertyBindingPath, valuePropertyType, queryString, typeInitialized);
            }
        }

        private void initControlType()
        {
            IsFirstFilterControl    = false;

            IsTextFilterControl     = false;
            IsNumericFilterControl  = false;
            IsBooleanFilterControl  = false;
            IsListFilterControl     = false;
            IsDateTimeFilterControl = false;

            if (FilterType == FilterType.Text)
            {
                IsTextFilterControl = true;
            }
            else if (FilterType == FilterType.Numeric)
            {
                IsNumericFilterControl = true;
            }
            else if (FilterType == FilterType.Boolean)
            {
                IsBooleanFilterControl = true;
            }
            else if (FilterType == FilterType.List)
            {
                IsListFilterControl = true;
            }
            else if (FilterType == FilterType.DateTime)
            {
                IsDateTimeFilterControl = true;
            }
        }

        private void handleListFilterType()
        {
            if (FilterCurrentData.Type == FilterType.List)
            {
                ComboBox comboBox             = this.Template.FindName("PART_ComboBoxFilter", this) as ComboBox;
                DataGridComboBoxColumn column = AssignedDataGridColumn as DataGridComboBoxColumn;
                
                if (comboBox != null && column != null)
                {
                    if (DataGridComboBoxExtensions.GetIsTextFilter(column))
                    {
                        FilterCurrentData.Type = FilterType.Text;
                        initControlType();
                    }
                    else //list filter type
                    {
                        BindingOperations.SetBinding(comboBox, ComboBox.ItemsSourceProperty,
                          BindingOperations.GetBinding(column, ComboBox.ItemsSourceProperty));

                        Action<object, CurrentChangingEventArgs> handler = null;

                        handler = delegate(object sender, CurrentChangingEventArgs e)
                        {
                            comboBox.Items.CurrentChanging -= new CurrentChangingEventHandler(handler);

                            IList list = comboBox.ItemsSource.Cast<object>().ToList();

                            list.Insert(0, DependencyProperty.UnsetValue);

                            comboBox.ItemsSource = list;

                            comboBox.DisplayMemberPath = column.DisplayMemberPath;
                            comboBox.SelectedValuePath = column.SelectedValuePath;
                        };

                        comboBox.Items.CurrentChanging += new CurrentChangingEventHandler(handler);
                    }
                }
            }
        }

        private string getValuePropertyBindingPath(DataGridColumn column)
        {
            string path = String.Empty;

            if (column is DataGridBoundColumn)
            {
                DataGridBoundColumn bc = column as DataGridBoundColumn;
                path = (bc.Binding as Binding).Path.Path;
            }
            else if (column is DataGridTemplateColumn)
            {
                DataGridTemplateColumn tc = column as DataGridTemplateColumn;

                object templateContent = tc.CellTemplate.LoadContent();

                if (templateContent != null && templateContent is TextBlock)
                {
                    TextBlock block = templateContent as TextBlock;

                    BindingExpression binding = block.GetBindingExpression(TextBlock.TextProperty);

                    path = binding.ParentBinding.Path.Path;
                }
            }
            else if (column is DataGridComboBoxColumn)
            {
                DataGridComboBoxColumn comboColumn = column as DataGridComboBoxColumn;

                path = null;

                Binding binding = ((comboColumn.SelectedValueBinding) as Binding);

                if (binding == null)
                {
                    binding = ((comboColumn.SelectedItemBinding) as Binding);
                }

                if (binding == null)
                {
                    binding = comboColumn.SelectedValueBinding as Binding;
                }

                if (binding != null)
                {
                    path = binding.Path.Path;
                }

                if (comboColumn.SelectedItemBinding != null && comboColumn.SelectedValueBinding == null)
                {
                    if (path != null && path.Trim().Length > 0)
                    {
                        if (DataGridComboBoxExtensions.GetIsTextFilter(comboColumn))
                        {
                            path += "." + comboColumn.DisplayMemberPath; 
                        }
                        else
                        {
                            path += "." + comboColumn.SelectedValuePath;
                        }
                    }
                }
            }

            return path;
        }

        private Type getValuePropertyType(string path, Type elementType)
        {
            Type type = typeof(object);

            if (elementType != null)
            {
                string[] properties = path.Split(".".ToCharArray()[0]);

                PropertyInfo pi = null;

                if (properties.Length == 1)
                {
                    pi = elementType.GetProperty(path);
                }
                else
                {
                    pi = elementType.GetProperty(properties[0]);

                    for (int i = 1; i < properties.Length; i++)
                    {
                        if (pi != null)
                        {
                            pi = pi.PropertyType.GetProperty(properties[i]);
                        }
                    }
                }


                if (pi != null)
                {
                    type = pi.PropertyType;
                }
            }

            return type;
        }

        private Type getItemSourceElementType(out bool typeInitialized)
        {
            typeInitialized = false;

            Type elementType = null;

            IList l = (DataGridItemsSource as IList);

            if (l != null && l.Count > 0)
            {
                object obj = l[0];

                if (obj != null)
                {
                    elementType = l[0].GetType();
                    typeInitialized = true;
                }
                else
                {
                    elementType = typeof(object);
                }
            }
            if (l == null)
            {
                ListCollectionView lw = (DataGridItemsSource as ListCollectionView);

                if (lw != null && lw.Count > 0)
                {
                    object obj = lw.CurrentItem;

                    if (obj != null)
                    {
                        elementType = lw.CurrentItem.GetType();
                        typeInitialized = true;
                    }
                    else
                    {
                        elementType = typeof(object);
                    }
                }
            }

            return elementType;
        }

        private FilterType getFilterType(
            Type valuePropertyType, bool isAssignedDataGridColumnComboDataGridColumn)
        {
            FilterType filterType;

            if (isAssignedDataGridColumnComboDataGridColumn)
            {
                filterType = FilterType.List;
            }
            else if (valuePropertyType == typeof(Boolean) || valuePropertyType == typeof(Nullable<Boolean>))
            {
                filterType = FilterType.Boolean;
            }
            else if (valuePropertyType == typeof(SByte) || valuePropertyType == typeof(Nullable<SByte>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Byte) || valuePropertyType == typeof(Nullable<Byte>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Int16) || valuePropertyType == typeof(Nullable<Int16>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(UInt16) || valuePropertyType == typeof(Nullable<UInt16>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Int32) || valuePropertyType == typeof(Nullable<Int32>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(UInt32) || valuePropertyType == typeof(Nullable<UInt32>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Int64) || valuePropertyType == typeof(Nullable<Int64>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Single) || valuePropertyType == typeof(Nullable<Single>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Int64) || valuePropertyType == typeof(Nullable<Int64>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Decimal) || valuePropertyType == typeof(Nullable<Decimal>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(float) || valuePropertyType == typeof(Nullable<float>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Double) || valuePropertyType == typeof(Nullable<Double>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(Int64) || valuePropertyType == typeof(Nullable<Int64>))
            {
                filterType = FilterType.Numeric;
            }
            else if (valuePropertyType == typeof(DateTime) || valuePropertyType == typeof(Nullable<DateTime>))
            {
                filterType = FilterType.DateTime;
            }
            else
            {
                filterType = FilterType.Text;
            }

            return filterType;
        }

        private bool isComboDataGridColumn(DataGridColumn column)
        {
            return column is DataGridComboBoxColumn;
        }
        #endregion

        #region Querying
        private void handleTextBoxFilterChanged(object sender, RoutedEventArgs e)
        {
            if (FilterCurrentData != null)
            {
                if (FilterCurrentData.Type == FilterType.Numeric || FilterCurrentData.Type == FilterType.DateTime)
                {
                    if (FilterCurrentData.Operator != FilterOperator.Undefined || FilterCurrentData.QueryString != String.Empty)
                    {
                        onFilterChanged();
                    }
                }
                else if (FilterCurrentData.Type == FilterType.Text)
                {
                    FilterCurrentData.Operator = FilterOperator.Like;

                    onFilterChanged();
                }
            }
        }

        private void handleComboBoxFilterChanged(object sender, RoutedEventArgs e)
        {
            FilterCurrentData.Operator = FilterOperator.Equals;

            onFilterChanged();
        }

        private void handleCheckBoxFilterChanged(object sender, RoutedEventArgs e)
        {
            FilterCurrentData.Operator = FilterOperator.Equals;

            onFilterChanged();
        }

        private void handleComboBoxFilterOperatorChanged(object sender, RoutedEventArgs e)
        {
            if (FilterCurrentData != null 
                && 
                (FilterCurrentData.Operator != FilterOperator.Undefined || FilterCurrentData.QueryString != String.Empty)
                &&
                (sender is ComboBox && (sender as ComboBox).Visibility == Visibility.Visible))
            {
                handleTextBoxFilterChanged(null, null);
            }
        }

        private void handleSelectedDateChangedEvent(object sender, RoutedEventArgs e)
        {
            onFilterChanged();
        }

        protected void onFilterChanged()
        {
            if (DataGrid != null)
            {
                QueryController query = QueryControllerFactory.GetQueryController(
                    DataGrid, FilterCurrentData, DataGridItemsSource);

                query.DoQuery();

                IsFirstFilterControl = query.IsCurentControlFirstControl;
            }
        }
        #endregion

        #region Clear query
        private void handleClearFilterButton(object sender, RoutedEventArgs e)
        {
            if (DataGrid != null)
            {
                QueryController query = QueryControllerFactory.GetQueryController(
                    DataGrid, FilterCurrentData, DataGridItemsSource);

                query.ClearFilter();
            }
        }
        #endregion

        #region Helpers
        private void AddHandler(string name, RoutedEvent e, Delegate d)
        {
            FrameworkElement element = this.Template.FindName(name, this) as FrameworkElement;

            if (element != null)
            {
                element.RemoveHandler(e, d);

                element.AddHandler(e, d, true);
            }
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer Fireminds
Croatia Croatia
Sanjin Matusan - C#, WPF, SQL, .NET

Comments and Discussions