Index correspondence between DataPoints in a ChartSeries and the corresponding UIElements

2 Answers 73 Views
ChartView
Dimitrios
Top achievements
Rank 1
Dimitrios asked on 13 Jul 2022, 04:12 PM | edited on 13 Jul 2022, 04:12 PM

In my UI I have a RadChartView, holding some ChartSeries; (in my specific case this is a PolarChart holding a PolarPointSeries).

I have an event which triggers when the user clicks one of the points in this series.

void chart_SelectedPointChanged(object sender, ChartViewSelectedPointChangedEventArgs e)

Using this event, I wish to change the graphical styling of the point that the user selects, at the moment it is selected.

 

It is straightforward to attain the DataPoint that is clicked by the user via

e.NewSelectedPoint;
It is also straightforward to attain the collection of UIChartElements in the plotted series, via
e.NewSelectedSeries.Children;

I can edit the style of any of these plotted UIChartElements, e.g. setting the first one to some image:

e.NewSelectedSeries.Children[0].BackgroundShape.Image = new System.Drawing.Image("MyImage.png");

 

 

I then expected I could edit the graphical styling of the particular point selected by the user using the index of the selected DataPoint:

e.NewSelectedSeries.Children[e.NewSelectedPoint.Index].BackgroundShape.Image = new System.Drawing.Image("MyImage.png)

However, the resulting UIChartElement (e.NewSelectedSeries.Children[e.NewSelectedPoint.Index]) does not correspond to the selected DataPoint (e.NewSelectedPoint); it instead corresponds to one of the other DataPoints in the series.

It appears as though the indexing of e.NewSelectedPointSeries.Children does not correspond to the indexing of e.NewSelectedPoint.DataPoints.

Have I misunderstood the API? Is there a better way of accessing the UIChartElement corresponding to a selected point?

 

For the record, the attempted approach I outline above works correctly with a previous release of Telerik UI for WinForms (2018.1.116.40), but does not work upon upgrading to a newer version (2018.3.1016.40).

2 Answers, 1 is accepted

Sort by
1
Accepted
Dess | Tech Support Engineer, Principal
Telerik team
answered on 20 Jul 2022, 08:39 AM

Hello, Dimitrios,  

I was able to reproduce the undesired behavior that you are facing. After further testing and deeper investigation with more data points, I can confirm that the visual PolarPointElement are not correctly associated with the PolarDataPoint even though the label element is correct.

I have logged it in our feedback portal by creating a public thread on your behalf. You can track its progress, subscribe for status changes and add your comments on the following link - feedback item.

I have also updated your Telerik points.

Currently, the possible solution that I can suggest is to use the following custom renderer. The achieved result is illustrated in the gif file:

        public RadForm1()
        {
            InitializeComponent();
            this.radChartView1.CreateRenderer += RadChartView1_CreateRenderer;
            radChartView1.AreaType = ChartAreaType.Polar; 
            radChartView1.Controllers.Add(new ChartSelectionController());
            radChartView1.SelectedPointChanged += radChartView1_SelectedPointChanged;
            radChartView1.SelectionMode = ChartSelectionMode.SingleDataPoint;

            radChartView1.Series.Clear();
            PolarPointSeries series = new PolarPointSeries() { PointSize = new SizeF(25, 25) };
            series.ShowLabels = false;
            
            radChartView1.Series.Add(series);

            PolarDataPoint point0 = new PolarDataPoint() { Angle = 0, Value = 20, Label ="0" };
            PolarDataPoint point1 = new PolarDataPoint() { Angle = 45, Value = 40, Label = "45" };
            PolarDataPoint point2 = new PolarDataPoint() { Angle = 90, Value = 60, Label = "90" };
            PolarDataPoint point3 = new PolarDataPoint() { Angle = 135, Value = 100, Label = "135" };
            PolarDataPoint point4 = new PolarDataPoint() { Angle = 160, Value = 90, Label = "160" };
            PolarDataPoint point5 = new PolarDataPoint() { Angle = 230, Value = 27, Label = "230" };
            PolarDataPoint point6 = new PolarDataPoint() { Angle = 290, Value = 65, Label = "290" };
            PolarDataPoint point7 = new PolarDataPoint() { Angle = 330, Value = 78, Label = "330" };
            series.DataPoints.Add(point0);
            series.DataPoints.Add(point1);
            series.DataPoints.Add(point2);
            series.DataPoints.Add(point3);
            series.DataPoints.Add(point4);
            series.DataPoints.Add(point5);
            series.DataPoints.Add(point6);
            series.DataPoints.Add(point7);
        }

        private void radChartView1_SelectedPointChanged(object sender, ChartViewSelectedPointChangedEventArgs e)
        {
            foreach (PolarPointElement el in e.NewSelectedSeries.Children)
            {
                if (el.DataPoint == e.NewSelectedPoint)
                {
                    el.BackColor = Color.Red;

                }
                else
                {
                    el.BackColor = e.NewSelectedSeries.BackColor;
                }
            }
        }

        private void RadChartView1_CreateRenderer(object sender, ChartViewCreateRendererEventArgs e)
        {
            if (e.Area is PolarArea)
            {
                e.Renderer = new CustomPolarRenderer(e.Area as PolarArea);
            }
        } 

        public class CustomPolarRenderer : PolarRenderer
        {
            public CustomPolarRenderer(PolarArea area) : base(area)
            {
            }

            protected override void Initialize()
            {
                base.Initialize();
                for (int i = 0; i < this.DrawParts.Count; i++)
                {
                    PolarPointSeriesDrawPart linePart = this.DrawParts[i] as PolarPointSeriesDrawPart;
                    if (linePart != null)
                    {
                        this.DrawParts[i] = new CustomPolarPointSeriesDrawPart((PolarPointSeries)linePart.Element, this);
                    }
                }
            }

        }

        public class CustomPolarPointSeriesDrawPart : PolarPointSeriesDrawPart
        {
            public CustomPolarPointSeriesDrawPart(PolarPointSeries series, IChartRenderer renderer) : base(series, renderer)
            {
            }

            protected override void DrawPoints()
            {
                PointF[] points = this.GetPointsPositionsArray();

                if (points == null)
                {
                    return;
                }

                RadGdiGraphics radGraphics = new RadGdiGraphics(this.Renderer.Surface as Graphics);
                List<DataPointElement> pointElements = new List<DataPointElement>();
                DataPointElementLayoutSlotComparer comparer = new DataPointElementLayoutSlotComparer();

                if (this.Renderer is CartesianRenderer)
                {
                    comparer.Vertical = ((CartesianRenderer)this.Renderer).Area.Orientation == Orientation.Vertical;
                }

                comparer.Collection = this.Element.DataPoints; 

                for (int i = 0; i < this.Element.Children.Count; i++)
                {
                    DataPointElement childElement = this.Element.Children[i] as DataPointElement;

                    if (childElement == null || !childElement.IsVisible || childElement.PointSize.Width < 1f || childElement.PointSize.Height < 1f)
                    {
                        continue;
                    }

                    SizeF pointSize = childElement.PointSize;
                    PointF pointLocation = points[i];

                    RectangleF ptRect = new RectangleF(pointLocation, pointSize);
                    ptRect.Offset(pointSize.Width / -2f, pointSize.Height / -2f);

                    if (childElement.BackgroundShape != null)
                    {
                        childElement.BackgroundShape.Paint((Graphics)radGraphics.UnderlayGraphics, ptRect);
                    }

                    GraphicsPath pointPath = null;

                    if (childElement.Shape != null)
                    {
                        pointPath = childElement.Shape.CreatePath(ptRect);
                    }
                    else
                    {
                        pointPath = new GraphicsPath();
                        pointPath.AddEllipse(ptRect);
                    }

                    if (pointPath != null)
                    {
                        Telerik.WinControls.Primitives.FillPrimitiveImpl pointFill = new Telerik.WinControls.Primitives.FillPrimitiveImpl(childElement, null);
                        pointFill.PaintFill(radGraphics, pointPath, ptRect);

                        Telerik.WinControls.Primitives.BorderPrimitiveImpl pointBorder = new Telerik.WinControls.Primitives.BorderPrimitiveImpl(childElement, null);
                        pointBorder.PaintBorder(radGraphics, null, pointPath, ptRect);

                        if (childElement.Image != null)
                        {
                            Telerik.WinControls.Primitives.ImagePrimitiveImpl pointImage = new Telerik.WinControls.Primitives.ImagePrimitiveImpl(childElement);
                            pointImage.PaintImage(radGraphics, childElement.Image, ptRect, childElement.ImageLayout, childElement.ImageAlignment, childElement.ImageOpacity, false);
                        }

                        pointPath.Dispose();
                    }
                }
            }
        }

I believe that it would fit your scenario.

Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik

The Premier Dev Conference is back! 

Coming to you live from Progress360 in-person or on your own time, DevReach for all. Register Today.


Dimitrios
Top achievements
Rank 1
commented on 20 Jul 2022, 04:20 PM

Dear Dess,

Thank you very much for your swift reply and actions in response to my request.

I added the overridden DrawPoints() method as you suggested and it solved the problem!

Ten out of ten customer service! Thank you!

1
Dess | Tech Support Engineer, Principal
Telerik team
answered on 15 Jul 2022, 10:23 AM
Hello, Dimitrios, 

If I understand your requirement correctly, you need to extract the visual element associated with a specific data point. For this purpose, feel free to use the following approach: 
        private void radChartView1_SelectedPointChanged(object sender, ChartViewSelectedPointChangedEventArgs e)
        {
            foreach (PolarPointElement el in e.NewSelectedSeries.Children)
            {
                if (el.DataPoint==e.NewSelectedPoint)
                {
                    el.BackColor = Color.Red;
                }
            } 
        }


There is not a direct relation between the data points and the corresponding visual element by index. That is why you can't rely on the index for accessing the desired element.

I hope this information helps. If you need any further assistance please don't hesitate to contact me. 

Regards,
Dess | Tech Support Engineer, Principal
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Dimitrios
Top achievements
Rank 1
commented on 19 Jul 2022, 02:22 PM | edited

Dear Dess,

Thank you very much for your response. Unfortunately, I cannot get the the solution you provide to function as described.

I am using Telerik for WinForms version 2018.3.1016.40 and I implemented your proposed solution as follows:

using System;
using System.Drawing;
using Telerik.Charting;
using Telerik.WinControls.UI;

namespace ExampleNamespace
{
    public partial class RadForm1 : Telerik.WinControls.UI.RadForm
    {

        public RadForm1()
        {
            InitializeComponent();
            chart.AreaType = ChartAreaType.Polar;
            chart.Controllers.Add(new ChartSelectionController());
            chart.SelectedPointChanged += Chart_SelectedPointChanged;
            chart.SelectionMode = ChartSelectionMode.SingleDataPoint;

            chart.Series.Clear();
            PolarPointSeries series = new PolarPointSeries() { PointSize = new SizeF(25, 25) };
            chart.Series.Add(series);

            PolarDataPoint point0 = new PolarDataPoint() { Angle = 0, Value = 20 };
            PolarDataPoint point1 = new PolarDataPoint() { Angle = 45, Value = 40 };
            PolarDataPoint point2 = new PolarDataPoint() { Angle = 90, Value = 60 };
            PolarDataPoint point3 = new PolarDataPoint() { Angle = 135, Value = 100 };
            PolarDataPoint point4 = new PolarDataPoint() { Angle = 160, Value = 90 };
            PolarDataPoint point5 = new PolarDataPoint() { Angle = 230, Value = 27 };
            PolarDataPoint point6 = new PolarDataPoint() { Angle = 290, Value = 65 };
            PolarDataPoint point7 = new PolarDataPoint() { Angle = 330, Value = 78 };
            series.DataPoints.Add(point0);
            series.DataPoints.Add(point1);
            series.DataPoints.Add(point2);
            series.DataPoints.Add(point3);
            series.DataPoints.Add(point4);
            series.DataPoints.Add(point5);
            series.DataPoints.Add(point6);
            series.DataPoints.Add(point7);
        }

        private void Chart_SelectedPointChanged(object sender, ChartViewSelectedPointChangedEventArgs e)
        {
            foreach (PolarPointElement el in e.NewSelectedSeries.Children)
            {
                if (el.DataPoint == e.NewSelectedPoint)
                    el.BackColor = Color.Red;
            }
        }
    }
}

It seems that the plotted point that changes to red does not necessarily correspond to the DataPoint selected by the user. Each time the code is compiled, it seems that the point that is highlighted when clicking on a particular point is determined at random.

When there are only three points as in your example animation, the probability of a compiled application having correct behavior are then relatively high (one in six, perhaps...?). However, with more points in the series, this probability reduces quickly. Does the code you used to construct your demonstration animation work correctly every time you compile it?

Tags
ChartView
Asked by
Dimitrios
Top achievements
Rank 1
Answers by
Dess | Tech Support Engineer, Principal
Telerik team
Share this question
or