This is a migrated thread and some comments may be shown as answers.

Aggregate Function on Dynamically Created Columns

14 Answers 774 Views
GridView
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Jarrod
Top achievements
Rank 1
Jarrod asked on 01 Mar 2011, 08:38 PM
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++;
}






14 Answers, 1 is accepted

Sort by
0
Yavor Georgiev
Telerik team
answered on 05 Mar 2011, 11:39 PM
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!
0
Jarrod
Top achievements
Rank 1
answered on 21 Mar 2011, 03:40 PM
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
0
Jarrod
Top achievements
Rank 1
answered on 21 Mar 2011, 03:55 PM
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
0
Yavor Georgiev
Telerik team
answered on 21 Mar 2011, 04:20 PM
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
0
Jarrod
Top achievements
Rank 1
answered on 21 Mar 2011, 04:29 PM
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)
0
Jarrod
Top achievements
Rank 1
answered on 21 Mar 2011, 04:46 PM
Hi Yavor,

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

Regards,

Jarrod
0
Yavor Georgiev
Telerik team
answered on 22 Mar 2011, 11:05 AM
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
0
Jarrod
Top achievements
Rank 1
answered on 22 Mar 2011, 11:29 AM
Hi Yavor,

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

Any other suggestions?

Jarrod
0
Jessie
Top achievements
Rank 1
answered on 14 Jun 2011, 08:12 AM
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 
0
Yavor Georgiev
Telerik team
answered on 14 Jun 2011, 08:57 AM
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
0
Jessie
Top achievements
Rank 1
answered on 15 Jun 2011, 01:23 AM

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


0
Jessie
Top achievements
Rank 1
answered on 15 Jun 2011, 04:46 AM
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
0
Yavor Georgiev
Telerik team
answered on 15 Jun 2011, 09:33 AM
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
0
Ratna
Top achievements
Rank 1
answered on 06 Oct 2011, 11:13 PM
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.)







Tags
GridView
Asked by
Jarrod
Top achievements
Rank 1
Answers by
Yavor Georgiev
Telerik team
Jarrod
Top achievements
Rank 1
Jessie
Top achievements
Rank 1
Ratna
Top achievements
Rank 1
Share this question
or