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

RadPieChart LegendItems Updated Event

3 Answers 54 Views
Chart
This is a migrated thread and some comments may be shown as answers.
Ernie S
Top achievements
Rank 1
Ernie S asked on 06 Aug 2014, 08:46 PM
Is there any kind of event I can wire to in a Pie Chart that is fired when the LegendItems collection is updated?

I am creating a behavior I would like to attached to a PieChart and has a dependency property that will bind to the LegendItem like so:

1.<i:Interaction.Behaviors>
2.    <localbehavior:RadPieChartLegendBehavior
3.         Legend="{Binding ElementName=PieChartLegend}"
4.         LegendItems="{Binding ElementName=PieChart, Path=LegendItems}"/>
5.</i:Interaction.Behaviors>

Where PieChart is a RadPieChart and PieChartLegend is a RadLegend.  I am using a behavior so I can project the Legend Items to a standard format that other charting objects are using with a common code set (so I dont have to write specific complex Styles/Templates for each).  LegendItems on line 4 is a Dependency Property that calls a non-static method to wire the the CollectionChanged event:

01.public ObservableCollection<LegendItem> LegendItems
02.{
03.    get { return (ObservableCollection<LegendItem>)GetValue(LegendItemsProperty); }
04.    set { SetValue(LegendItemsProperty, value); }
05.}
06.public static readonly DependencyProperty LegendItemsProperty = DependencyProperty.Register(
07.    "LegendItems"
08.    , typeof(ObservableCollection<LegendItem>)
09.    , typeof(RadPieChartLegendBehavior)
10.    , new PropertyMetadata(default(ObservableCollection<LegendItem>), OnLegendItemCollectionChanged));
11. 
12.private static void OnLegendItemCollectionChanged(DependencyObject dependencyObject
                            , DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
13.{
14.    var control = dependencyObject as RadPieChartLegendBehavior;
15.    if (control != null)
16.        control.WireLegendUpdate();
17.}
18. 
19.private void WireLegendUpdate()
20.{
21.    LegendItems.CollectionChanged += OnLegendItemCollectionOnCollectionChanged;
22.}

This kind of works. 

The data the chart is bound to can be changed by the user when they select different columns to chart.  When they pick a new column, the dataset is recreated and the chart updates.  But the LegendItems collection, it appears, is NOT recreated  (= new LegendItemCollection) or emptied (.Clear()) but rather  it has its item updated 1 to 1?  Then extras are removed and new ones are added only when necessary?   The result being that OnLegendItemCollectionOnCollectionChanged on line 21 isnt always tripped.

The reason I came to this conclusion is the CollectionChanged event fires ONLY when the number of items in LegendItems changes.  If the user picks a field to map that results in the EXACT same number of legend items that the prior selected field resulted in the CollectionChanged event does NOT fire.  I could use PropertyChanged on each LegendItem but that would result in the event being fired many times (it is already being called multiple times if the collection count changes but not nearly as much).

Is there something I am missing?

Thanks
Ernie


3 Answers, 1 is accepted

Sort by
0
Ernie S
Top achievements
Rank 1
answered on 07 Aug 2014, 08:42 PM
And example of what I mean can be seen in this project:

https://dl.dropboxusercontent.com/u/9209668/PieChartLegendNotification.zip

Using version v2014.1.331.1050.

Ernie
0
Accepted
Martin Ivanov
Telerik team
answered on 11 Aug 2014, 11:26 AM
Hello Augustine,

Note that the CollectionChanged event is called only if you remove or add items. When you reset the collection the event won't be fired. When you change the ItemsSource of the PieSeries its LegendItems collection will be updated corresponding to the count of the pie pie slices. Also, the collection will reuse the items that are currently loaded in the legend.

For example if you already have 5 items in the legend and your add 5 items in the ItemsSource of the series, those 5 LengedItems will be reused, the collection won't be updated (only the properties of its items) and the event won't be fired. If you already have 5 items and you replace the series ItemsSource with a collection that has 8 items, the first 5 items will be reused and in the collection will be added 3 more items.

If you want to listen for a change in the collection you can modify the ChartItems collection instead of reset it. For this approach you can subscribe for the ChartItems CollectionChanged event and when you select different color, you can clear the collection and add the new items instead of reset the entire collection.

Here is an example in code:
public MainPage()
{
    InitializeComponent();
 
    this.ChartItems = new ObservableCollection<ChartItem>();
    this.ChartItems.CollectionChanged += LegendItemsOnCollectionChanged;
     
    OriginalCount_Click(null, null);
}
 
private void LegendItemsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
    MessageBox.Show("LegendItems Collection Changed!", "WORKED!", MessageBoxButton.OK);
}
 
private void SameCount_Click(object sender, RoutedEventArgs e)
{
    var items = new RadObservableCollection<ChartItem>
    {
        new ChartItem {Label = "Item 10", Value = 52.21},
        new ChartItem {Label = "Item 11", Value = 35.06},
        new ChartItem {Label = "Item 12", Value = 46.48},
        new ChartItem {Label = "Item 13", Value = 51.25},
        new ChartItem {Label = "Item 14", Value = 87.51},
    };
 
    ChartItems.Clear();
    foreach (var item in items)
    {
        ChartItems.Add(item);
    }
}

This will ensure that each time when you add and item the CollectionChanged event will be fired.

Another approach is to clear the LegendItems collection of the chart each time before you set the ChartItems.

private void SameCount_Click(object sender, RoutedEventArgs e)
{
        if (PieChart.LegendItems != null)
        {
            this.PieChart.LegendItems.Clear();
        }
 
        var items = new RadObservableCollection<ChartItem>
        {
            new ChartItem {Label = "Item 10", Value = 52.21},
            new ChartItem {Label = "Item 11", Value = 35.06},
            new ChartItem {Label = "Item 12", Value = 46.48},
            new ChartItem {Label = "Item 13", Value = 51.25},
            new ChartItem {Label = "Item 14", Value = 87.51},
            //new ChartItem {Label = "Item 15", Value = 48.11},
            //new ChartItem {Label = "Item 16", Value = 61.24},
            //new ChartItem {Label = "Item 17", Value = 16.15},
            //new ChartItem {Label = "Item 18", Value = 51.05},
            //new ChartItem {Label = "Item 19", Value = 25.56}
        };
 
        ChartItems = items;
    }

Please let me know if this helps.

Regards,
Martin
Telerik
 
Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
 
0
Ernie S
Top achievements
Rank 1
answered on 11 Aug 2014, 02:25 PM
Thanks Martin.  That explains it.

I used you second idea of clearing the legend items to always force regeneration.   Took some extra plumbing but it works.  An actual event to wire to would be nice to see in a future version :).  Another option is not update the legend via the dispatcher (took a peak at the source code) but I suppose you dont want to risk performance hits especially since the LegendItems is kind of an optional thing anyway.

Thanks again,
Ernie
Tags
Chart
Asked by
Ernie S
Top achievements
Rank 1
Answers by
Ernie S
Top achievements
Rank 1
Martin Ivanov
Telerik team
Share this question
or