Extending the Filter Criteria

6 posts, 0 answers
  1. Kevin
    Kevin  avatar
    10 posts
    Member since:
    Jul 2012

    Posted 09 Dec 2010 Link to this post

    I'm looking to add a custom control to each FilterCriteria in our RadDataFilter. Ideally, this would be a button to the right of the UserInput control. When pressed, it'll fetch unique values from the database and present them in a list of some sort. The user can then select a unique value and insert it into the UserInput control instead of having to type the value themselves.

    I've looked at the documentation regarding custom FilterEditors. It looks like I could create the mapping from EditorTemplateRule to DataTemplate based on the PropertyInfo type instead of the PropertyInfo name. I would then need only a handful of DataTemplates (one per type) instead of hundreds of DataTemplates (we have hundreds of columns to search across).

    However, it also seems I'd have to re-implement the stock functionality of the FilterEditors by providing a UserInput control that mimicks the current behavior. For instance, I'd have to handle DateTimes by providing a calendar selector. I'd like to not have to do that. I've been going through the Styles and Templates documentation to see if I can just add a template with a custom control to the right of the FilterEditor, but I'm not having much luck coming up with a solution.

    Does anyone have ideas on the best way to approach this sort of customization?

    Thanks.
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 10 Dec 2010 Link to this post

    Hello Kevin ,

    Custom Filter Editors would be the only way to go.

    There is almost no "stock" functionality when creating the default editors. Here is how we create the default DateTimeEditor:

    private static FrameworkElement CreateDateTimeEditor()
    {
        var dateTimePicker = new RadDateTimePicker();
        dateTimePicker.InputMode = InputMode.DatePicker;
        dateTimePicker.IsTooltipEnabled = false;
     
        var selectedValueBinding = new Binding("Value")
        {
            Mode = BindingMode.TwoWay,
            FallbackValue = null,
            Converter = new DateTimeFilterEditorConverter()
        };
        dateTimePicker.SetBinding(RadDateTimePicker.SelectedValueProperty, selectedValueBinding);
     
        return dateTimePicker;
    }

    So you can create a new control that will host both the editor and your additional UI and return this whole thing as the editor.

    For your reference I am pasting the entire source code of our FilterEditorFactory:

    using System;
    using System.Windows;
    using Telerik.Windows.Controls.Filtering.Editors;
    using Telerik.Windows.Data;
    using System.Windows.Data;
    using System.Windows.Controls;
    using System.Collections;
    using System.Collections.Generic;
     
    namespace Telerik.Windows.Controls.GridView
    {
        internal static class FilterEditorFactory
        {
            public static bool IsCoreFrameworkElement(object element)
            {
                var fe = element as FrameworkElement;
                if (fe != null)
                {
                    var assName = fe.GetType().Assembly.FullName.Split(',')[0];
     
    #if SILVERLIGHT
                    return assName == "System.Windows";
    #endif
     
    #if WPF
                    return assName == "PresentationFramework";
    #endif
                }
     
                return false;
            }
     
            public static FrameworkElement CreateEditor(Type type)
            {
                FrameworkElement editor;
     
                if (type == typeof(string))
                {
                    editor = FilterEditorFactory.CreateStringEditor();
                }
                else if (type == typeof(DateTime) || type == typeof(DateTime?))
                {
                    editor = FilterEditorFactory.CreateDateTimeEditor();
                }
                else if (type == typeof(bool) || type == typeof(bool?))
                {
                    editor = FilterEditorFactory.CreateBooleanEditor(type);
                }
                else if (type.IsNumericType())
                {
                    editor = FilterEditorFactory.CreateNumericEditor(type);
                }
                else if (type.IsEnum)
                {
                    editor = FilterEditorFactory.CreateEnumEditor(type);
                }
                else
                {
                    editor = FilterEditorFactory.CreateDefaultEditor();
                }
     
                return editor;
            }
     
            /// <summary>
            /// Creates the string editor.
            /// </summary>
            /// <returns></returns>
            private static FrameworkElement CreateStringEditor()
            {
                return new StringFilterEditor();
            }
     
            private static FrameworkElement CreateDateTimeEditor()
            {
                var dateTimePicker = new RadDateTimePicker();
                dateTimePicker.InputMode = InputMode.DatePicker;
                dateTimePicker.IsTooltipEnabled = false;
     
                var selectedValueBinding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    FallbackValue = null,
                    Converter = new DateTimeFilterEditorConverter()
                };
                dateTimePicker.SetBinding(RadDateTimePicker.SelectedValueProperty, selectedValueBinding);
     
                return dateTimePicker;
            }
     
            private static FrameworkElement CreateBooleanEditor(Type type)
            {
                var checkBox = new CheckBox();
                checkBox.IsThreeState = type == typeof(bool?);
                 
                var isCheckedBinding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    FallbackValue = false
                };
                checkBox.SetBinding(CheckBox.IsCheckedProperty, isCheckedBinding);
     
                return checkBox;
            }
     
            private static FrameworkElement CreateNumericEditor(Type type)
            {
                var maskedTextBox = new RadMaskedTextBox();
                maskedTextBox.MaskType = MaskType.Numeric;
                maskedTextBox.UpdateValueEvent = UpdateValueEvent.LostFocus;
     
                if (type == typeof(double)
                    || type == typeof(double?)
                    || type == typeof(float)
                    || type == typeof(float?))
                {
                    maskedTextBox.Mask = "f";
                }
                else
                {
                    maskedTextBox.Mask = "d";
                }
     
                var valueBinding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    Converter = new FilterDescriptorValueToMaskedTextBoxValueConverter(),
                    FallbackValue = null
                };
                maskedTextBox.SetBinding(RadMaskedTextBox.ValueProperty, valueBinding);
     
                return maskedTextBox;
            }
     
            private static FrameworkElement CreateEnumEditor(Type type)
            {
                var comboBox = new RadComboBox();
     
                comboBox.SelectedValuePath = "Value";
                comboBox.DisplayMemberPath = "DisplayName";
                comboBox.ItemsSource = EnumDataSource.FromType(type);
                 
                var selectedItemBinding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    FallbackValue = null
                };
                comboBox.SetBinding(RadComboBox.SelectedValueProperty, selectedItemBinding);
             
                return comboBox;
            }
     
            private static FrameworkElement CreateDefaultEditor()
            {
                var textBox = new TextBox();
     
                var textBinding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    FallbackValue = null
                };
                textBox.SetBinding(TextBox.TextProperty, textBinding);
     
                TextBoxBehavior.SetUpdateTextOnEnter(textBox, true);
                TextBoxBehavior.SetSelectAllOnGotFocus(textBox, true);
     
                return textBox;
            }
     
            private class FilterDescriptorValueToMaskedTextBoxValueConverter : IValueConverter
            {
                public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    if (value == FilterDescriptor.UnsetValue)
                    {
                        return null;
                    }
     
                    return value;
                }
     
                public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    return value;
                }
            }
     
            private class DateTimeFilterEditorConverter : IValueConverter
            {
                public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    if (value == FilterDescriptor.UnsetValue)
                    {
                        return null;
                    }
     
                    return value;
                }
     
                public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    return value;
                }
            }
        }
    }

    I hope this helps. Let me know if you have any questions.

    Greetings,
    Ross
    the Telerik team
    Browse the videos here>> to help you get started with RadControls for WPF
  3. UI for WPF is Visual Studio 2017 Ready
  4. Kevin
    Kevin  avatar
    10 posts
    Member since:
    Jul 2012

    Posted 10 Dec 2010 Link to this post

    Thanks for the information, Ross. It's good to know I have a path I can follow. Unfortunately I'm still at a loss as to how I can make use of the reference code you provided. Are you suggesting that I:

    1) Make a custom user control for each Data Type.
    2) Have that user control include functionality that you provided in your reference code as well as my custom control.
    3) Create a DataTemplate in XAML for each custom control (by data type).
    4) Map the DataTemplates to data types using EditorTemplateRules that use PropertyType instead of PropertyName.

    I want to make sure I've got the high level steps right before attacking this.
  5. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 10 Dec 2010 Link to this post

    Hi Kevin ,

    That is exactly my idea.

    You can find a similar implementation in our online example called Custom Filter Editors. For example, we have an editor which is RadSlider. Another one is a panel with two buttons. Another one is a RadComboBox. All of them are just controls.

    The bottom-line is that the custom editor can be any kind of control -- simple or complex -- as long as you have something inside it that is data-bound to the property called Value. Value is the name of the property on the view model serving as the DataContext for the whole editor control. This is in fact is the Value of the underlying FilterDescriptor object.

    Since I have provided all the code needed to initialize our default editors, you can copy them to your more complex control and add your part of the editor.

    Let me know if you have any other questions. I will help with anything I can.

    Best wishes,
    Ross
    the Telerik team
    Browse the videos here>> to help you get started with RadControls for WPF
  6. Kevin
    Kevin  avatar
    10 posts
    Member since:
    Jul 2012

    Posted 10 Dec 2010 Link to this post


    Thanks again for the help. I have the code to the point where the RadDataFilter shows my user control which contains the Telerik StringFilterEditor as well as my custom component.It does this based on Type, which is what I need so I think this approach will work out just fine.

    There are a couple of issues I'm running into though:

    1) Creating the custom user control
    Here are the user controls I'm now working with:
    SearchView - our user control that contains the RadDataFilter
    UniqueStringFilterEditor - our user control that contains the Telerik StringFilterEditor and the UniqueValueSelector
    UniqueValueSelector - the user control that contains a button which will display unique values for the current column

    I'm currently adding the Telerik StringFilterEditor to the UniqueStringFilterEditor through the code-behind, by inserting it into a stack panel. I'd really like to declare the StringFilterEditor through the XAML instead, but I can't determine whether or not that's possible using the FilterEditorFactory. Do you have any ideas on that?

    2) Passing data through the user controls
    I'll need to take the selected column name from the RadDataFilter and pass it through to the UniqueValueSelector. It seems like the kind of thing I'd use data binding for, but I can't see how to do it just yet. Any tips there would be greatly appreciated.

  7. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 14 Dec 2010 Link to this post

    Hello Kevin ,

    "I'd really like to declare the StringFilterEditor through the XAML instead, but I can't determine whether or not that's possible using the FilterEditorFactory."

    The Factory is actually internal and is ours. You don't have access to the Factory so just forget that I mentioned it.

    The result of the FilterEditorFactory is what RadDataFilter creates by default and provides to the developer (you) in the event handler. Having this default control you can do two things with it:

    A. Decide to stick with it and configure it further. For example, change its Background or anything else that comes to mind. This can be done in the EditorCreated event. If this is the case you don't need an EditorTemplateSelector since you are not replacing the default control but simply configuring it.

    B. If you have an EditorTemplateSelector the Factory will not even try to produce something because it knows that the editor will be provided by you. In this case you use the EditorTemplateSelector. Since you will replace the default editor with your very own one -- you don't have to care about this Factory at all. What the Factory does (or does not do in this case) is not your concern. I just pasted the code from our Factory so you can create the default editors with the same settings as we do. In other words, now you are the Factory for filter editors, not RadDataFilter.

    I'll need to take the selected column name from the RadDataFilter and pass it through to the UniqueValueSelector.

    This information is kept in the DataContext that the editor control receives which is of type SimpleFilterViewModel. The most important properties that you care about on this view model are called:

    Member -- this is the "name of the column", as you call it.
    MemberType
    Operator
    Value

    I have prepared a sample project that demonstrates all of this. I have created some dummy editor control that simply demonstrates the stuff that you can bind to.

    I really hope this helps.

    Regards,
    Ross
    the Telerik team
    Browse the videos here>> to help you get started with RadControls for WPF
Back to Top
UI for WPF is Visual Studio 2017 Ready