Good day,
My requierment is to have a RadChartView with a LineSeries and a RangeSeries. I add CategoricalDataPoint objects manually to the LineSeries and I manually add RangeDataPoint objects to the RangeSeries.
When I encounter NULL valued datapoints, for the LineSeries, I do the following. The NULL valued datapoints appear as gaps in the chart as expected.
Dim value As Nullable(Of Double) Dim serControl As LineSeries serControl = New LineSeries() chart.Series.Add(serControl) value = IIf(IsDBNull(dr(data.ControlParameter.ID.ToString())), New Nullable(Of Double), dr(data.ControlParameter.ID.ToString())) serControl.DataPoints.Add(New Telerik.Charting.CategoricalDataPoint(value))
However the RangeSeries do not handle the NULL valued datapoints the same. I get the following error: Nullable object must have a value.
Dim LowerBound, UpperBound As Nullable(Of Double) Dim serRange As RangeSeries serRange = New RangeSeries() chart.Series.Add(serRange) LowerBound = IIf(IsDBNull(dr("Lowerbound"), New Nullable(Of Double), dr("LowerBound")) UpperBound = IIf(IsDBNull(dr("Upperbound"), New Nullable(Of Double), dr("UpperBound")) serRange.DataPoints.Add(New Telerik.Charting.RangeDataPoint(UpperBound, LowerBound))
I need to be able to show gaps, not zero's in a RangeSeries when there are NULL values.
How can I do this?
Hello Telerik Support,
after taking my first steps with RangeSelector and ChartView controls I found an issue with a simple bar chart. The length of some bars in the chart of the RangeSelector doesn't match with the bars in the ChartView. The relation is wrong.I provided a screenshot and marked the bars. I could provide a sample application, but I can't attach a zip file, so here's the code of the main form:
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Linq;
using
System.Text;
using
System.Windows.Forms;
using
Telerik.WinControls;
using
Telerik.WinControls.UI;
namespace
TelerikChartView
{
public
partial
class
MainForm : Telerik.WinControls.UI.RadForm
{
private
List<Kapazitaetspunkt> _BackListPunkte;
private
BindingList<Kapazitaetspunkt> Punkte;
public
MainForm()
{
InitializeComponent();
}
private
void
MainForm_Load(
object
sender, EventArgs e)
{
_BackListPunkte =
new
List<Kapazitaetspunkt>();
Punkte =
new
BindingList<Kapazitaetspunkt>(_BackListPunkte);
//Punkte = new BindingList<Kapazitaetspunkt>();
rngTimeSelector.AssociatedControl = cvChart;
rngTimeSelector.RangeSelectorElement.ScrollSelectorElement.Visibility = ElementVisibility.Collapsed;
BarSeries bars =
new
BarSeries();
bars.DataSource = Punkte;
bars.ValueMember = nameof(Kapazitaetspunkt.Percentage);
bars.CategoryMember = nameof(Kapazitaetspunkt.Date);
cvChart.Series.Add(bars);
}
private
void
GenerateTestData(
int
addMonths = 0)
{
Punkte.Clear();
const
int
count = 25;
const
int
step = 4;
var von = DateTime.Today.AddMonths(addMonths);
var bis = von.AddDays(count);
double
percentage = 0;
//_BackListPunkte.Add(new Kapazitaetspunkt(von.AddDays(-1), null));
Punkte.Add(
new
Kapazitaetspunkt(von.AddDays(-1),
null
));
while
(von < bis)
{
//_BackListPunkte.Add(new Kapazitaetspunkt(von, percentage));
Punkte.Add(
new
Kapazitaetspunkt(von, percentage));
percentage += step;
von = von.AddDays(1);
}
//_BackListPunkte.Add(new Kapazitaetspunkt(von.AddDays(1), null));
Punkte.Add(
new
Kapazitaetspunkt(von.AddDays(1),
null
));
}
private
void
btnGenerateData_Click(
object
sender, EventArgs e)
{
GenerateTestData();
rngTimeSelector.RangeSelectorElement.InitializeElements();
rngTimeSelector.RangeSelectorElement.ResetLayout(
true
);
}
private
void
btnGenerateData2_Click(
object
sender, EventArgs e)
{
GenerateTestData(2);
rngTimeSelector.RangeSelectorElement.InitializeElements();
rngTimeSelector.RangeSelectorElement.ResetLayout(
true
);
}
private
void
rngTimeSelector_ScaleInitializing(
object
sender, ScaleInitializingEventArgs e)
{
e.Cancel =
true
;
}
}
public
class
Kapazitaetspunkt : INotifyPropertyChanged
{
private
DateTime _Date;
public
DateTime Date
{
get
=> _Date;
set
{
if
(value != Date)
{
_Date = value;
OnPropertyChanged();
}
}
}
public
double
? _Percentage;
public
double
? Percentage
{
get
=> _Percentage;
set
{
if
(value != Percentage)
{
_Percentage = value;
OnPropertyChanged();
}
}
}
public
Kapazitaetspunkt(DateTime date,
double
? percentage)
{
Date = date;
Percentage = percentage;
}
public
event
PropertyChangedEventHandler PropertyChanged;
protected
virtual
void
OnPropertyChanged([CallerMemberName]
string
propertyName =
null
)
{
PropertyChanged?.Invoke(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
}
Regards,
Stephan
I need to draw a series of graphs and show the legend. When the legend does not fit in the RadChartView, a scroll bar appears - this is acceptable, you can quickly see the whole legend. But the title of the legend is cut off.
Currently, RadChartView offers exporting to a Bitmap in one of the following formats: https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imageformat?view=netframework-4.8
It would be nice to have export functionality to a vector image.
Please refer to the attached sample project. Select a range from RadRangeSelector and print the chart. You will notice that the preview chart is shifted. Even if the the default margin is reduced to 0, the preview chart is not the same as the one displayed on the form.
Workaround: export the chart to a file which file can be printed: https://docs.telerik.com/devtools/winforms/controls/chartview/features/export
Hi team,
We need a ChartView with multiple series and multiple Y-axis. The axis color of each Y-axis should be automatically painted according to the palette setting (thank you for the brilliant work - it's very intuitive). However, if clear the series and re-assign a new set of series, the border color of the first axis changes to black, regardless which palette is active. If the palette is set to a new one, the border color can be re-painted correctly. See screenshot as attached.
How to reproduce: Please refer to the attached project for a demo of reproduction. Click the first button to populate new random data. The border color of the first axis should change to black after clicking the button twice and more times. Clicking the second button will force the palette to change and the border color will become normal.
Thank you, team.
Best,
Yusi
When you have BarSeries and LineSeries in RadChartView the problem is that the DataPoint.Presenter is always BarSerries, never LineSeries.
To workaround, create custom renderer:
this.radChartView1.CreateRenderer += this.RadChartView1_CreateRenderer;
private void RadChartView1_CreateRenderer(object sender, ChartViewCreateRendererEventArgs e)
{
e.Renderer = new CustomCartesianRenderer(e.Area as CartesianArea);
}
public class CustomCartesianRenderer : CartesianRenderer
{
public CustomCartesianRenderer(CartesianArea area)
: base(area)
{ }
public override DataPoint HitTest(int x, int y)
{
for (int i = this.DrawParts.Count - 1; i >= 0; i--)
{
DataPoint dataPoint = this.DrawParts[i].HitTest(new Point(x, y));
if (dataPoint != null)
{
return dataPoint;
}
}
return base.HitTest(x, y);
}
}
Use attached to reproduce.
- Click at least 7 times.
Workaround: Crete new series instance each time.
Hi Team,
Can you please share sample or demo code for candlestick graph (C# winform).
Thanks
Use attached to reproduce.
Workaround:
series.DataPoints.Add(new PieDataPoint(0.01, "Germany"));
After 1 million the decimal places of the labels are not correct, In your image. the labels on the horizontal axis go 999,992.50 - 999,995.00 - 999,997.5 - 1,000,000.00 - *1,000,003.00(should be 1,000,002.50) - 1,000,005.00 - *1,000,008.00(should be 1,000,007.5)
To reproduce: WaterfallSeries series = new WaterfallSeries(); series.ShowLabels = true; series.DataPoints.Add(new WaterfallDataPoint(50000, false, false, "Beginning\nBalance")); series.DataPoints.Add(new WaterfallDataPoint(17000, false, false, "Jan")); series.DataPoints.Add(new WaterfallDataPoint(14000, false, false, "Feb")); series.DataPoints.Add(new WaterfallDataPoint(-12000, false, false, "Mar")); series.DataPoints.Add(new WaterfallDataPoint(69000, true, false, "Q1")); series.DataPoints.Add(new WaterfallDataPoint(-22000, false, false, "Apr")); series.DataPoints.Add(new WaterfallDataPoint(-18000, false, false, "May")); series.DataPoints.Add(new WaterfallDataPoint(500, false, false, "Jun")); series.DataPoints.Add(new WaterfallDataPoint(-30000, true, false, "Q2")); series.DataPoints.Add(new WaterfallDataPoint(39000, false, true, "Ending\nBalance")); this.radChartView1.Series.Add(series); CartesianGridLineAnnotation annotation1 = new CartesianGridLineAnnotation(); annotation1.Label = "Annotation"; annotation1.ForeColor = Color.Lime; annotation1.BackColor = Color.Black; this.radChartView1.Annotations.Add(annotation1); Workaround: public RadForm1() { InitializeComponent(); this.radChartView1.CreateRenderer += radChartView1_CreateRenderer; WaterfallSeries series = new WaterfallSeries(); series.ShowLabels = true; series.DataPoints.Add(new WaterfallDataPoint(50000, false, false, "Beginning\nBalance")); series.DataPoints.Add(new WaterfallDataPoint(17000, false, false, "Jan")); series.DataPoints.Add(new WaterfallDataPoint(14000, false, false, "Feb")); series.DataPoints.Add(new WaterfallDataPoint(-12000, false, false, "Mar")); series.DataPoints.Add(new WaterfallDataPoint(69000, true, false, "Q1")); series.DataPoints.Add(new WaterfallDataPoint(-22000, false, false, "Apr")); series.DataPoints.Add(new WaterfallDataPoint(-18000, false, false, "May")); series.DataPoints.Add(new WaterfallDataPoint(500, false, false, "Jun")); series.DataPoints.Add(new WaterfallDataPoint(-30000, true, false, "Q2")); series.DataPoints.Add(new WaterfallDataPoint(39000, false, true, "Ending\nBalance")); this.radChartView1.Series.Add(series); CartesianGridLineAnnotation annotation1 = new CartesianGridLineAnnotation(); annotation1.Label = "Annotation"; annotation1.ForeColor = Color.Lime; annotation1.BackColor = Color.Black; annotation1.PositonOffset = new SizeF(0, -20); annotation1.Axis = this.radChartView1.Axes[1] as CartesianAxis; annotation1.Value = 70000; annotation1.BorderColor = Color.Red; annotation1.BorderDashStyle = DashStyle.Solid; annotation1.BorderWidth = 1; this.radChartView1.Annotations.Add(annotation1); } private void radChartView1_CreateRenderer(object sender, ChartViewCreateRendererEventArgs e) { e.Renderer = new CustomCartesianRenderer(e.Area as CartesianArea); } public class CustomCartesianRenderer : CartesianRenderer { public CustomCartesianRenderer(CartesianArea area) : base(area) { } protected override void Initialize() { base.Initialize(); for (int i = 0; i <= this.DrawParts.Count - 1; i++) { CartesianGridLineAnnotationDrawPart annotationPart = this.DrawParts[i] as CartesianGridLineAnnotationDrawPart; if (annotationPart != null) { this.DrawParts[i] = new CustomCartesianGridLineAnnotationDrawPart((CartesianGridLineAnnotation)annotationPart.Element, this); } } } } public class CustomCartesianGridLineAnnotationDrawPart : CartesianGridLineAnnotationDrawPart { public CustomCartesianGridLineAnnotationDrawPart(CartesianGridLineAnnotation element, CartesianRenderer renderer) : base(element, renderer) { } public override void Draw() { FieldInfo fi = typeof(CartesianGridLineAnnotation).GetField("model", BindingFlags.NonPublic | BindingFlags.Instance); ChartAnnotationModel model = fi.GetValue(this.Element) as ChartAnnotationModel; RectangleF rect = ChartRenderer.ToRectangleF(model.LayoutSlot); rect.Offset(this.ViewportOffsetX, this.ViewportOffsetY); Graphics graphics = this.Renderer.Surface as Graphics; RadGdiGraphics radGraphics = new RadGdiGraphics(graphics); Rectangle clipRect = ChartRenderer.ToRectangle(this.Element.View.GetArea<CartesianArea>().AreaModel.PlotArea.LayoutSlot); clipRect.Offset((int)this.ViewportOffsetX, (int)this.ViewportOffsetY); graphics.SetClip(clipRect); GraphicsPath path = new GraphicsPath(); path.AddLine(rect.Location, new PointF(rect.Right, rect.Bottom)); BorderPrimitiveImpl border = new BorderPrimitiveImpl(this.Element, null); border.PaintBorder(radGraphics, null, path, rect); rect.Size = graphics.MeasureString(this.Element.Label, this.Element.Font); rect.Offset(this.Element.PositonOffset.Width + 1, this.Element.PositonOffset.Height + 1); TextParams tp = new TextParams(); tp.font = this.Element.Font; tp.foreColor = this.Element.ForeColor; tp.paintingRectangle = rect; tp.text = this.Element.Label; FillPrimitiveImpl fill = new FillPrimitiveImpl(this.Element, null); fill.PaintFill(radGraphics, null, rect); TextPrimitiveHtmlImpl text = new TextPrimitiveHtmlImpl(); text.PaintPrimitive(radGraphics, 0f, new SizeF(1f, 1f), tp); } }
To reproduce: - Add a chart to the form and set series with DataSource - Close and dispose the form Workaround: Set the DataSource property of the series to null prior closing.
To reproduce: please refer to the attached sample project and gif file illustrating the behavior on my end. Note: similar to the axis, the series should also have ClipLabels property which will control whether the labels will be clipped or not. Workaround: use custom renderer: Sub New() InitializeComponent() AddHandler Me.RadChartView1.CreateRenderer, AddressOf RadChartView1_CreateRenderer Dim barSeries As New Telerik.WinControls.UI.BarSeries("Performance", "RepresentativeName") barSeries.Name = "Q1" barSeries.ShowLabels = True barSeries.DataPoints.Add(New CategoricalDataPoint(177, "Harley")) barSeries.DataPoints.Add(New CategoricalDataPoint(128, "White")) barSeries.DataPoints.Add(New CategoricalDataPoint(143, "Smith")) barSeries.DataPoints.Add(New CategoricalDataPoint(111, "Jones")) barSeries.DataPoints.Add(New CategoricalDataPoint(118, "Marshall")) Me.RadChartView1.Series.Add(barSeries) Dim barSeries2 As New Telerik.WinControls.UI.BarSeries("Performance", "RepresentativeName") barSeries2.Name = "Q2" barSeries2.ShowLabels = True barSeries2.DataPoints.Add(New CategoricalDataPoint(153, "Harley")) barSeries2.DataPoints.Add(New CategoricalDataPoint(141, "White")) barSeries2.DataPoints.Add(New CategoricalDataPoint(130, "Smith")) barSeries2.DataPoints.Add(New CategoricalDataPoint(88, "Jones")) barSeries2.DataPoints.Add(New CategoricalDataPoint(109, "Marshall")) Me.RadChartView1.Series.Add(barSeries2) Dim lassoZoomController As New ChartPanZoomController() RadChartView1.Controllers.Add(lassoZoomController) End Sub Public Class CustomLabelElementDrawPart Inherits BarLabelElementDrawPart Public Sub New(owner As ChartSeries, renderer As IChartRenderer) MyBase.New(owner, renderer) End Sub Public Overrides Sub Draw() If Not Me.Element.ShowLabels Then Return End If Dim graphics As Graphics = TryCast(Me.Renderer.Surface, Graphics) Dim radGraphics As RadGdiGraphics = New RadGdiGraphics(graphics) Dim isSmartLabelsEnabled As Boolean = Me.Element.View.ShowSmartLabels Dim isLineToLabelEnabled As Boolean = Me.Element.DrawLinesToLabels For Each dataPointElement As DataPointElement In Me.Element.Children Dim categoricalDataPoint As CategoricalDataPoint = TryCast(dataPointElement.DataPoint, CategoricalDataPoint) For i As Integer = 0 To dataPointElement.Children.Count - 1 Dim labelElement As LabelElement = TryCast(dataPointElement.Children(i), LabelElement) If labelElement Is Nothing Then Continue For End If labelElement.OnLabelFormatting(New ChartViewLabelFormattingEventArgs(labelElement)) If Not labelElement.IsVisible OrElse String.IsNullOrEmpty(labelElement.Text) Then Continue For End If Dim rect As Rectangle Dim slot As RadRect = labelElement.GetLayoutSlot() If isSmartLabelsEnabled AndAlso labelElement.SmartRectangle <> Rectangle.Empty Then rect = labelElement.SmartRectangle Else slot = AdjustLayoutSlot(slot, labelElement.DataPointElement) rect = ChartRenderer.ToRectangle(slot) End If Dim state As Object = radGraphics.SaveState() Dim horizontalTranslate As Integer = rect.X + rect.Width / 2 Dim verticalTranslate As Integer = rect.Y + rect.Height / 2 Dim clipRect As RectangleF If TypeOf Me.Renderer Is CartesianRenderer Then Dim size As SizeF = graphics.MeasureString("W", Me.Element.Font) Dim plotRect As RadRect = Me.Element.Model.LayoutSlot plotRect.X += Me.ViewportOffsetX plotRect.Y += Me.ViewportOffsetY clipRect = ChartRenderer.ToRectangleF(plotRect) clipRect.Y -= size.Height / 2.0F clipRect.Height += size.Height clipRect.Width += size.Width * 2.0F graphics.SetClip(clipRect) End If Dim angle As Single = CSng(Me.Element.LabelRotationAngle) Mod 360.0F If angle <> 0 Then radGraphics.TranslateTransform(horizontalTranslate, verticalTranslate) radGraphics.RotateTransform(angle) radGraphics.TranslateTransform(-horizontalTranslate, -verticalTranslate) End If If isLineToLabelEnabled Then Me.DrawConnectingLine(radGraphics, labelElement, dataPointElement, isSmartLabelsEnabled AndAlso labelElement.SmartRectangle <> Rectangle.Empty) End If If labelElement.BackgroundShape IsNot Nothing Then labelElement.BackgroundShape.Paint(CType(radGraphics.UnderlayGraphics, Graphics), rect) End If Dim fill As Telerik.WinControls.Primitives.FillPrimitiveImpl = New Telerik.WinControls.Primitives.FillPrimitiveImpl(labelElement, Nothing) fill.PaintFill(radGraphics, 0, System.Drawing.Size.Empty, rect) Dim border As Telerik.WinControls.Primitives.BorderPrimitiveImpl = New Telerik.WinControls.Primitives.BorderPrimitiveImpl(labelElement, Nothing) border.PaintBorder(radGraphics, 0, System.Drawing.Size.Empty, rect) Using brush As Brush = New SolidBrush(labelElement.ForeColor) Dim drawRectangle As RectangleF = New RectangleF() drawRectangle.X = rect.X + labelElement.Padding.Left drawRectangle.Y = rect.Y + labelElement.Padding.Top drawRectangle.Width = rect.Width - labelElement.Padding.Right drawRectangle.Height = rect.Height - labelElement.Padding.Bottom Dim format As StringFormat = New StringFormat() format.Alignment = ContentAlignmentToHorizontalStringAlignment(labelElement.TextAlignment) format.LineAlignment = Me.ContentAlignmentToVerticalStringAlignment(labelElement.TextAlignment) graphics.DrawString(labelElement.Text, labelElement.Font, brush, drawRectangle, format) End Using If angle <> 0 Then radGraphics.ResetTransform() radGraphics.RestoreState(state) End If graphics.ResetClip() Next Next End Sub Public Function ContentAlignmentToVerticalStringAlignment(ByVal contentAlignment As ContentAlignment) As StringAlignment Dim result As StringAlignment Select Case contentAlignment Case contentAlignment.BottomCenter, contentAlignment.BottomLeft, contentAlignment.BottomRight result = StringAlignment.Far Case contentAlignment.TopCenter, contentAlignment.TopLeft, contentAlignment.TopRight result = StringAlignment.Near Case Else result = StringAlignment.Center End Select Return result End Function Private Function ContentAlignmentToHorizontalStringAlignment(ByVal contentAlignment As ContentAlignment) As StringAlignment Dim result As StringAlignment Select Case contentAlignment Case contentAlignment.BottomLeft, contentAlignment.MiddleLeft, contentAlignment.TopLeft result = StringAlignment.Near Case contentAlignment.BottomRight, contentAlignment.MiddleRight, contentAlignment.TopRight result = StringAlignment.Far Case Else result = StringAlignment.Center End Select Return result End Function End Class Private Sub RadChartView1_CreateRenderer(sender As Object, e As ChartViewCreateRendererEventArgs) e.Renderer = New CustomCartesianRenderer(e.Area) End Sub Public Class CustomCartesianRenderer Inherits CartesianRenderer Public Sub New(area As CartesianArea) MyBase.New(area) End Sub Protected Overrides Sub Initialize() MyBase.Initialize() For i As Integer = 0 To Me.DrawParts.Count - 1 Dim label As BarLabelElementDrawPart = TryCast(Me.DrawParts(i), BarLabelElementDrawPart) If (label IsNot Nothing) Then Me.DrawParts(i) = New CustomLabelElementDrawPart(label.Element, Me) End If Next End Sub End Class
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; } } }