Completed
Last Updated: 19 Sep 2018 12:49 by Dimitar
ADMIN
Hristo
Created on: 06 Sep 2018 10:10
Category: ChartView
Type: Bug Report
0
FIX. RadChartView - improve the VerticalAdjusmentLabelsStrategy of the smart labels in the BarSeries so that it avoids collisions with the bar data point
How to reproduce:
private void UpdateChart()
{
    this.radChartView1.Series.Clear();

    // Create two series for the bar chart.
    BarSeries requiredBarSeries = new BarSeries
    {
        ShowLabels = true,
        CombineMode = ChartSeriesCombineMode.Cluster,
        NumberOfColors = 2,
    };

    BarSeries actualBarSeries = new BarSeries
    {
        ShowLabels = true,
        CombineMode = ChartSeriesCombineMode.Cluster,
        NumberOfColors = 2,
    };
    
    // Add the data. 
    AddDataPoint(requiredBarSeries, .0333d, "Germany", "Required");
    AddDataPoint(actualBarSeries, .050d, "Germany", "Actual");
    AddDataPoint(requiredBarSeries, .0333d, "United States", "Required");
    AddDataPoint(actualBarSeries, .050d, "United States", "Actual");
    AddDataPoint(requiredBarSeries, .00d, "France", "Required");
    AddDataPoint(actualBarSeries, .050d, "France", "Actual");

    AddDataPoint(requiredBarSeries, 0.0d, "United Kingdom", "Required");
    AddDataPoint(actualBarSeries, .050d, "United Kingdom", "Actual");


    AddDataPoint(requiredBarSeries, 0.955d, "Russia", "Required");
    // Change the following value to .95d to see 
    // the issue.
    AddDataPoint(actualBarSeries, .15d, "Russia", "Actual");

    this.radChartView1.Series.Add(requiredBarSeries);
    this.radChartView1.Series.Add(actualBarSeries);

    CategoricalAxis horizontalAxis = radChartView1.Axes[0] as CategoricalAxis;
    horizontalAxis.LabelFitMode = AxisLabelFitMode.MultiLine;
}

private void AddDataPoint(BarSeries series, double val, string category, string label)
{
    series.DataPoints.Add(new CategoricalDataPoint
    {
        Value       = val,
        Label       = String.Format("{0}\n{1:P2}", label, val),
        Category    = category
    });
}

Workaround: create a custom SmartLabelsController
public RadForm1()
        {
            InitializeComponent();


            SmartLabelsController labelsController = new CustomSmartLabelsController();
            this.radChartView1.Controllers.Add(labelsController);
     
            // Set up the chart
        }

public class CustomSmartLabelsController : SmartLabelsController
    {
        protected override SmartLabelsStrategyBase GetDefaultStrategy(ChartArea area)
        {
            SmartLabelsStrategyBase strategy = base.GetDefaultStrategy(area);

            if (strategy is VerticalAdjusmentLabelsStrategy)
            {
                strategy = new CustomVerticalAdjusmentLabelsStrategy();
                Dictionary<Type, List<Type>> strategies = typeof(SmartLabelsController).GetField("strategySeries", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(this) as Dictionary<Type, List<Type>>;
                strategies.Remove(typeof(VerticalAdjusmentLabelsStrategy));
                strategies.Add(typeof(CustomVerticalAdjusmentLabelsStrategy), new List<Type> { typeof(BarSeries), typeof(LineSeriesBase) });
            }

            return strategy;
        }
    }

    public class CustomVerticalAdjusmentLabelsStrategy : VerticalAdjusmentLabelsStrategy
    {
        public override void CalculateLocations(ChartSeriesCollection series, Rectangle plotArea)
        {
            if (series.Count == 0)
            {
                return;
            }

            List<LabelElement> labels = new List<LabelElement>();
            List<int> overlaps = new List<int>();

            foreach (ChartSeries chartSeries in series)
            {
                if (!chartSeries.ShowLabels || !chartSeries.IsVisible)
                {
                    continue;
                }

                foreach (DataPointElement point in chartSeries.Children)
                {
                    LabelElement label = (LabelElement)point.Children[0];
                    Rectangle labelRect = ChartRenderer.ToRectangle(label.GetLayoutSlot());
                    labelRect.X += (int)(series[0].View.Viewport.X + ((IChartView)series[0].View).PlotOriginX);
                    labelRect.Y += (int)(series[0].View.Viewport.Y + ((IChartView)series[0].View).PlotOriginY);

                    if (chartSeries.View.GetArea<CartesianArea>().Orientation == Orientation.Vertical)
                    {
                        labelRect.Y -= this.DistanceToLabel;
                    }
                    else
                    {
                        labelRect.X += this.DistanceToLabel;
                    }

                    labelRect.Inflate(0, this.DistanceBetweenLabels);

                    label.SmartRectangle = labelRect;
                    labels.Add(label);
                }
            }

            CartesianSeries cartesianSeries = (CartesianSeries)series[0];

            if (cartesianSeries != null)
            {
                this.RestrictWithinAxes(labels, cartesianSeries);
            }

            LabelElement labelToMove = this.GetElementWithMostOverlaps(labels, series);
            int counter = 0;

            while (labelToMove != null && counter < labels.Count)
            {
                Rectangle firstDirectionRect;
                Rectangle secondDirectionRect;
                int firstMoveCost;
                int secondMoveCost;

                Rectangle dataPointLayout = ChartRenderer.ToRectangle(labelToMove.DataPoint.LayoutSlot);
                dataPointLayout.X += (int)labelToMove.View.Viewport.X;
                dataPointLayout.Y += (int)labelToMove.View.Viewport.Y;

                if (cartesianSeries.View.GetArea<CartesianArea>().Orientation == System.Windows.Forms.Orientation.Vertical)
                {
                    firstDirectionRect = this.GetBestPositionInUpwardDirection(labels, labelToMove, cartesianSeries);
                    secondDirectionRect = this.GetBestPositionInDownwardDirection(labels, labelToMove, cartesianSeries);

                    firstMoveCost = Math.Abs(labelToMove.SmartRectangle.Y - firstDirectionRect.Y);
                    secondMoveCost = Math.Abs(labelToMove.SmartRectangle.Y - secondDirectionRect.Y);
                }
                else
                {
                    firstDirectionRect = this.GetBestPositionInLeftDirection(labels, labelToMove, cartesianSeries);
                    secondDirectionRect = this.GetBestPositionInRightDirection(labels, labelToMove, cartesianSeries);

                    firstMoveCost = Math.Abs(dataPointLayout.X - firstDirectionRect.X);
                    secondMoveCost = Math.Abs(dataPointLayout.X - secondDirectionRect.X);
                }

                if (!dataPointLayout.IntersectsWith(secondDirectionRect) && firstMoveCost > secondMoveCost)
                {
                    labelToMove.SmartRectangle = secondDirectionRect;
                }
                else
                {
                    labelToMove.SmartRectangle = firstDirectionRect;
                }

                labelToMove = this.GetElementWithMostOverlaps(labels, series);
                counter++;
            }

            this.FinalPositionsOptimization(labels);

            foreach (LabelElement label in labels)
            {
                Rectangle rect = label.SmartRectangle;
                rect.Inflate(0, -this.DistanceBetweenLabels);

                Rectangle dataPointLayout = ChartRenderer.ToRectangle(label.DataPoint.LayoutSlot);
                dataPointLayout.X += (int)cartesianSeries.View.Viewport.X;
                dataPointLayout.Y += (int)cartesianSeries.View.Viewport.Y;
                int delta = 1;

                if (dataPointLayout.Y > label.SmartRectangle.Y)
                {
                    delta = -1;
                }

                rect.Y += delta * this.DistanceToLabel;
                label.SmartRectangle = rect;
            }
        }
    }
0 comments