1 Answer, 1 is accepted
Hi, Tim,
RadChartView allows you to specify only the distance between bar groups as percent by the CategoricalAxis.GapLength. However, if you want to adjust the spacing between the stacked bar groups, I have prepared a sample code snippet for your reference which uses a custom renderer:
public RadForm1()
{
InitializeComponent(); new RadControlSpyForm().Show();
this.radChartView1.CreateRenderer += new ChartViewCreateRendererEventHandler(radChartView1_CreateRenderer);
BarSeries barSeries = new 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"));
this.radChartView1.Series.Add(barSeries);
BarSeries barSeries2 = new 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"));
this.radChartView1.Series.Add(barSeries2);
BarSeries barSeries3 = new BarSeries("Performance", "RepresentativeName");
barSeries3.Name = "Q3";
barSeries3.DataPoints.Add(new CategoricalDataPoint(177, "Harley"));
barSeries3.DataPoints.Add(new CategoricalDataPoint(128, "White"));
barSeries3.DataPoints.Add(new CategoricalDataPoint(143, "Smith"));
barSeries3.DataPoints.Add(new CategoricalDataPoint(111, "Jones"));
barSeries3.DataPoints.Add(new CategoricalDataPoint(118, "Marshall"));
this.radChartView1.Series.Add(barSeries3);
BarSeries barSeries4 = new BarSeries("Performance", "RepresentativeName");
barSeries4.Name = "Q4";
barSeries4.DataPoints.Add(new CategoricalDataPoint(153, "Harley"));
barSeries4.DataPoints.Add(new CategoricalDataPoint(141, "White"));
barSeries4.DataPoints.Add(new CategoricalDataPoint(130, "Smith"));
barSeries4.DataPoints.Add(new CategoricalDataPoint(88, "Jones"));
barSeries4.DataPoints.Add(new CategoricalDataPoint(109, "Marshall"));
this.radChartView1.Series.Add(barSeries4);
barSeries.CombineMode = ChartSeriesCombineMode.Stack;
barSeries2.CombineMode = ChartSeriesCombineMode.Stack;
barSeries3.CombineMode = ChartSeriesCombineMode.Stack;
barSeries4.CombineMode = ChartSeriesCombineMode.Stack;
barSeries.StackGroupKey = 0;
barSeries2.StackGroupKey = 0;
barSeries3.StackGroupKey = 1;
barSeries4.StackGroupKey = 1;
}
void radChartView1_CreateRenderer(object sender, ChartViewCreateRendererEventArgs e)
{
RenderParameter param = new RenderParameter();
param.BarOffset = 5;
e.Renderer = new CustomCartesianRenderer(param, e.Area as CartesianArea);
}
public class RenderParameter
{
public int BarOffset { get; set; }
public RenderParameter()
{
}
}
public class CustomCartesianRenderer : CartesianRenderer
{
internal RenderParameter RenderParameter { get; set; }
public CustomCartesianRenderer(CartesianArea area) : base(area)
{
}
public CustomCartesianRenderer(RenderParameter renderParameter, CartesianArea area) : base(area)
{
RenderParameter = renderParameter ?? throw new ArgumentNullException(nameof(renderParameter));
}
protected override void Initialize()
{
base.Initialize();
for (int i = 0; i < this.DrawParts.Count; i++)
{
BarSeriesDrawPart linePart = this.DrawParts[i] as BarSeriesDrawPart;
if (linePart != null)
{
this.DrawParts[i] = new CustomBarSeriesDrawPart((BarSeries)linePart.Element, this);
}
}
}
}
public class CustomBarSeriesDrawPart : BarSeriesDrawPart
{
private RectangleF AdjustBarDataPointBounds(DataPointElement point, RectangleF bounds)
{
RectangleF barBounds = bounds;
if (point.BorderBoxStyle == BorderBoxStyle.SingleBorder || point.BorderBoxStyle == BorderBoxStyle.OuterInnerBorders)
{
barBounds.X += point.BorderWidth - (int)((point.BorderWidth - 1f) / 2f);
barBounds.Width -= point.BorderWidth;
barBounds.Y += point.BorderWidth - (int)((point.BorderWidth - 1f) / 2f);
barBounds.Height -= point.BorderWidth;
}
else if (point.BorderBoxStyle == BorderBoxStyle.FourBorders)
{
barBounds.Y += 1;
barBounds.Height -= 1;
barBounds.X += 1;
barBounds.Width -= 1;
}
if (((CartesianRenderer)this.Renderer).Area.Orientation == System.Windows.Forms.Orientation.Horizontal)
{
barBounds.X--;
}
return barBounds;
}
public CustomBarSeriesDrawPart(BarSeries series, IChartRenderer renderer) : base(series, renderer)
{
}
public override void DrawSeriesParts()
{
CustomCartesianRenderer customRenderer = this.Renderer as CustomCartesianRenderer;
RenderParameter param = customRenderer.RenderParameter;
int xOffset = 0;
Graphics graphics = ((ChartRenderer)this.Renderer).Graphics;
RadGdiGraphics radGraphics = new RadGdiGraphics(graphics);
for (int j = 0; j < this.Element.DataPoints.Count; j++)
{
RadRect slot = this.Element.DataPoints[j].LayoutSlot;
if (this.Element.Name=="Q3" ||this.Element.Name=="Q4") {
xOffset = param.BarOffset;
}
RectangleF barBounds = new RectangleF((float)(this.OffsetX + slot.X+xOffset ), (float)(this.OffsetY + slot.Y), (float)slot.Width, (float)slot.Height);
bool isAreaVertical = ((BarSeries)this.Element).View.GetArea<CartesianArea>().Orientation == System.Windows.Forms.Orientation.Vertical;
DataPointElement childElement = (DataPointElement)this.Element.Children[j];
if (isAreaVertical)
{
float realHeight = barBounds.Height * childElement.HeightAspectRatio;
barBounds.Y += barBounds.Height - realHeight;
barBounds.Height = realHeight;
}
else
{
float realWidth = barBounds.Width * childElement.HeightAspectRatio;
barBounds.Width = realWidth;
}
barBounds = AdjustBarDataPointBounds(childElement, barBounds);
if (isAreaVertical)
{
barBounds.Width = Math.Max(barBounds.Width, 1f);
}
else
{
barBounds.Height = Math.Max(barBounds.Height, 1f);
}
RadImageShape background = childElement.BackgroundShape;
if (background != null)
{
background.Paint(graphics, barBounds);
}
Telerik.WinControls.Primitives.FillPrimitiveImpl fill = new Telerik.WinControls.Primitives.FillPrimitiveImpl(childElement, null);
fill.PaintFill(radGraphics, 0, new SizeF(1, 1), barBounds);
Telerik.WinControls.Primitives.BorderPrimitiveImpl border = new Telerik.WinControls.Primitives.BorderPrimitiveImpl(childElement, null);
border.PaintBorder(radGraphics, 0, new SizeF(1, 1), barBounds);
if (childElement.Image != null)
{
GraphicsState state = graphics.Save();
Region clipRegion = graphics.Clip;
graphics.SetClip(barBounds, CombineMode.Intersect);
Telerik.WinControls.Primitives.ImagePrimitiveImpl pointImage = new Telerik.WinControls.Primitives.ImagePrimitiveImpl(childElement);
pointImage.PaintImage(radGraphics, childElement.Image, barBounds, childElement.ImageLayout, childElement.ImageAlignment, childElement.ImageOpacity, false);
graphics.Clip = clipRegion;
graphics.Restore(state);
}
}
}
}
The achieved result is illustrated in the below screenshot. Note that this is just a sample approach and it may not cover all possible cases. Feel free to modify and further extend it in a way that fits the custom requirements you have:
I hope this information helps.
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.
Thank you Dess that is what I wanted it to look like. I was hoping for property I could set but this does adjust the spacing the way I needed.