Skip to content

Instantly share code, notes, and snippets.

@crowbarsolutions
Last active April 12, 2023 09:16
Show Gist options
  • Save crowbarsolutions/784910a96bb6821b5fb5 to your computer and use it in GitHub Desktop.
Save crowbarsolutions/784910a96bb6821b5fb5 to your computer and use it in GitHub Desktop.
Have Kendo or Telerik grid date time filters to ignore or not account for the time in the underlying data
using System.Web.Mvc;
using MyProject.ModelBinders;
using Kendo.Mvc.UI;
namespace MyProject.CustomAttributes
{
public class CustomDataSourceRequestAttribute : DataSourceRequestAttribute
{
public override IModelBinder GetBinder()
{
return new CustomDataSourceRequestModelBinder();
}
}
}
using System;
using System.Linq;
using System.Web.Mvc;
using Kendo.Mvc;
using Kendo.Mvc.UI;
namespace MyProject.ModelBinders
{
/// <summary>
/// DateTime filtering is horribly unintuitive in Kendo Grids when a non-default (00:00:00) time is attached
/// to the grid's datetime data. We use this custom model binder to transform the grid filters to return
/// results that ignore the attached time, leading to intuitive results that make users happy.
///
/// To use the code, substitute the [DataSourceRequest] attribute for [CustomDataSourceRequest] in your MVC controller
///
/// If you will be comparing UTC datetimes, you will need to add ".ToUniversalTime()" to the end of all DateTime constructors
/// inside the switch statement below.
/// </summary>
public class CustomDataSourceRequestModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Get an instance of the original kendo model binder and call the binding method
var baseBinder = new DataSourceRequestModelBinder();
var request = (DataSourceRequest)baseBinder.BindModel(controllerContext, bindingContext);
if (request.Filters != null && request.Filters.Count > 0)
{
var transformedFilters = request.Filters.Select(TransformFilterDescriptors).ToList();
request.Filters = transformedFilters;
}
return request;
}
private IFilterDescriptor TransformFilterDescriptors(IFilterDescriptor filter)
{
if (filter is CompositeFilterDescriptor)
{
var compositeFilterDescriptor = filter as CompositeFilterDescriptor;
var transformedCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = compositeFilterDescriptor.LogicalOperator };
foreach (var filterDescriptor in compositeFilterDescriptor.FilterDescriptors)
{
transformedCompositeFilterDescriptor.FilterDescriptors.Add(TransformFilterDescriptors(filterDescriptor));
}
return transformedCompositeFilterDescriptor;
}
if (filter is FilterDescriptor)
{
var filterDescriptor = filter as FilterDescriptor;
if (filterDescriptor.Value is DateTime)
{
var value = (DateTime)filterDescriptor.Value;
switch (filterDescriptor.Operator)
{
case FilterOperator.IsEqualTo:
//convert the "is equal to <date><time>" filter to a "is greater than or equal to <date> 00:00:00" AND "is less than <date + 1day> 00:00:00"
var isEqualCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And };
isEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
FilterOperator.IsGreaterThanOrEqualTo, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0)));
isEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
FilterOperator.IsLessThan, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0).AddDays(1)));
return isEqualCompositeFilterDescriptor;
case FilterOperator.IsNotEqualTo:
//convert the "is not equal to <date><time>" filter to a "is less than <date> 00:00:00" OR "is greater than or equal to <date + 1day> 00:00:00"
var notEqualCompositeFilterDescriptor = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.Or };
notEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
FilterOperator.IsLessThan, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0)));
notEqualCompositeFilterDescriptor.FilterDescriptors.Add(new FilterDescriptor(filterDescriptor.Member,
FilterOperator.IsGreaterThanOrEqualTo, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0).AddDays(1)));
return notEqualCompositeFilterDescriptor;
case FilterOperator.IsGreaterThanOrEqualTo:
//convert the "is greater than or equal to <date><time>" filter to a "is greater than or equal to <date> 00:00:00"
filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 0, 0, 0);
return filterDescriptor;
case FilterOperator.IsGreaterThan:
//convert the "is greater than <date><time>" filter to a "is greater than or equal to <date + 1day> 00:00:00"
var greaterThanFilterDescriptor = new FilterDescriptor(filterDescriptor.Member, FilterOperator.IsGreaterThanOrEqualTo, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0).AddDays(1));
return greaterThanFilterDescriptor;
case FilterOperator.IsLessThanOrEqualTo:
//convert the "is less than or equal to <date><time>" filter to a "is less than <date + 1day> 00:00:00"
var lessThanEqualToFilterDescriptor = new FilterDescriptor(filterDescriptor.Member, FilterOperator.IsLessThan, new DateTime(value.Year, value.Month, value.Day, 0, 0, 0).AddDays(1));
return lessThanEqualToFilterDescriptor;
case FilterOperator.IsLessThan:
//convert the "is less than <date><time>" filter to a "is less than <date> 00:00:00"
filterDescriptor.Value = new DateTime(value.Year, value.Month, value.Day, 0, 0, 0);
return filterDescriptor;
case FilterOperator.IsNull:
return filterDescriptor;
case FilterOperator.IsNotNull:
return filterDescriptor;
default:
throw new Exception(string.Format("Filter operator '{0}' is not supported for DateTime member '{1}'", filterDescriptor.Operator, filterDescriptor.Member));
}
}
}
return filter;
}
}
}
namespace MyProject.Controllers
{
public class ExampleController : Controller
{
public ActionResult ReadGridData([CustomDataSourceRequest] DataSourceRequest request)
{
var myGridData = _myDataRepository.GetData();
return Json(myGridData.ToDataSourceResult(request));
}
}
}
The MIT License (MIT)
Copyright (c) 2015 Crowbar Solutions Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment