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
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
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
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
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(); } } }}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
