This is a migrated thread and some comments may be shown as answers.

FilterMemberPath is broken for DataView

8 Answers 265 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Magnus
Top achievements
Rank 1
Magnus asked on 17 Aug 2017, 08:17 AM

I'm binding a DataView to the ItemsSource of a RadGridView and some columns are of the DateTime type. On the DateTime-columns I set FilterMemberPath to col.DataMemberBinding.Path.Path + ".Date" as mentioned here: http://www.telerik.com/forums/filtering-on-date-only to only filter on the date part.

This functionality is now broken. I't works in 2016.1.217.45, but not in 2016.3.1024.45 or later versions (I've tried up to the latest release version 2017.2.216.40)

If I replace my DataView with an IEnumerable<SomeObject> the filtering works as expected.

8 Answers, 1 is accepted

Sort by
0
Stefan
Telerik team
answered on 22 Aug 2017, 07:17 AM
Hi Magnus,

Thanks for your feedback.

I managed to replicate the reported behavior with the specified assemblies versions. I am researching what the cause for it might be, but I need a little more time. I will update you as soon as I have a result.

Thank you in advance for understanding.

Regards,
Stefan X1
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Accepted
Stefan
Telerik team
answered on 30 Aug 2017, 12:33 PM
Hello Magnus,

Firstly, thank you for your patience.

After investigating the case, it seems that this is an issue on our end. I have logged it in our bug tracking system. You can track the state of the relevant item in the Feedback Portal: When DataTable is used as a source and the FilterMemberPath points to the Date property of a DateTime one, the distinct values are not loaded.

I have also added some Telerik points to your account as a token of gratitude for your cooperation in reporting the problem.

Regards,
Stefan X1
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Magnus
Top achievements
Rank 1
answered on 31 Aug 2017, 05:54 AM
Great! Thanks foryour help. I'm looking forward to the fix!
0
Magnus
Top achievements
Rank 1
answered on 22 May 2018, 12:01 PM
Do you have any idea when this issue will be addressed? Or if it's possible to fix it myself by some custom implementation?
0
Stefan
Telerik team
answered on 25 May 2018, 08:41 AM
Hello Magnus,

We made a couple of attempts to fix the problem, but some other mechanisms were affected by them. There are some limitations that hold us back from providing a stable fix. So, unfortunately, for the time being I cannot confirm when the issue will be fixed. I cannot also suggest a workaround for it. Please, excuse us for the inconvenience caused.

Regards,
Stefan
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Magnus
Top achievements
Rank 1
answered on 01 Feb 2019, 08:58 AM
Is it possible to write a custom implementation of MemberColumnFilterDescriptor and use some Expression magic to solve this? I'm struggling with building a LINQ expression for the filtering, but not sure if it's possible...
0
Magnus
Top achievements
Rank 1
answered on 01 Feb 2019, 10:56 AM

Here's a custom MemberColumnFilterDescriptor that I wrote to overcome this issue (with the help from various posts scattered among this forum). I guess that it maybe needs to be customized to fit into other applications than mine.

namespace MyNamespaceOfCustomControls
{
    using System;
    using System.Data;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
 
    using Telerik.Windows.Controls;
    using Telerik.Windows.Controls.GridView;
    using Telerik.Windows.Data;
 
    public class DatePartColumnFilterDescriptor : MemberColumnFilterDescriptor
    {
        private static readonly MethodInfo PassesFilterMethodInfo =
            typeof(DatePartColumnFilterDescriptor).GetMethod(nameof(PassesFilter));
 
        // This is the delegate that we will call in order to get the property value of an item.
        private Delegate getPropertyValueDelegate;
 
        private readonly GridViewBoundColumnBase column;
 
        public DatePartColumnFilterDescriptor(GridViewBoundColumnBase column)
            : base(column)
        {
            this.column = column;
        }
 
        /// <summary>
        /// Creates a predicate filter expression used for collection filtering.
        /// </summary>
        /// <param name="instance">The instance expression, which will be used for filtering.</param>
        /// <returns>A predicate filter expression.</returns>
        public override Expression CreateFilterExpression(Expression instance)
        {
            // $dataRowView
            ParameterExpression parameter = (ParameterExpression)instance;
 
            // this
            ConstantExpression thisExpression = Expression.Constant(this, typeof(DatePartColumnFilterDescriptor));
             
            // this.PassesFilter(dataRowView)
            MethodCallExpression filteringExpression = Expression.Call(thisExpression, PassesFilterMethodInfo, parameter);
 
            // This expression simply calls the PassesFilter method to determine whether an item passes the filter.
            // This is the expression that our data engine will use in order to see whether each person passes the current filter.
            // The data engine will build something like this:
            // var result = sourceCollection.Where(value => this.PassesFilter(value));
            // and return only persons that pass the filtering criteria.
            return filteringExpression;
        }
 
        /// <summary>
        /// Refreshes the column filter descriptor from its parent column.
        /// </summary>
        public override void Refresh()
        {
            base.Refresh();
 
            // If we've got no member binding, bail
            if (string.IsNullOrEmpty(this.Member))
            {
                return;
            }
 
            // Get the property name
            var propertyName = this.column.DataMemberBinding.Path.Path;
 
            // Build a lambda expression on DataRowView to get the DateTime value from it's string indexer.
            var param = Expression.Parameter(typeof(DataRowView), "t");
            var body = Expression.Property(param, "Item", Expression.Constant(propertyName));
            var lambda = Expression.Lambda<Func<DataRowView, object>>(body, param);
 
            this.getPropertyValueDelegate = lambda.Compile();
        }
 
        /// <summary>
        /// Determines whether a data item passes the current distinct values filter.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        public bool PassesFilter(object item)
        {
            var filterDescriptor = (IColumnFilterDescriptor)this;
            if (!filterDescriptor.IsActive)
            {
                // Always pass if filter is not active
                return true;
            }
 
            // Invoke the delegate to get the current DateTime? value from the item (DataRowView)
            DateTime? itemPropertyValue = (DateTime?)this.getPropertyValueDelegate.DynamicInvoke(item);
             
            // Check the distinct filter first
            if (this.PassDistinctFilter(filterDescriptor.DistinctFilter, itemPropertyValue))
            {
                return true;
            }
 
            if (!filterDescriptor.FieldFilter.IsActive || (!filterDescriptor.FieldFilter.Filter1.IsActive && !filterDescriptor.FieldFilter.Filter2.IsActive))
            {
                // Always pass if field filters are not active, but not if the distinct filter in active
                return !filterDescriptor.DistinctFilter.IsActive;
            }
 
            // Check primary filter
            var passFilter1 = this.PassSingleFilter(itemPropertyValue, filterDescriptor.FieldFilter.Filter1);
            if (!filterDescriptor.FieldFilter.Filter2.IsActive)
            {
                return passFilter1;
            }
 
            // Check secondary filter
            var passFilter2 = this.PassSingleFilter(itemPropertyValue, filterDescriptor.FieldFilter.Filter2);
 
            // Combine
            switch (filterDescriptor.FieldFilter.LogicalOperator)
            {
                case FilterCompositionLogicalOperator.And:
                    return passFilter1 && passFilter2;
                case FilterCompositionLogicalOperator.Or:
                    return passFilter1 || passFilter2;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
 
        private bool PassDistinctFilter(IDistinctValuesFilterDescriptor distinctFilter, DateTime? itemPropertyValue)
        {
            // Check if any distinct values are selected
            var distinctValues = distinctFilter.DistinctValues.Cast<DateTime>().ToList();
            if (distinctValues.Count != 0)
            {
                // Check if any of the distinct values matches this item
                if (distinctValues.Any(
                    distinctValue => this.PassSingleFilter(
                        itemPropertyValue,
                        distinctValue,
                        distinctFilter.DistinctValuesComparisonOperator,
                        true)))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private bool PassSingleFilter(DateTime? itemValue, OperatorValueFilterDescriptorBase filterDescriptorBase)
        {
            return this.PassSingleFilter(itemValue, (DateTime)filterDescriptorBase.Value, filterDescriptorBase.Operator, false);
        }
 
        private bool PassSingleFilter(DateTime? itemValue, DateTime filterValue, FilterOperator filterOperator, bool distinct)
        {
            if (!itemValue.HasValue)
            {
                return filterOperator == FilterOperator.IsNull;
            }
 
            // Only use date part when not comparing to distinct values
            var adjustedDate = distinct ? itemValue.Value : itemValue.Value.Date;
            switch (filterOperator)
            {
                case FilterOperator.IsLessThan:
                    return adjustedDate < filterValue;
                case FilterOperator.IsLessThanOrEqualTo:
                    return adjustedDate <= filterValue;
                case FilterOperator.IsEqualTo:
                    return adjustedDate == filterValue;
                case FilterOperator.IsNotEqualTo:
                    return adjustedDate != filterValue;
                case FilterOperator.IsGreaterThanOrEqualTo:
                    return adjustedDate >= filterValue;
                case FilterOperator.IsGreaterThan:
                    return adjustedDate > filterValue;
                case FilterOperator.IsNull:
                    return false;
                case FilterOperator.IsNotNull:
                    return true;
                // We don't support the rest of the operators
                case FilterOperator.StartsWith:
                case FilterOperator.EndsWith:
                case FilterOperator.Contains:
                case FilterOperator.DoesNotContain:
                case FilterOperator.IsContainedIn:
                case FilterOperator.IsNotContainedIn:
                case FilterOperator.IsEmpty:
                case FilterOperator.IsNotEmpty:
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}
0
Vladimir Stoyanov
Telerik team
answered on 05 Feb 2019, 04:53 PM
Hello Magnus,

I am glad to hear that you found a solution suitable for your scenario. 

Thank you for sharing your approach with the community. 

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
GridView
Asked by
Magnus
Top achievements
Rank 1
Answers by
Stefan
Telerik team
Magnus
Top achievements
Rank 1
Magnus
Top achievements
Rank 1
Vladimir Stoyanov
Telerik team
Share this question
or