I am trying to display labels on horizontally stacked bars. I want the labels inside each bar, but I can't figure out to accomplish this.
See attachment "labels-on-bars" for how I want it to look, using our previous charting tool. The other attachments "regular-labels" are just turning on showlabels, which has them positioned where I want vertically but not horizontally, and smart labels, which is just a jumbled mess of silliness.
I tried different "strategies" for the smart labels and I also tried deriving my own, but there was no clear indication of what to do in "CalculateLocations".
I've hooked into the LabelFormatting, and I can get the position of each bar, but the BarLabelElement only seems to have a position offset and no way to set the position in absolute coordinates.
6 Answers, 1 is accepted

Further investigation revealed that the labels are centered on the "full" bar when stacked, rather than just the stacked portion of the bar. So, that's lame.
I was able to calculate the proper position of the stacked portion of the bar and found the label's layout slot and was able to determine the offset to the centers. It actually worked... EXCEPT it totally kills everything. It ends up in some infinite updating loop.
How can I implement this in CalculateLocations?
Private Sub OnLabelFormattingHack(sender As Object, e As Telerik.WinControls.UI.ChartViewLabelFormattingEventArgs)
Dim DataPoint As Telerik.Charting.DataPoint = DirectCast(e.LabelElement.Parent, Telerik.WinControls.UI.DataPointElement).DataPoint
If TypeOf DataPoint Is Telerik.Charting.CategoricalDataPoint Then
Dim LabelElement As Telerik.WinControls.UI.BarLabelElement = DirectCast(e.LabelElement, Telerik.WinControls.UI.BarLabelElement)
Dim SeriesIndex As Integer = DataPoint.Parent.Index
If SeriesIndex > 0 Then
Dim PreviousSeries As Telerik.WinControls.UI.BarSeries = DirectCast(MyBase.Series(SeriesIndex - 1), Telerik.WinControls.UI.BarSeries)
Dim PreviousBar As Telerik.Charting.CategoricalDataPoint = DirectCast(PreviousSeries.DataPoints(DataPoint.Index), Telerik.Charting.CategoricalDataPoint)
Dim PreviousSlot As Telerik.Charting.RadRect = PreviousBar.LayoutSlot
Dim FullBarSlot As Telerik.Charting.RadRect = DataPoint.LayoutSlot
Dim AdjustedBarSlot As Telerik.Charting.RadRect = New Telerik.Charting.RadRect(PreviousSlot.Right, FullBarSlot.Y, FullBarSlot.Right - PreviousSlot.Right, FullBarSlot.Height)
Dim LabelSlot As Telerik.Charting.RadRect = LabelElement.GetLayoutSlot()
LabelElement.PositionOffset = New Drawing.PointF(CSng(AdjustedBarSlot.Center.X - LabelSlot.Center.X), CSng(AdjustedBarSlot.Center.Y - LabelSlot.Center.Y))
End If
End If
End Sub


Thank you for writing.
By default, the BarLabelElements are centered within the bar when the CombineMode is ChartSeriesCombineMode.Stack (please refer to the attached screenshot). If you need to manipulate the default label's position, as you have already found out, it is necessary to adjust the PositionOffset property for the label in the LabelFormatting event considering the DataPoint.LayoutSlot. Here is demonstrated a sample approach:
Sub
New
()
InitializeComponent()
AddHandler
Me
.RadChartView1.LabelFormatting,
AddressOf
OnLabelFormatting
Dim
barSeries
As
New
Telerik.WinControls.UI.BarSeries(
"Performance"
,
"RepresentativeName"
)
barSeries.Name =
"Q1"
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"
))
barSeries.CombineMode = ChartSeriesCombineMode.Stack
barSeries.ShowLabels =
True
Me
.RadChartView1.Series.Add(barSeries)
Dim
barSeries2
As
New
Telerik.WinControls.UI.BarSeries(
"Performance"
,
"RepresentativeName"
)
barSeries2.Name =
"Q2"
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"
))
barSeries2.CombineMode = ChartSeriesCombineMode.Stack
barSeries2.ShowLabels =
True
Me
.RadChartView1.Series.Add(barSeries2)
End
Sub
Private
Sub
OnLabelFormatting(sender
As
Object
, e
As
Telerik.WinControls.UI.ChartViewLabelFormattingEventArgs)
Dim
DataPoint
As
Telerik.Charting.DataPoint =
DirectCast
(e.LabelElement.Parent, Telerik.WinControls.UI.DataPointElement).DataPoint
If
TypeOf
DataPoint
Is
Telerik.Charting.CategoricalDataPoint
Then
Dim
LabelElement
As
Telerik.WinControls.UI.BarLabelElement =
DirectCast
(e.LabelElement, Telerik.WinControls.UI.BarLabelElement)
Dim
SeriesIndex
As
Integer
= DataPoint.Parent.Index
If
SeriesIndex > 0
Then
LabelElement.PositionOffset =
New
PointF(DataPoint.LayoutSlot.Width / 2 + 10, -1 * DataPoint.LayoutSlot.Height / 2 - 10)
End
If
End
If
End
Sub
Thus, when resizing the chart, the label keeps its position. The attached gif file illustrates the achieved result.
I hope this information helps. Should you have further questions I would be glad to help.
Dess
Telerik

I'm not sure you understand my current problem. Setting the label offset is causing some kind of infinite update loop where it just keeps calling the event over and over again. The only way to break the loop is to check whether the offset has been set,which causes me to not be able to catch the resizingand update the position offset.
My other question, was how can I implement my own positioning strategy because that seems like it should be a more effective means of controlling the positions. I tried implementing my own and just running basically this same code in CalculatePositions, but it would cause the chart to crash before it even got to my code. I also dried deriving from the vertical strategy and it would crash as well, even if I didn't override a single thing.
One final question I hadn't thought of before, is I would also like to control the label size but I did not see any means for setting the size. There was a size property but it was 0,0, and setting it did not appear to restrict the label's boundaries.
Thank you for writing back.
It is normal that the LabelFormatting event is fired a lot of times as it is responsible for the correct label's style. By using the provided code snippet I have noticed that the label elements are constantly moving horizontally as their LayoutSlot is changed when adjusting the PositionOffset property. By calling the GetLayoutSlot method of the BarLabelElement, the LabelFormatting events firing increases as well.
RadChartView provides a built-in mechanism for resolving labels overlapping with the SmartLabelsController. You can add the controller to the Controllers collection of RadChartView and it will optimize the arrangement of the labels in a way that there will be less overlaps. It is possible to create a custom SmartLabelsStrategy and position the labels manually. The following example shows a sample approach how you can position the labels in the top part of the chart:
Dim
controler =
New
MySmartLabelsController()
controler.Strategy =
New
MyStrategy()
Me
.RadChartView1.Controllers.Add(controler)
Public
Class
MyStrategy
Inherits
SmartLabelsStrategyBase
Public
Overrides
Sub
CalculateLocations(series
As
ChartSeriesCollection, plotArea
As
Rectangle)
Dim
labels
As
New
List(Of LabelElement)()
Dim
overlaps
As
New
List(Of
Integer
)()
Dim
x
As
Integer
= 70
Dim
y
As
Integer
= 30
Dim
spacing
As
Integer
= 6
For
Each
chartSeries
As
ChartSeries
In
series
If
Not
chartSeries.ShowLabels
OrElse
Not
chartSeries.IsVisible
Then
Continue
For
End
If
For
Each
point
As
DataPointElement
In
chartSeries.Children
Dim
label
As
LabelElement =
DirectCast
(point.Children(0), LabelElement)
Dim
labelRect
As
Rectangle = ChartRenderer.ToRectangle(label.GetLayoutSlot())
Dim
newRect =
New
Rectangle(x, y, labelRect.Width, labelRect.Height)
x += spacing + labelRect.Width
If
x + spacing + labelRect.Width > plotArea.Width - 100
Then
y += spacing + labelRect.Height
x = 70
End
If
label.SmartRectangle = newRect
labels.Add(label)
Next
Next
End
Sub
End
Class
Public
Class
MySmartLabelsController
Inherits
SmartLabelsController
Public
Overrides
Sub
CalculateLabelsPositions(series
As
ChartSeriesCollection, plotArea
As
Rectangle)
If
Me
.Strategy IsNot
Nothing
Then
Me
.Strategy.CalculateLocations(series, plotArea)
End
If
End
Sub
End
Class
As to the labels' size, note that they are auto sized to fit their content considering the font. If you need to enlarge the label, it is necessary to increase its LayoutSlot which requires creating a custom BarLabelElement:
Public
Class
CustomBarSeries
Inherits
BarSeries
Protected
Overrides
Function
CreatePointElement(point
As
DataPoint)
As
DataPointElement
Return
New
CustomBarPointElement(point)
End
Function
End
Class
Public
Class
CustomBarPointElement
Inherits
BarPointElement
Public
Overrides
ReadOnly
Property
ThemeRole
As
String
Get
Return
"Bar"
End
Get
End
Property
Public
Sub
New
(dataPoint
As
DataPoint)
MyBase
.
New
(dataPoint)
End
Sub
Protected
Overrides
Function
CreateLabelElement(point
As
DataPointElement)
As
LabelElement
Return
New
CustomBarLabelElement(point)
End
Function
End
Class
Public
Class
CustomBarLabelElement
Inherits
BarLabelElement
Public
Sub
New
(dataPointElement
As
DataPointElement)
MyBase
.
New
(dataPointElement)
End
Sub
Public
Overrides
Function
GetLayoutSlot()
As
RadRect
Dim
rect
As
RadRect =
MyBase
.GetLayoutSlot()
Return
New
RadRect(rect.X, rect.Y, rect.Width + 30, rect.Height + 20)
End
Function
End
Class
I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Telerik