Aggregate Function on Dynamically Created Columns

15 posts, 0 answers
  1. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 01 Mar 2011 Link to this post

    Hi I seem to have a problem with attaching an aggregate function to a dynamically generated column. Essentially I have a collection of rows and for each Data item I have another collection, which I essentially pivot on and create dynamic columns on. The problem I am having is that I need to be able to provide the sum of rows for each of the columns. But it doesn't seem to work.

    I have inserted a copy of the offending piece of code. It all works fine except for the aggregation function. Any help would be greatly appreciated.

    foreach (ItemModel fund in _viewModel.Item)
    {
        var binding = new Binding(string.Format("FundOpportunitySummaries[{0}].HoldingValueSystemCurrency", iterator))
                          {
                              Converter = Resources["currencyConverter"] as CurrencyFormatter,
                              ConverterParameter = false
                          };
     
        var sumFunction = new SumFunction()
                            {
                                Caption = "$US(mm)",
                                ResultFormatString = "{}{0:###,##0.0}",
                                SourceField = string.Format("FundOpportunitySummaries[{0}].HoldingValueSystemCurrency", iterator)
                            };
     
        var column = new GridViewDataColumn
                         {
                             Header = fund.ShortName ?? fund.Name,
                             DataMemberBinding = binding,
                             CellStyleSelector = new SalesStageStyleSelector(),
                             IsSortable = true,
                             IsFilterable = false,
                             TextAlignment = TextAlignment.Right,
                             Width = 75,
                             UniqueName = string.Format("dyn_{0}", iterator)
                         };
     
        column.AggregateFunctions.Add(sumFunction);
     
        GridCompanies.Columns.Insert(position, column);
     
        position++; iterator++;
    }






  2. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 05 Mar 2011 Link to this post

    Hello Jarrod,

     We will investigate this and try to determine if it's a bug. In the mean time, you can use our generic AggregateFunction like so:
    var aggregate = new AggregateFunction<ItemModel, string>
    {
         AggregationExpression = models => string.Format("{0:###,##0.0}", models.Sum(model => model.FundOpportunitySummaries[iterator].HoldingValueSystemCurrency));
         Caption = "$US(mm)"
    };
     
    column.AggregateFunctions.Add(aggregate);


    Kind regards,
    Yavor Georgiev
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  3. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 21 Mar 2011 Link to this post

    Hi Yavor,

    Thanks for your response, I have tried this out but it doesn't seem to work for me.

    I have copied your code verbatim and it is still blank.

    Regards, Jarrod
  4. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 21 Mar 2011 Link to this post

    An interesting thing to note as well is that with the new code it crashes completely when I try and group, but the old way (from my original code sample) shows the Aggregate Function caption in the footer but with no value.

    Not sure if this provides you with any more information to go on?

    Regards, Jarrod
  5. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 21 Mar 2011 Link to this post

    Hi Jarrod,

     I think the problem might be in the fact that SourceField points to an indexer property. Could you please try setting the SourceField property of your SumFunction aggregate to a simple property of ItemModel, which is not an indexer? Also, could you please post the exception details you get when you try to group using the generic AggregateFunction? This might help locate the problem.

    Best wishes,
    Yavor Georgiev
    the Telerik team
  6. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 21 Mar 2011 Link to this post

    Hi Yavor,

    The error that I received is as follows:

    Microsoft JScript runtime error: Unhandled Error in Silverlight Application Expression of type 'System.Linq.IGrouping`2[System.Int32,dCRMxRM.Dashboards.Models.CompanyModel]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable`1[dCRMxRM.Dashboards.Models.Interfaces.ICompanyModel]' of method 'System.Nullable`1[System.Double] Sum[ICompanyModel](System.Collections.Generic.IEnumerable`1[dCRMxRM.Dashboards.Models.Interfaces.ICompanyModel], System.Func`2[dCRMxRM.Dashboards.Models.Interfaces.ICompanyModel,System.Nullable`1[System.Double]])'   at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi)
       at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments)
       at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
       at System.Linq.Expressions.MethodCallExpressionN.Rewrite(Expression instance, IList`1 args)
       at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
       at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
       at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
       at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
       at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
       at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
       at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
       at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
       at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
       at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
       at Telerik.Windows.Data.Expressions.ParameterRewriter.Rewrite(LambdaExpression lambda, ParameterExpression newParameter)
       at Telerik.Windows.Data.AggregateFunction`2.CreateAggregateExpression(Expression enumerableExpression)
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.<ProjectionPropertyValueExpressions>b__3(AggregateFunction f)
       at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.CreateProjectionInitExpression()
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.CreateAggregateFunctionsProjectionMemberBinding()
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.<CreateMemberBindings>d__0.MoveNext()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
       at System.Linq.Expressions.Expression.MemberInit(NewExpression newExpression, IEnumerable`1 bindings)
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.CreateSelectBodyExpression()
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.CreateResultSelectorExpression()
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilder.get_ResultSelectorExpression()
       at Telerik.Windows.Data.Expressions.GroupDescriptorExpressionBuilderBase.CreateQuery()
       at Telerik.Windows.Data.Expressions.GroupDescriptorCollectionExpressionBuilder.CreateQuery()
       at Telerik.Windows.Data.QueryableExtensions.GroupBy(IQueryable source, IEnumerable`1 groupDescriptors)
       at Telerik.Windows.Data.QueryableExtensions.Aggregate(IQueryable source, IEnumerable`1 aggregateFunctions)
       at Telerik.Windows.Controls.GridView.GridViewDataControl.CreateAggregateResults()
       at Telerik.Windows.Controls.GridView.GridViewDataControl.OnItemsCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
       at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
       at Telerik.Windows.Data.DataItemCollection.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
       at Telerik.Windows.Data.DataItemCollection.OnCollectionViewCollectionChanged(NotifyCollectionChangedEventArgs e)
       at Telerik.Windows.Data.DataItemCollection.Telerik.Windows.Data.IWeakEventListener<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.ReceiveWeakEvent(Object sender, NotifyCollectionChangedEventArgs e)
       at Telerik.Windows.Data.WeakEvent.WeakListener`1.Handler(Object sender, TArgs args)
       at Telerik.Windows.Data.QueryableCollectionView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
       at Telerik.Windows.Data.QueryableCollectionView.RefreshOverride()
       at Telerik.Windows.Data.QueryableCollectionView.RefreshOrDefer()
       at Telerik.Windows.Data.QueryableCollectionView.ProcessSynchronousCollectionChanged(NotifyCollectionChangedEventArgs args)
       at Telerik.Windows.Data.QueryableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
       at Telerik.Windows.Data.QueryableCollectionView.OnRefresh()
       at Telerik.Windows.Data.QueryableCollectionView.EndDefer()
       at Telerik.Windows.Data.QueryableCollectionView.DeferHelper.Dispose()
       at Telerik.Windows.Data.DataItemCollection.EndDefer()
       at Telerik.Windows.Data.DataItemCollection.DeferHelper.Dispose()
       at Telerik.Windows.Controls.GridView.GridViewDataControl.PerformGrouping(IGroupDescriptor descriptor, Nullable`1 insertionIndex, GroupingEventAction action)
       at Telerik.Windows.Controls.GridView.GridViewDataControl.<>c__DisplayClass4b.<RequestGrouping>b__4a()
       at Telerik.Windows.Controls.CursorManager.PerformTimeConsumingOperation(FrameworkElement frameworkElement, Action action)
       at Telerik.Windows.Controls.GridView.GridViewDataControl.RequestGrouping(IGroupDescriptor descriptor, Nullable`1 insertionIndex, GroupingEventAction action)
       at Telerik.Windows.Controls.GridView.GridViewDataControl.OnGroupPanelCellRequestGroupAction(Object sender, RequestGroupActionEventArgs e)
  7. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 21 Mar 2011 Link to this post

    Hi Yavor,

    I also tried a static indexor and it was still not displaying anything.

    Regards,

    Jarrod
  8. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 22 Mar 2011 Link to this post

    Hi Jarrod,

     I'm not sure whether the indexer is instance or static would make any difference. A possible approach when dealing with indexed properties is to set the SourceFieldType property. Please try setting the SourceFieldType property of your SumFunction to the type of the HoldingValueSystemCurrency property.

    Greetings,
    Yavor Georgiev
    the Telerik team
  9. Jarrod
    Jarrod avatar
    15 posts
    Member since:
    Jan 2011

    Posted 22 Mar 2011 Link to this post

    Hi Yavor,

    I tried setting the SourceFieldType = typeof(double) but this still didn't work.

    Any other suggestions?

    Jarrod
  10. Jessie
    Jessie avatar
    3 posts
    Member since:
    Jun 2011

    Posted 14 Jun 2011 Link to this post

    Hi all,

    I have the similar issue now.

    And I have tried the way that Yavor suggested:

    var aggregate = new AggregateFunction<ItemModel, string>
    {
         AggregationExpression = models => string.Format("{0:###,##0.0}", models.Sum(model => model.FundOpportunitySummaries[iterator].HoldingValueSystemCurrency));
         Caption = "$US(mm)"
    };
     
    column.AggregateFunctions.Add(aggregate);

    The aggregate function can work without exception. But weird thing is the aggregate result always is the same as last column rather than the related column.

    Anyone know why and how to fix the issue?

    Thank u~~

    Jessie 
  11. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 14 Jun 2011 Link to this post

    Hello Jessie,

     I'm afraid I'll need some more info to diagnose the problem. Can you please share some code and a screenshot of what your RadGridView looks like?

    Kind regards,
    Yavor Georgiev
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  12. Jessie
    Jessie avatar
    3 posts
    Member since:
    Jun 2011

    Posted 14 Jun 2011 Link to this post

    Hi Yavor,

    In my project, I want to show a screen with dynamically created columns whose footer displays the aggregate sum value of each associated dynamically created columns, as shown in the attached screenshot.

    The associated xaml code is: 

    <Window x:Class="Test.TestAggregateFunction"
        Title="TestAggregateFunction" Height="300" Width="600">
        <Grid>
            <telerik:RadGridView
                    x:Name="TestDataGrid"
                    ItemsSource="{Binding }"
                    AutoGenerateColumns="False"
                    telerik:StyleManager.Theme="Office_Blue"
                    RowIndicatorVisibility="Collapsed"
                    CanUserFreezeColumns="False"
                    CanUserReorderColumns="True"
                    CanUserDeleteRows="False"
                    CanUserInsertRows="False"
                    CanUserSortColumns="True"
                    Width="Auto"
                    VerticalAlignment="Stretch"
                    ShowColumnFooters="True"
                    ShowGroupPanel="False"
                    IsReadOnly="True">
            </telerik:RadGridView>
        </Grid>
    </Window>


    And the code behind is:
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using Telerik.Windows.Controls;
    using Telerik.Windows.Data;
     
    namespace Test
    {
       /// <summary>
       /// Interaction logic for TestAggregateFunction.xaml
       /// </summary>
       public partial class TestAggregateFunction : Window
       {
          public TestAggregateFunction()
          {
             InitializeComponent();
              
             string[] allItemCats = {"A", "B", "C", "D", "E"};
     
             ICollection<ItemModel> items = this.GetItemData();
     
             this.TestDataGrid.Columns.Add(
                   new GridViewDataColumn()
                   {
                      UniqueName = "ID"
                      , DataMemberBinding = new Binding("ID")
                   }
                );
     
             foreach (var itemCat in allItemCats)
             {
     
                GridViewColumn itemColumn = new GridViewDataColumn()
                {
                   UniqueName = itemCat,
                   DataMemberBinding = new Binding(string.Format("ItemCatList[{0}]", itemCat)),
                };
     
                var aggregateFunc = new AggregateFunction<ItemModel, int>()
                {
                   AggregationExpression = beans =>
                    beans.Select(x => x.ItemCatList[itemCat]).Where(y => y != null).Sum(),
                };
     
                itemColumn.AggregateFunctions.Add(aggregateFunc);
     
                this.TestDataGrid.Columns.Add(itemColumn);
             }
     
             this.TestDataGrid.ItemsSource = items;
          }
     
          private ICollection<ItemModel> GetItemData()
          {
             ICollection<ItemModel> items = new List<ItemModel>();
     
             ItemModel i1 = new ItemModel();
             i1.ID = "i1";
             i1.ItemCatList.Add("A", 1);
             i1.ItemCatList.Add("B", 1);
             i1.ItemCatList.Add("E", 1);
             items.Add(i1);
     
             ItemModel i2 = new ItemModel();
             i2.ID = "i2";
             i2.ItemCatList.Add("C", 1);
             i2.ItemCatList.Add("D", 1);
             i2.ItemCatList.Add("E", 1);
             items.Add(i2);
     
             ItemModel i3 = new ItemModel();
             i3.ID = "i3";
             i3.ItemCatList.Add("B", 1);
             i3.ItemCatList.Add("E", 1);
             items.Add(i3);
     
             ItemModel i4 = new ItemModel();
             i4.ID = "i4";
             i4.ItemCatList.Add("E", 10);
             items.Add(i4);
     
             return items;
          }
       }
     
       public class ItemModel
       {
          public IDictionary<string, int> ItemCatList { get; set; }
     
          public string ID { get; set; }
     
          public ItemModel()
          {
             this.ItemCatList = new Dictionary<string, int>();
          }
       }
    }

    My issue is also shown in the screenshot: all the aggregate result is actually the result of the last column rather than the result of each column.

    Could you give me some suggestions on how to calculate the aggregatefunction footer under this circumstance?

    Thanks a million ~~~

    Yours sincerely,

    Jessie


  13. Jessie
    Jessie avatar
    3 posts
    Member since:
    Jun 2011

    Posted 14 Jun 2011 Link to this post

    In addition, the data actually bind to each column in the real project is an object. Thus, the aggregation is calculated based on a property of each object. So That is the reason I am using generic aggregate function. 


    Jessie
  14. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 15 Jun 2011 Link to this post

    Hello Jessie,

     The problem is that you're capturing the value of the enumerator (the "itemCat" variable) in the LINQ expression, which creates a closure around the enumerator, hence the "itemCat" variable in the expression always equals the last value of the enumerator - "E".

     The answer is to assign "itemCat" to a local variable in the foreach loop and use that local variable in the expression. This way the expression closes around the value of the local variable, not the enumerator.

    Regards,
    Yavor Georgiev
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  15. Ratna
    Ratna avatar
    2 posts
    Member since:
    Oct 2011

    Posted 06 Oct 2011 Link to this post

    I  have a radcolumn in the xaml file and i m trying to add an aggregate function during runtime.

    my xaml code is:
    <telerik:RadGridView x:Name="PunchOut" x:Uid="dgPunchOut" AutoGenerateColumns="False" Margin="5" Height="440" CanUserFreezeColumns="False" IsSynchronizedWithCurrentItem="True"
                                                 ShowGroupFooters="True">                            
                                <telerik:RadGridView.Columns>
                                    <telerik:GridViewDataColumn UniqueName="PartNumber" Header="Part Number" Width="145"  DataMemberBinding="{Binding PartNumber}" IsReadOnly="True" />
                                    <telerik:GridViewDataColumn UniqueName="Price" Header="Price" DataMemberBinding="{Binding Price}" Width="100" IsReadOnly="True" />
                                    <telerik:GridViewDataColumn UniqueName="SelectedQuantity" Header="SelectedQuantity" Width="45"  IsReadOnly="False" DataMemberBinding="{Binding SelectedQuantity}" />                                     
                                </telerik:RadGridView.Columns>
                            </telerik:RadGridView>

    Here, for price column, im adding aggregate function in code:
     public void SetSourceField()
            {
               
                SumFunction sumfunction = new SumFunction();
                GridViewColumn price = new GridViewColumn();
                price = PunchOut.Columns["Price"];

                sumfunction.SourceField = "Price";
                sumfunction.ResultFormatString = "{}Total:{0:c}";

                price.AggregateFunctions.Add(sumfunction);
                this.PunchOut.Rebind(); // i dont know if this is necessary
            }

    i bind it during a call to the function:
      private void GetItemsCompleted(object sender, PunchOutServiceNamespace.GetItemsCompletedEventArgs e)
            {
                PunchOut.ItemsSource = e.Result;// this e.result comes from wcf service so i dont have access to any datasource other than through e.result();
                SetSourceField(); 
                PunchOut.Rebind(); // punch out is the name of my gridview
            }

    this aggregate function does not show up.

    (i cannot have the binding property set in the xaml file).
    i have to write the aggregate function only in the code behind and not in the xaml file.

    please let me know what im missing. (something like a refresh or additional insert etc.)







Back to Top