Hi,
I'm trying to do some layering work, and before my 2 charts are rendered need to place some boxes on the chart. I've been reading up on chart layers and ordering trying to follow a few examples by creating 3 layers and adding them to the chart, then setting the layer order using UserLayerIndex and then implementing FillSceneGraph to draw the boxes I need in my custom layer .
The code I'm using is as follows:
void RenderCompositeChart(UltraChart chart, DataTable ColumnLineData, DataTable LineData, double StartAtY1, double EndAtY1, double StartAtY2, double EndAtY2, int
PageID)
{
// Combine the two existing data sets into a single dataset for the IG composite
// chart to use
CombinedTable = RenderCombinedAxisDataTable(ColumnLineData, LineData);
chart.ChartType =
.Composite;
();
chart.CompositeChart.ChartAreas.Add(area);
.X_Axis);
.Y_Axis);
.Y2_Axis);
xAxisColumn.DataType =
.String;
xAxisColumn.SetLabelAxisType =
.GroupBySeries;
xAxisColumn.Labels.ItemFormat =
.ItemLabel;
xAxisLine.DataType =
xAxisLine.SetLabelAxisType =
.ContinuousData;
yAxis.DataType =
.Numeric;
yAxis.Labels.ItemFormat =
.DataValue;
yAxis.LineThickness = 1;
y2Axis.DataType =
y2Axis.Labels.ItemFormat =
y2Axis.LineThickness = 1;
area.Axes.Add(xAxisColumn);
area.Axes.Add(xAxisLine);
area.Axes.Add(yAxis);
area.Axes.Add(y2Axis);
// Define two chart layers for drawing on
ColumnLayer.Key =
;
LineLayer.Key =
// Now, define the series for each layer... best way to do this
// is to iterate the measures and determine which axis it has been
// assigned to...
maIndex = -1;
_gadget.MeasureAxis)
maIndex++;
// Define a series for the relevant column in the data table
maSeries.Data.DataSource = CombinedTable;
maSeries.Label = ma.Name;
maSeries.Data.LabelColumn = CombinedTable.Columns[CombinedTable.Columns.Count - 1].ColumnName;
maSeries.Data.ValueColumn = CombinedTable.Columns[maIndex].ColumnName;
// Assign the series to the correct chart layer
.ASSIGNTO_LAYER1)
chart.Series.AddRange(
[] { maSeries });
ColumnLayer.Series.Add(maSeries);
(_gadget.Stacked)
colSeries = CombinedTable.Columns[maIndex];
colStackMinimum = 0;
colStackTotal = 0;
ValidateStackValues(colSeries,
colStackTotal);
StartAtY1 =
.Min(StartAtY1, colStackMinimum);
EndAtY1 =
.Max(EndAtY1, colStackTotal);
}
else
LineLayer.Series.Add(maSeries);
// Define the properties of each of the chart layers
ColumnLayer.AxisX = xAxisColumn;
ColumnLayer.AxisY = yAxis;
ColumnLayer.ChartArea = area;
ColumnLayer.ChartType =
.StackColumnChart;
)
chart.StackChart.StackStyle =
.Complete;
.ColumnChart;
ColumnLayer.ChartArea.PE.StrokeOpacity = 50;
LineLayer.AxisX = xAxisLine;
LineLayer.AxisY = y2Axis;
LineLayer.ChartArea = area;
LineLayer.ChartType = (_gadget.UseSmoothCurve ?
.LineChart);
chart.CompositeChart.ChartLayers.Add(ColumnLayer);
chart.CompositeChart.ChartLayers.Add(LineLayer);
chart.ColumnChart.SeriesSpacing = 0;
gridLayer.Key =
chart.CompositeChart.ChartLayers.Add(gridLayer);
//Set the colour scheme for the chart
ApplyChartPalette(chart);
chart.Border.Thickness = 0;
chart.ColumnChart.NullHandling = Infragistics.UltraChart.Shared.Styles.
.DontPlot;
//Setting tooltip for chart
SetToolTip(chart);
ColumnLegend.ChartLayers.Add(ColumnLayer);
LineLegend.ChartLayers.Add(LineLayer);
chart.CompositeChart.Legends.Add(ColumnLegend);
chart.CompositeChart.Legends.Add(LineLegend);
// Reserve a 20% space for the legend
ResizeChart(chart);
FontSize = 12;
// Formatting, formatting, formatting! Let's try and tidy these suckers up!
// I18CHECKED
chart.Axis.X.Labels.Font = verdana;
chart.Axis.Y.Labels.Font = verdana;
chart.Axis.Y2.Labels.Font = verdana;
// Make the legend 2 points smaller for now
ColumnLegend.LabelStyle.Font = legVerdana;
LineLegend.LabelStyle.Font = legVerdana;
// Tickmark style cannot be set to 'Smart' (bug logged as CAS-04642-NCZJWZ) when range limits have been set. I have therefore lifted the smart calibration algorithm from infragistics
// so I can still use it
interval = 0;
interval1 = 0;
// Perform smart scaling
AxisSmartScale(
interval);
interval1);
ScaleAxis(chart, yAxis, StartAtY1, EndAtY1, interval);
ScaleAxis(chart, y2Axis, StartAtY2, EndAtY2, interval1);
)EndAtY2;
EndAtY2Size = FontSize * (EndAtY2Val.ToString().Length + 1);
SetLabelAppearance(xAxisColumn,
.VerticalLeftFacing);
SetLabelAppearance(yAxis,
.Horizontal);
SetLabelAppearance(y2Axis,
);
SetLabelInterval(chart, ColumnLineData,
, xAxisColumn);
// Work out the extents we will need to place the legends to the right of the chart
szy1Legend = GetLegendDimensions(ColumnLineData, FontSize - 2);
szy2Legend = GetLegendDimensions(LineData, FontSize - 2);
// Which legend is going to be bigger, we will use that as our extent for the y-Axis'
yAxisExtent = (szy1Legend.Width > szy2Legend.Width ? szy1Legend.Width : szy2Legend.Width);
y2Axis.Extent = yAxisExtent;
LegendBorder.Thickness = 0;
// This code is left in, in case we ever need to refer to the creation of a legend
// for the column data. It's been turned off for now because it's unnecessary given the
// current x-axis label display
ColumnLegend.Bounds =
((width + EndAtY2Size) - yAxisExtent, 10, yAxisExtent - EndAtY2Size, szy1Legend.Height + 1);
ColumnLegend.BoundsMeasureType =
.Pixels;
ColumnLegend.Border = LegendBorder;
ColumnLegend.Visible =
(ColumnLegend.Visible)
LineLegend.Bounds =
((width + EndAtY2Size) - yAxisExtent, szy1Legend.Height + 35, yAxisExtent - EndAtY2Size, szy2Legend.Height + 3);
((width + EndAtY2Size) - szy2Legend.Width, 10, szy2Legend.Width - EndAtY2Size, szy2Legend.Height + 3);
// Reset the extent available to the legend to the Y2 axis one only
y2Axis.Extent = szy2Legend.Width;
LineLegend.BoundsMeasureType =
LineLegend.Border = LegendBorder;
ChartBorder.Thickness = 0;
chart.Border = ChartBorder;
chart.Visible =
// Shade differently any bars according to range or selection information
//chart.ChartDrawItem += new Infragistics.UltraChart.Shared.Events.ChartDrawItemEventHandler(chart_ChartDrawItem);
chart.ChartDrawItem +=
e)
chart_ChartDrawItem(sender, e, PageID);
};
// Add a custom layer if we need to
(_gadget.Ranges.Count != 0)
// Modify the order of drawing the layers...
chart.UserLayerIndex =
// Add the fill scene event to the chart
chart.FillSceneGraph +=
(CompositeChart_FillSceneGraph);
However, the final result shows the custom layer being drawn last - over the top of the 2 chart layers. Can anyone point me in the right direction?
Thank you! I doubt I would have ever got to that solution by myself!
Inserting at the last Line point worked perfectly, working FillSceneGraph code listed below (in case anyone else needs it!)
x;
y;
(chart.CompositeChart.ChartLayers.Count == 0)
x = (
];
y = (
Infragistics.UltraChart.Core.Primitives.
lastPrimLine;
primIndex=-1;
lastLinePrimIndex=0;
e.SceneGraph)
primIndex++;
// According to Infragistics, if we can find the last line primitive, we
// should insert our custom boxes there when doing composite chart custom
// rendering
lastPrimLine = prim
lastLinePrimIndex = primIndex;
_gadget.Ranges)
box;
// Dimension along y rather than x axis
)x.Map(r.Start) : x.MapMinimum;
)x.Map(r.End) : x.MapMaximum;
box =
(
)(y.MapMinimum - y.MapMaximum - 1)));
)y.Map(r.Start) : y.MapMinimum;
)y.Map(r.End) : y.MapMaximum;
)(rangeStart - rangeEnd)));
It sounds like UserLayerIndex isn't implemented for composite charts.
What you can do, however, is use scene.Insert(yourBox, n) instead of scene.Add(yourBox), where n is the index of the primitive you want to insert it before. You can find n by looping through the SceneGraph and examining the primitives in the order they occur.
A good place to insert something like this is after the last gridline (a Line primitive) or perhaps before your line series (a Polyline primitive) or column series (a Box primitive with a colored PE or with box.Layer set to a ColumnLayer).
To illustrate the problem I have. The black and green bars above, are meant to be appearing in the background...and are being drawn directly to my 3rd ChartLayerAppearance object in the composite chart using the FillSceneGraph method.
As an aside, I've been trying to get the ILayer type approach working ...
/// <summary>
///
Custom layer class to support range shading on the background grid of line and bar charts
</summary>
public class GridRangeLayer :
ILayer
_gadget;
_chart;
chartReference)
#region
ILayer Members
IColorModel _chartColorModel;
ChartColorModel
_chartColorModel; }
{ _chartColorModel =
_chartComponent;
ChartComponent {
_chartComponent; }
; }
_chartCore;
get { return _chartCore; }set { _chartCore = value ; }
_chartData;
get { return _chartData; }set { _chartData = value ; }
scene)
.Composite)
if (_chart.CompositeChart.ChartLayers.Count == 0) return ;
box.PE.Fill = r.BackColor;
box.PE.StrokeWidth = 0;
s
._brush.Dispose(); this._brush = new LinearGradientBrush(new Point((int)(x.MapMinimum + 1), (int)y.Map(8750)), new Point((int)(x.MapMinimum + 1), (int)y.Map(20000)), Color.Wheat, Color.White);
{ r
System.Collections.
get { return _grid; }set { _grid = value; }String _layerID;
public Rectangle OuterBound
{get { return _outerBound; }set { _outerBound = value ; }bool _visible;
However with this class, the _Grid property never seems to include any valid data - even though it appears to contain the correct series. This particular class works perfectly fine for standard charts. If I can get the correct grid data, I'm not sure what I'm missing here though, then I think the problem would be solved.
Any help appreciated!