如何实施Telerik Custom Aggregate?

时间:2012-06-11 16:33:14

标签: c# asp.net-mvc-3 telerik aggregate telerik-grid

我有一个Telerik Grid,它有一个需要显示列总和的页脚。但是,其中一个列的数据类型为TimeSpan,Telerik的Sum聚合不支持。我需要使用GridBoundColumnBuilder.Aggregate()来添加聚合。 所以我想基本上问题是如何在telerik的Aggregate()方法中引用我的自定义聚合。如果你发现其他任何我做错了,请随意指出:)使用{{ 3}},我为自定义聚合创建了一个类,名为SumAggregate,如下所示。 (请注意,这还没有完成 - 它取自文章。它实际上实现了一个完全不同的聚合)

SumAggregate.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Web.Mvc;

namespace TelerikPOC.CustomAggregates
{
    public class SumAggregate : AggregateFunction
    {
        private System.Collections.Generic.List<object> distinctValues;

        /// <summary>
        /// Initializes the current aggregate function to its initial
        /// state ready to accumulate and merge values.
        /// </summary>
        /// <remarks>
        /// This method is called every time the accumulation of values 
        /// must start over for a new subset of records from the data source.
        /// </remarks>
        public void Init()
        {
            this.distinctValues = new System.Collections.Generic.List<object>();
        }

        /// <summary>
        /// Accumulates new argument values to the current aggregate function.
        /// </summary>
        /// <remarks>
        /// This aggregate function accepts one argument:
        /// number - a numeric value to accumulate to the aggregate function;
        /// </remarks>
        public void Accumulate(object[] values)
        {
            if (!distinctValues.Contains(values[0]))
            {
                distinctValues.Add(values[0]);
            }
        }

        /// <summary>
        /// Merges the specified aggregate function to the current one.
        /// </summary>
        /// <param name="Aggregate">
        /// Specifies an aggregate function to be merged to the current one.
        /// </param>
        /// <remarks>
        /// This method allows the reporting engine to merge two accumulated
        /// subsets of the same aggregate function into a single result.
        /// </remarks>
        public void Merge(AggregateFunction aggregate)
        {
            // Accumulate the values of the specified aggregate function.
            System.Collections.Generic.List<object> sums1 =     ((SumAggregate)aggregate).distinctValues;
            foreach (object o in sums1)
            {
                this.Accumulate(new object[] { o });
            }
        }

        /// <summary>
        /// Returns the currently accumulated value of the aggregate function.
        /// </summary>
        /// <returns>
        /// The currently accumulated numeric value of the aggregate function.
        /// </returns>
        public object GetValue()
        {
            return this.distinctValues.Count;
        }
    }
}

这是添加聚合的代码。这与 index.cshtml 中的代码冲突,但我想包括两种添加聚合的方法,只是为了给出答案的更多选项。这需要修改为使用自定义聚合而不是内置聚合,就像它现在使用的那样。

GridHelper.cs (未完成 - 将添加逻辑以循环遍历列等等。顺便说一下,如果有人愿意帮助我 我也会非常感激,不过我承认我还没有尝试过任何东西。)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Web.Mvc.UI.Fluent;
using TelerikPOC.CustomAggregates;

namespace TelerikPOC.Helpers
{
    public class GridHelper
    {
        public static void AddAggregateToColumn(GridBoundColumnBuilder<dynamic> columnBuilder, string Aggregate)
        {
            switch (Aggregate)
            {
                case "Sum":
                    {
                        columnBuilder.Aggregate(aggregates => aggregates.Sum())
                            .GroupFooterTemplate(result => "Sum:" + result.Sum)
                            .ClientFooterTemplate("Sum: <#= Sum #>")
                            .FooterTemplate(result => "Total: " + result.Sum);
                    }
                    break;
            }
        }
    }
}

然后我使用HtmlHelper类来构建/渲染telerik网格,如下所示:

来自 Index.cshtml :(请务必阅读右侧的评论)

@{
    Html.Telerik()
    .Grid(Model)
    .Name("statisticalGrid")
    .Columns(columns =>
    {
        columns.Bound(o => o.PlanID).Aggregate(something);         //This is probably going to be where 
        columns.Bound(o => o.SessionID).Aggregate(something);      //I need the help. Just not sure
        columns.Bound(o => o.TimeSpan).Aggregate(something);       //how to reference the custom
        columns.Bound(o => o.TimeSpanDouble).Aggregate(something); //aggregate here, in
    })                                                             //place of `something`
    .Sortable(sortable => sortable.Enabled(true))
    .Filterable()
    .Pageable(page => page.PageSize(25))
    .Reorderable(reorder => reorder.Columns(true))
    .Groupable(groupable => groupable.Enabled(true))
    .ClientEvents(events => events
        .OnColumnReorder("onReorder"))
    .Render();
}

所以我想基本上问题是如何在telerik的Aggregate()方法中引用我的自定义聚合。如果你发现其他任何我做错了,请随意指出:)

编辑:注意到我必须在SumAggregate类中实现方法CreateAggregateExpression(Expression, bool)。但不完全确定如何实现它。

上次编辑:我使用自定义列构建器方法来构建columms,因此我不确定如何在此处进行格式化。我能够格式化总和,但不能格式化列的其余部分,因为在telerik调用的上下文之外,我无法访问item变量。这基本上就是我的列构建逻辑:

在telerik代码中,

.Columns(a => GridHelper.GenerateColumns(a, Model.SelectedReport))

生成列类似于:

public static void GenerateColumns(GridColumnFactory<dynamic> columnFactory, Company.Project.Data.Entity.Report reportStructure)
        {
            foreach (var columnLayout in reportStructure.ReportCols.OrderBy(o => o.ColumnSequence))
            {
                GridBoundColumnBuilder<dynamic> columnBuilder = columnFactory.Bound(columnLayout.ColumnType);
                //do other stuff here (add aggregates, formatting, etc)
            }

那么我将如何在此上下文中进行格式化?

1 个答案:

答案 0 :(得分:5)

您可以做的一件事是在“Days”中添加另一个代表TimeSpan的列。然后,您不必使用自定义聚合。

型号:

public List<Objects.Temp> GetTemp()
{
  List<Objects.Temp> ltemp = new List<Objects.Temp>();
  System.Random r = new Random();
  Objects.Temp t = new Objects.Temp();
  t.Name = "One";
  t.start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(25) - t.start;
  t.tsDays = t.ts.Days;
  ltemp.Add(t);
  t = new Objects.Temp();
  t.Name = "Two";
  t.start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(15) - t.start;
  t.tsDays = t.ts.Days;
  ltemp.Add(t);
  t = new Objects.Temp();
  t.Name = "Three";
  t.start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(55) - t.start;
  t.tsDays = t.ts.Days;
  ltemp.Add(t);

  return ltemp;
}

查看:

@(Html.Telerik().Grid(Model)
  .Name("Grid")
  .Columns(columns =>
  {
    columns.Bound(o => o.Name)
            .Aggregate(aggregates => aggregates.Count())
            .FooterTemplate(@<text>Total Count: @item.Count</text>)
            .GroupFooterTemplate(@<text>Count: @item.Count</text>);

    columns.Bound(o => o.start)
            .Width(200)
            .Aggregate(aggreages => aggreages.Max())
      //.Format("{0:c}")
            .FooterTemplate(@<text>Max: @item.Max</text>)
            .GroupFooterTemplate(@<text>Max: @item.Max</text>);

    columns.Bound(o => o.Value)
            .Width(200)
            .Aggregate(aggregates => aggregates.Average())
            .FooterTemplate(@<text>Average: @item.Average</text>)
            .GroupFooterTemplate(@<text>Average: @item.Average</text>);

    columns.Bound(o => o.ts)
            .Width(100)
            .Aggregate(aggregates => aggregates.Count().Min().Max())
            .FooterTemplate(
            @<text>
                <div>Min: @item.Min</div>
                <div>Max: @item.Max</div>
            </text>)
            .GroupHeaderTemplate(@<text>@item.Title: @item.Key (Count: @item.Count)</text>);

    columns.Bound(o => o.tsDays)
          .Width(100)
          .Aggregate(aggregates => aggregates.Sum())
          .FooterTemplate(
          @<text>
                <div>Sum: @item.Sum Days</div>
            </text>)
          .GroupHeaderTemplate(@<text>@item.Title: @item.Key (Sum: @item.Sum)</text>);
  })
    .Sortable()

你得到的东西看起来像这样:

enter image description here

版本2

我更新了我的答案,以便只有一个TimeSpan列与TimeSpan相加并以TimeSpan格式显示它。列中的数据实际上是TimeSpan.TotalMilliseconds,但它使用模板显示为TimeSpan,并使用TimeSpan.FromMilliseconds方法格式化TimeSpan。我认为这样做比创建自定义聚合类更容易,如果甚至可以使用MVC扩展。

型号:

public class Temp
{
  public string Name { get; set; }
  public DateTime Start { get; set; }
  public double Value { get; set; }
  public TimeSpan ts { get; set; }
  public double tsMilliseconds { get; set; }
}


  List<Objects.Temp> ltemp = new List<Objects.Temp>();
  System.Random r = new Random();
  Objects.Temp t = new Objects.Temp();
  t.Name = "One";
  t.Start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(25) - t.Start;
  t.tsMilliseconds = t.ts.TotalMilliseconds;
  ltemp.Add(t);
  t = new Objects.Temp();
  t.Name = "Two";
  t.Start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(15) - t.Start;
  t.tsMilliseconds = t.ts.TotalMilliseconds;
  ltemp.Add(t);
  t = new Objects.Temp();
  t.Name = "Three";
  t.Start = DateTime.Now;
  t.Value = r.NextDouble();
  t.ts = DateTime.Today.AddDays(55) - t.Start;
  t.tsMilliseconds = t.ts.TotalMilliseconds;
  ltemp.Add(t);

查看:

@(Html.Telerik().Grid(Model)
  .Name("Grid")
  .Columns(columns =>
  {
    columns.Bound(o => o.Name)
            .Aggregate(aggregates => aggregates.Count())
            .FooterTemplate(@<text>Total Count: @item.Count</text>)
            .GroupFooterTemplate(@<text>Count: @item.Count</text>);

    columns.Bound(o => o.Start)
            .Template(@<text>@item.Start.ToShortDateString()</text>)
            .Aggregate(aggreages => aggreages.Max())
            .FooterTemplate(@<text>Max: @item.Max.Format("{0:d}")</text>)
            .GroupHeaderTemplate(@<text>Max: @item.Max.Format("{0:d}")</text>)
            .GroupFooterTemplate(@<text>Max: @item.Max.Format("{0:d}")</text>);

    columns.Bound(o => o.Value)
            .Width(200)
            .Aggregate(aggregates => aggregates.Average())
            .FooterTemplate(@<text>Average: @item.Average</text>)
            .GroupFooterTemplate(@<text>Average: @item.Average</text>);

    columns.Bound(o => o.tsMilliseconds)
          .Width(100)
          .Aggregate(aggregates => aggregates.Sum())
          .Template(@<text>@TimeSpan.FromMilliseconds(@item.tsMilliseconds)</text>)
          .Title("TimeSpan")
          .FooterTemplate(
          @<text>
                <div>Sum: @TimeSpan.FromMilliseconds( @Convert.ToDouble( @item.Sum.Value.ToString() ) ) </div>
            </text>)
          //header if you group by TimeSpan
          .GroupHeaderTemplate(@<text>@item.Title: @item.Key (Sum: @TimeSpan.FromMilliseconds(@Convert.ToDouble(@item.Sum.Value.ToString())))</text>)
          //footer for grouping
          .GroupFooterTemplate(@<text>Sum: @TimeSpan.FromMilliseconds(@Convert.ToDouble(@item.Sum.Value.ToString()))</text>);
  })
    .Sortable()
    .Groupable(settings => settings.Groups(groups => groups.Add(o => o.Start)))
) 

没有分组产生这个:

TimeSpan Aggregate

通过分组产生:

With Grouping