Hi, is there a way to control the orientation of Labels in CartesianGridLineAnnotation?
To avoid overlapping I would like to have a vertical label on a vertical line, which looks to me like better visual design.
But I haven't found any properties to Control simple things like orientation.
4 Answers, 1 is accepted
You can control how the label of the CartesianGridLineAnnotation is oriented by using a custom CartesianGridLineAnnotationDrawPart. You can find below a sample code snippet demonstrating how to achieve the illustrated behavior:
public
RadForm1()
{
InitializeComponent();
this
.radChartView1.CreateRenderer += radChartView1_CreateRenderer;
ScatterLineSeries scatterSeries =
new
ScatterLineSeries();
scatterSeries.DataPoints.Add(
new
ScatterDataPoint(15, 19));
scatterSeries.DataPoints.Add(
new
ScatterDataPoint(18, 10));
scatterSeries.DataPoints.Add(
new
ScatterDataPoint(13, 15));
scatterSeries.DataPoints.Add(
new
ScatterDataPoint(10, 8));
scatterSeries.DataPoints.Add(
new
ScatterDataPoint(5, 2));
scatterSeries.PointSize =
new
SizeF(8, 8);
this
.radChartView1.Series.Add(scatterSeries);
ScatterLineSeries scatterSeries2 =
new
ScatterLineSeries();
scatterSeries2.DataPoints.Add(
new
ScatterDataPoint(2, 24));
scatterSeries2.DataPoints.Add(
new
ScatterDataPoint(7, 12));
scatterSeries2.DataPoints.Add(
new
ScatterDataPoint(15, 10));
scatterSeries2.DataPoints.Add(
new
ScatterDataPoint(18, 22));
scatterSeries2.DataPoints.Add(
new
ScatterDataPoint(20, 20));
scatterSeries2.Shape =
new
RoundRectShape(1);
scatterSeries2.PointSize =
new
SizeF(8, 8);
this
.radChartView1.Series.Add(scatterSeries2);
CartesianGridLineAnnotation annotation1 =
new
CartesianGridLineAnnotation();
annotation1.Axis =
this
.radChartView1.Axes[0]
as
CartesianAxis;
annotation1.Value = 5.8;
annotation1.Label =
"Some text"
;
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
CustomRenderer(e.Area
as
CartesianArea);
}
public
class
CustomRenderer : CartesianRenderer
{
public
CustomRenderer(CartesianArea area) :
base
(area)
{
}
protected
override
void
InitializeAnnotations(AnnotationDrawMode drawMode)
{
for
(
int
i = 0; i <
this
.Area.Annotations.Count; i++)
{
if
(
this
.Area.Annotations[i]
is
CartesianGridLineAnnotation)
{
this
.DrawParts.Add(
new
CustomCartesianGridLineAnnotationDrawPart(
this
.Area.Annotations[i]
as
CartesianGridLineAnnotation,
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.Instance | BindingFlags.NonPublic);
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.textOrientation = Orientation.Vertical;
tp.font =
this
.Element.Font;
tp.foreColor =
this
.Element.ForeColor;
tp.paintingRectangle =
new
RectangleF(rect.X, rect.Y, rect.Height, rect.Width);
tp.text =
this
.Element.Label;
FillPrimitiveImpl fill =
new
FillPrimitiveImpl(
this
.Element,
null
);
fill.PaintFill(radGraphics,
null
, rect);
radGraphics.DrawString(tp,
new
SizeF(rect.Height, rect.Width));
}
}
I hope this information helps. If you need any further assistance please don't hesitate to contact me.
Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik
well, thank you.
I understand the idea and the concept, but it doesn't work, and I'm wondered why it worked on your side.
The order of execution is kind of wrong.
In the line radChartView1.Series.Add(scatterSeries2);
the CreateRenderer event is already invoked, it tries to measures some label or so,
there are about 20 levels of function calls on the call-stack all internal Telerik code.
At this point in time, the annotations arent' created, but the InitializeAnnotation is also called immediatly in the constructor of the Renderer.
It tried to add the series later, but this doesn't work, since the axis is not created, before the serious is added.
I have a barseries, instead of a scatter, but I guess this will not make the difference.
However, there is a problem with the order of commands and events.
Probably the Drawpart has to be added at another place (another time).
something like radchartview.Area.Renderer exists, but is not public accessible.
It is necessary to subscribe to the CreateRenderer event before populating RadChartView. Thus, we will ensure that the custom rendering for the annotation and its label will be performed. I have attached my sample project for your reference. Could you please give it a try on your end and see how it works?
Should you have further questions please let me know.
Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik
Well I found it.
This "InitializeAnnotations" is called 4 times.
The first 2 times there are no existing annotations, I had a crash, while your kind of stated "if an annotation exists than ..."
But since the method is called twice with annotations, there are 2 drawparts created for the same element (also in your sample).
It doesn't seem to disturb the layout logic, it's just probably not the perfect way as DrawParts should be used.
I would expect, adding multiple Drawparts for the same Element causes exceptions; well it didn't.
However, your support compensates the leak of documentation.
Just by reading documentation you never get the approch of accessing private Properties via Reflection and stuff like this.