<
UserControl
x:Class
=
"tmpTelerikGridAggregationBug.MainPage"
xmlns:Controls
=
"clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:Data
=
"clr-namespace:Telerik.Windows.Data;assembly=Telerik.Windows.Data"
Height
=
"300"
Width
=
"400"
>
<
StackPanel
>
<
Controls:RadGridView
x:Name
=
"ucGrid"
AutoExpandGroups
=
"True"
AutoGenerateColumns
=
"False"
>
<
Controls:RadGridView.Columns
>
<
Controls:GridViewDataColumn
UniqueName
=
"Name"
/>
<
Controls:GridViewDataColumn
UniqueName
=
"Age"
>
<
Controls:GridViewDataColumn.AggregateFunctions
>
<
Data:SumFunction
SourceField
=
"Age"
/>
</
Controls:GridViewDataColumn.AggregateFunctions
>
</
Controls:GridViewDataColumn
>
<
Controls:GridViewDataColumn
UniqueName
=
"Group"
/>
</
Controls:RadGridView.Columns
>
</
Controls:RadGridView
>
<
Button
x:Name
=
"ucButton"
Click
=
"ucButton_Click"
Content
=
"0"
/>
</
StackPanel
>
</
UserControl
>
namespace
tmpTelerikGridAggregationBug
{
public
partial
class
MainPage : UserControl
{
public
MainPage()
{
InitializeComponent();
var groupDescriptor =
new
ColumnGroupDescriptor { Column =
this
.ucGrid.Columns[
"Group"
] };
this
.ucGrid.GroupDescriptors.Add( groupDescriptor );
ucGrid.ItemsSource =
new
ObservableCollection<Person>
{
new
Person{Name=
"N1"
, Age=1, Group=
"G1"
},
new
Person{Name=
"N2"
, Age=2, Group=
"G1"
},
new
Person{Name=
"N3"
, Age=3, Group=
"G2"
},
new
Person{Name=
"N4"
, Age=4, Group=
"G2"
},
};
}
private
void
ucButton_Click(
object
sender, RoutedEventArgs e )
{
int
newValue =
new
Random().Next( 9 ) + 1;
ucButton.Content = newValue;
UpdateData( newValue );
}
private
void
UpdateData(
int
value)
{
foreach
( var person
in
ucGrid.Items.Cast<Person>() )
{
person.Age += value;
}
ucGrid.CalculateAggregates();
}
}
public
class
Person : INotifyPropertyChanged
{
private
int
_age;
[Required]
public
string
Group {
get
;
set
; }
public
string
Name {
get
;
set
; }
public
int
Age
{
get
{
return
_age; }
set
{
_age = value;
OnPropertyChanged(
"Age"
);
}
}
#region Implementation of INotifyPropertyChanged
public
event
PropertyChangedEventHandler PropertyChanged;
protected
void
OnPropertyChanged(
string
name )
{
var handler = PropertyChanged;
if
( handler !=
null
)
{
handler(
this
,
new
PropertyChangedEventArgs( name ) );
}
}
#endregion
}
}
15 Answers, 1 is accepted
You are right, the aggregate functions in the group footer are not recalculated by the CalculateAggregates() method. This is due to the fact that the group footers will be updated when the collection changes. When you change the person.Age value only the property changed is raised.
What you should use so that the group footers are calculated is to inform the GridView that the Collection has been changed, like so:
private
void
UpdateData(
int
value)
{var items = ucGrid.Items.Cast<Person>().ToList();
for
(
int
i = 0; i < ucGrid.Items.Count; i++)
{
var person = items[i];
if
(person !=
null
)
{
((IEditableCollectionView)
this
.clubsGrid.Items).EditItem(person);
person.Age += value;
((IEditableCollectionView)
this
.clubsGrid.Items).CommitEdit();
}
}
}
This way when the editing is done on the data level, the group aggregate footer will be updated accordingly.
Didie
the Telerik team
We currently have an observable collection, and we run the aggregate on a particular property.
This observable collection is updated via external REST calls to a server. In order to optimise performance, rather than blow away the observable and recreate it, we update the idividual items in the list (via a synchronisation method), and add/remove new or deleted items.
To do this via an EditItem/CommitEdit will make the code wildly and unmaintainable (and unusable outside of the gridview).
Are there any suggestions ?
I am currently trying out the RadControls_for_Silverlight4_2012_2_0912_DEV_hotfix .
(FWIW, adding/removing items does trigger an aggregate recalculation)
The aggregates will be updated when a CollectionChanged event is raised from the bound ItemsSource. If you can raise such a notification not using the EditItem/CommitEdit methods, then this will work for you.
Regards,Didie
the Telerik team
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.
I did try doing the "remove old item, insert updated item" thing. Which worked, but was very unperformant. I did read up on performance issues when the grid size resolves to infinity, but I also had performance problems when I gave the grid a static size.
Are there any other issues I should know about performance wise ?
I've gone over the performance considerations, and beleive they are all addressed (there is one complex binding, so I'll have a quick go at simplifiying that one ...)
Your first solution of iterating over the grid and executing EditItem and CommitEdit on all items is a very poor solution. Try doing this on a grid with over 500 rows.
There must be a better solution to this problem?
The aggregates should be updated once a Reset action is raised for the bound source collection. May I ask you to please confirm if you test with the latest version?
Didie
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
I have the latest version of the Silverlight grid control (2013.2.724.1050). What do you mean by "Reset" action? Do you have a code example?
If you set the collection to null and then assign a value to it, a CollectionChanged with NotifyCollectionChangedAction.Reset will be raised. The other actions should also refresh the aggregate values. I have tested removing or/and adding an item in a ObservableCollection. The aggregates are properly updated once a CollectionChanged event is raised.
Would it be possible for you to isolate the problem in a demo project and send it to us?
Regards,
Didie
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
In an attempt to get the column footer aggregates to update, I am removing and adding an item from the bound ObservableCollection after a line item is updated e.g: (snippet of ViewModel code)
var clone = SerializeHelper.CloneObject(updateddataobject);
BoundObservableCollection.Remove(updateddataobject);
BoundObservableCollection.Insert(0,clone);
I can confirm that this raises the CollectionChanged event on BoundObservableCollection property
(this is the ObservableCollection that is bound to the grid ItemsSource).
I have attached a test project showing my attempt to reproduce a problem with recalculating the aggregates.
Would you please check it and let me know how is it different from your solution?
Didie
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
I added inline aggregates and after I changed an item from the GridView, I got them updated. Would you please let me know what exactly should I change in the attached test project so that I can reproduce the issue?
Didie
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
public static ObservableCollection<Club> GetClubs() { ObservableCollection<Club> clubs = new ObservableCollection<Club>(); Club club; // Liverpool club = new Club("Liverpool", new DateTime(1892, 1, 1), 76212); club.Players.Add(new Player("Pepe Reina", 25, Position.GK, "Spain")); club.Players.Add(new Player("Jamie Carragher", 23, Position.DF, "England")); club.Players.Add(new Player("Steven Gerrard", 8, Position.MF, "England")); club.Players.Add(new Player("Fernando Torres", 9, Position.FW, "Spain")); clubs.Add(club); // Manchester Utd. club = new Club("Manchester Utd.", new DateTime(1878, 1, 1), 76212); club.Players.Add(new Player("Edwin van der Sar", 1, Position.GK, "Netherlands")); club.Players.Add(new Player("Rio Ferdinand", 5, Position.DF, "England")); club.Players.Add(new Player("Ryan Giggs", 11, Position.MF, "Wales")); club.Players.Add(new Player("Wayne Rooney", 10, Position.FW, "England")); clubs.Add(club); // Chelsea club = new Club("Liverpool", new DateTime(1905, 1, 1), 42055); club.Players.Add(new Player("Petr Čech", 1, Position.GK, "Czech Republic")); club.Players.Add(new Player("John Terry", 26, Position.DF, "England")); club.Players.Add(new Player("Frank Lampard", 8, Position.MF, "England")); club.Players.Add(new Player("Nicolas Anelka", 39, Position.FW, "France")); clubs.Add(club); // Arsenal club = new Club("Arsenal", new DateTime(1886, 1, 1), 60355); club.Players.Add(new Player("Manuel Almunia", 1, Position.GK, "Spain")); club.Players.Add(new Player("Gaël Clichy", 22, Position.DF, "France")); club.Players.Add(new Player("Cesc Fà bregas", 4, Position.MF, "Spain")); club.Players.Add(new Player("Robin van Persie", 11, Position.FW, "Netherlands")); clubs.Add(club); return clubs; }
There are now two Liverpool clubs. Run the test program and group by Name. You should see two records under the Liverpool grouping with a sum stadium capacity of 118267. Now click the "Update Value on first item" button. The first Liverpool record has the statium capacity changed to 39, however, the column aggregate still states that the sum is 118267. It has not been updated correctly. Please confirm whether you see the same behavior.
We were able to reproduce the issue.
You will need to first remove the item, then edit it and at the end insert it back to the collection:
private
void
Button1_Click(
object
sender, RoutedEventArgs e)
{
var collection =
this
.clubsGrid.ItemsSource
as
ObservableCollection<Club>;
var firstItem = collection[0]
as
Club;
collection.Remove(firstItem);
firstItem.StadiumCapacity = 39;
collection.Insert(0, firstItem);
}
If you follow this pattern, then the value will be updated fine.
Regards,
Didie
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>
If I understand this post correctly, doing a rebind on the grid would solve the issue? Leaving this here in case it's useful for someone else.
<code>ucGrid.rebind();</code>