New to Telerik UI for WinForms? Download free 30-day trial

Custom Excel-like filter popup with time part

Environment

Product Version Product Author
2020.3.915 RadGridView for WinForms Nadya Karaivanova

Description

By default, when using Excel-like filtering functionality in RadGridView it shows a pop up with RadCalendar so the user can easily pick a date.

custom-excel-like-filter-popup01.png

The following tutorial will demonstrate how you can show a RadDateTimePicker in order to be able to pick a time as well as a date.

custom-excel-like-filter-popup02.png

Solution

This can be achieved by creating a custom RadDateFilterPopup and add a time picker item. Let's first create a custom FilterMenuTimePickerElement that will host the RadDateTimePickerElement. Then, we need to create FilterMenuTimePickerItem that inherits from the RadMenuItemBase which should be added to the Items collection in our custom pop up.

public class FilterMenuTimePickerElement : LightVisualElement
{
    private RadHostItem hostItem;
    private RadDateTimePicker dateTimePicker;

    public RadDateTimePicker DateTimePicker
    {
        get { return this.dateTimePicker; }
    }

    protected override void InitializeFields()
    {
        base.InitializeFields();

        this.BorderGradientStyle = GradientStyles.Solid;
        this.BorderColor = Color.FromArgb(156, 189, 232);
    }

    protected override void CreateChildElements()
    {
        base.CreateChildElements();

        this.dateTimePicker = new RadDateTimePicker();
        this.dateTimePicker.DateTimePickerElement.Format = DateTimePickerFormat.Custom;
        this.dateTimePicker.DateTimePickerElement.CustomFormat = "dd/MM/yyyy HH:mm";

        this.dateTimePicker.DateTimePickerElement.ShowTimePicker = true;
        var calendar = this.dateTimePicker.DateTimePickerElement.GetCurrentBehavior() as RadDateTimePickerCalendar;
        calendar.TimePicker.TimePickerElement.Step = 1; 
        this.dateTimePicker.DateTimePickerElement.CalendarSize = new Size(400, 400);

        this.hostItem = new RadHostItem(this.dateTimePicker);
        this.Children.Add(this.hostItem);
    }
}

public class FilterMenuTimePickerItem : RadMenuItemBase
{
    RadCheckBoxElement checkBoxElement;
    FilterMenuTimePickerElement timePickerElement;

    public bool IsChecked
    {
        get { return this.checkBoxElement.IsChecked; }
        set { this.checkBoxElement.IsChecked = value; }
    }

    public FilterMenuTimePickerElement TimePickerElement
    {
        get { return this.timePickerElement; }
    }

    protected override void InitializeFields()
    {
        base.InitializeFields();
        this.Padding = new Padding(5, 5, 5, 0);
        this.MinSize = new Size(140, 60);
    }

    protected override void CreateChildElements()
    {
        base.CreateChildElements();

        this.checkBoxElement = new RadCheckBoxElement();
        this.checkBoxElement.IsChecked = false;
        this.checkBoxElement.Text = RadGridLocalizationProvider.CurrentProvider.GetLocalizedString(RadGridStringId.FilterFunctionSelectedDates);
        this.checkBoxElement.ToggleStateChanged += new StateChangedEventHandler(checkBoxElement_ToggleStateChanged);
        this.checkBoxElement.Margin = new Padding(0, 10, 0, 5);

        this.Children.Add(this.checkBoxElement);

        this.timePickerElement = new FilterMenuTimePickerElement();
        this.timePickerElement.Enabled = false;
        this.Children.Add(this.timePickerElement);
    }
    protected override SizeF ArrangeOverride(SizeF finalSize)
    {
        RectangleF rect = GetClientRectangle(finalSize);
        RadDropDownMenuLayout layout = FindAncestor<RadDropDownMenuLayout>();
        if (layout != null)
        {
            rect.X += (this.RightToLeft) ? 0 : layout.LeftColumnWidth;
            rect.Width -= layout.LeftColumnWidth;
        }

        foreach (RadElement element in this.Children)
        {
            if (element == this.checkBoxElement)
            {
                element.Arrange(new RectangleF(rect.X, 0, rect.Width, this.checkBoxElement.DesiredSize.Height));
            }
            else if (element == this.timePickerElement)
            {
                this.timePickerElement.Arrange(new RectangleF(rect.X, this.checkBoxElement.DesiredSize.Height,
                    rect.Width, rect.Height - this.checkBoxElement.DesiredSize.Height));
            }
            else
            {
                element.Arrange(rect);
            }
        }

        return finalSize;
    }

    void checkBoxElement_ToggleStateChanged(object sender, StateChangedEventArgs args)
    {
        this.timePickerElement.Enabled = this.checkBoxElement.IsChecked;
    }
    protected override void DisposeManagedResources()
    {
        this.checkBoxElement.ToggleStateChanged -= checkBoxElement_ToggleStateChanged;

        base.DisposeManagedResources();
    }
}

Now, let's create the custom filter popup:

public class MyRadDateFilterPopup : RadDateFilterPopup
{
    public MyRadDateFilterPopup(GridViewDataColumn dataColumn) 
        : base(dataColumn)
    {
    }

    protected override void InitializeElements()
    {
        CreateGeneralMenuItems();
        CreateCalendarElement();
        this.Items.Remove(this.CalendarItem);
        this.Items.Add(new FilterMenuTimePickerItem());
        CreateButtonsElement();
        CreateDateCustomItems();
    }
    protected override void SetInitialSelection()
    {
        base.SetInitialSelection();

        DateFilterDescriptor descriptor = GetFilterDescriptor() as DateFilterDescriptor;
        if (descriptor == null)
        {
            return;
        }

        foreach (RadItem item in this.Items)
        {
            FilterMenuTimePickerItem filterMenuTimePickerItem = item as FilterMenuTimePickerItem;
            if (filterMenuTimePickerItem == null)
            {
                continue;
            }

            if (descriptor.Operator == FilterOperator.IsEqualTo && descriptor.Value != null)
            {
                filterMenuTimePickerItem.IsChecked = true;
                filterMenuTimePickerItem.TimePickerElement.DateTimePicker.Value = descriptor.Value.Value;
            }
        }
    }

    protected override void OnButtonOkClick(EventArgs e)
    {
        FilterDescriptor filterDescriptor = null;
        foreach (RadItem item in this.Items)
        {
            FilterMenuTimePickerItem filterMenuTimePickerItem = item as FilterMenuTimePickerItem;
            if (filterMenuTimePickerItem != null && 
                filterMenuTimePickerItem.IsChecked)
            {
                DateTime? value = filterMenuTimePickerItem.TimePickerElement.DateTimePicker.DateTimePickerElement.Value;
                if (value.HasValue)
                {
                    DateTime date = value.Value;
                    filterDescriptor = new DateFilterDescriptor(this.DataColumn.Name, FilterOperator.IsEqualTo, date, false);
                }

                break;
            }
        }

        if (string.IsNullOrEmpty(filterDescriptor.Expression))
        {
            filterDescriptor = null;
        }

        base.FilterDescriptor = filterDescriptor;

        OnFilterConfirmed();
    }

    private bool SearchForAppliedFilterDescriptors(FilterDescriptor descriptor1, FilterDescriptor descriptor2)
    {
        if (descriptor1.Expression == descriptor2.Expression)
        {
            return true;
        }

        if (descriptor1 is CompositeFilterDescriptor)
        {
            foreach (FilterDescriptor descriptor in ((CompositeFilterDescriptor)descriptor1).FilterDescriptors)
            {
                if (SearchForAppliedFilterDescriptors(descriptor, descriptor2))
                {
                    return true;
                }
            }
        }

        return false;
    }
}

Finally, we should use the FilterPopupRequired event that is thrown just before the filter popup shows and substitute the default popup with our custom made popup.

 public Form1()
 {
     InitializeComponent();

     GridViewDateTimeColumn dateTimeColumn = new GridViewDateTimeColumn("DateTime");
     dateTimeColumn.FormatString = "{0:dd/MM/yyyy HH:mm}";
     dateTimeColumn.Format = DateTimePickerFormat.Custom;
     dateTimeColumn.CustomFormat = "dd/MM/yyyy HH:mm";
     dateTimeColumn.EditorType = GridViewDateTimeEditorType.DateTimePickerSpinMode;
     dateTimeColumn.FilteringMode = GridViewTimeFilteringMode.DateTime;

     this.radGridView1.Columns.Add(dateTimeColumn);

     this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;

     for (int i = 1; i <= 10; i++)
     {
         this.radGridView1.Rows.Add(DateTime.Now.AddDays(i), DateTime.Now.AddDays(i).AddHours(i - 10));
     }

     this.radGridView1.EnableFiltering = true;
     this.radGridView1.ShowHeaderCellButtons = true;
     this.radGridView1.ShowFilteringRow = false;
     this.radGridView1.FilterPopupRequired += this.RadGridView1_FilterPopupRequired;
 }

 private void RadGridView1_FilterPopupRequired(object sender, FilterPopupRequiredEventArgs e)
 {
     if (e.Column.Name == "DateTime")
     {
         RadDateFilterPopup popup = new MyRadDateFilterPopup(e.Column);
         popup.Size = new Size(popup.Size.Width, 240);
         e.FilterPopup = popup;
     }
 }
In this article