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