/// <summary>
        /// Draws the Y Axis labels, returns the width required to fit all the labels.
        /// </summary>
        /// <param name="dc"></param>
        /// <param name="scaleY"></param>
        /// <param name="labels"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        private double DrawYAxisLabels(DrawingContext dc, double scaleY, IEnumerable <GridLabel> labels, double offset)
        {
            // A list of areas on the label area on which we've already rendered text.
            // If we go to render text on a area that's already used, then go to the next
            // area and move out one width
            List <List <Range <double> > > usedDrawingAreas = new List <List <Range <double> > >();

            usedDrawingAreas.Add(new List <Range <double> >());

            List <double> maxWidths = new List <double>();

            maxWidths.Add(0);

            // First of all work out all of the render positions on the y axis

            List <LabelAndPos> labelAndPos = new List <LabelAndPos>();

            foreach (var gridLabel in labels)
            {
                double yValue = gridLabel.IsFloating ? (MinPoint.Y + MaxPoint.Y) * 0.5 : gridLabel.Location;
                if (!RangeY.Contains(yValue))
                {
                    continue;
                }
                double        yPos          = (-yValue + MinPoint.Y) * scaleY + this.ActualHeight;
                int           labelIndex    = 0;
                FormattedText formattedText = GetText(gridLabel.Text, gridLabel.Brush);

                UnrestrictedSize labelArea;
                if (gridLabel.Orientation == Orientation.Vertical)
                {
                    labelArea = new UnrestrictedSize(formattedText.Height, -formattedText.Width);
                }
                else
                {
                    labelArea = new UnrestrictedSize(formattedText.Width, formattedText.Height);
                }

                Point textPoint = new Point(-labelArea.Width - 1 + offset, yPos - labelArea.Height * 0.5 - 1);
                textPoint.Y = Math.Max(textPoint.Y, Math.Max(0, -labelArea.Height));
                // Uncomment this line to stop any labels going below the bottom chart line
                //textPoint.Y = Math.Min(textPoint.Y, this.ActualHeight - Math.Max(0,labelArea.Height));
                Range <double> range = new Range <double>(textPoint.Y, textPoint.Y + labelArea.Height);

                bool intersects;
                do
                {
                    intersects = false;
                    foreach (var usedRange in usedDrawingAreas[labelIndex])
                    {
                        if (range.Intersects(usedRange))
                        {
                            intersects = true;
                            labelIndex++;
                            if (usedDrawingAreas.Count <= labelIndex)
                            {
                                usedDrawingAreas.Add(new List <Range <double> >());
                                maxWidths.Add(0);
                            }
                            break;
                        }
                    }
                } while(intersects);

                usedDrawingAreas[labelIndex].Add(range);
                maxWidths[labelIndex] = Math.Max(maxWidths[labelIndex], labelArea.Width);
                labelAndPos.Add(new LabelAndPos(formattedText, textPoint, labelIndex, gridLabel.Orientation));
            }

            List <double> labelOffsets = new List <double>();

            labelOffsets.Add(0);
            double labelOffset = -2;

            foreach (double width in maxWidths)
            {
                labelOffset += 2 + width;
                labelOffsets.Add(labelOffset);
            }

            foreach (var gridLabel in labelAndPos)
            {
                FormattedText formattedText = gridLabel.Text;
                Point         textPoint     = gridLabel.Location;
                textPoint.X -= labelOffsets[gridLabel.Layer];
                if (gridLabel.Orientation == Orientation.Vertical)
                {
                    RotateTransform rotateTransform = new RotateTransform(-90);
                    dc.PushTransform(rotateTransform);
                    textPoint = rotateTransform.Inverse.Transform(textPoint);
                    dc.DrawText(formattedText, textPoint);
                    dc.Pop();
                }
                else
                {
                    dc.DrawText(formattedText, textPoint);
                }
            }

            double totalMaxWidth = -2;

            foreach (var maxWidth in maxWidths)
            {
                totalMaxWidth += maxWidth + 2;
            }
            return(totalMaxWidth);
        }
        /// <summary>
        /// Renders the grid lines and labels in immediate mode rendering style
        /// </summary>
        /// <param name="dc"></param>
        protected override void OnRender(DrawingContext dc)
        {
            Func <double, string> DefaultLabelGenerator = x => x.ToString();

            // Make sure these functions are assigned
            XAxisLabelGenerator = XAxisLabelGenerator ?? DefaultLabelGenerator;
            YAxisLabelGenerator = YAxisLabelGenerator ?? DefaultLabelGenerator;
            GridLineSpacingX    = GridLineSpacingX ?? GridLineSpacings.Base10;
            GridLineSpacingY    = GridLineSpacingY ?? GridLineSpacings.Base10;

            // Work out all the limits and scaling factors for rendering grid lines

            Size size = new Size(this.ActualWidth, this.ActualHeight);

            double scaleX = 0.0;
            double scaleY = 0.0;

            if (MaxPoint.X != MinPoint.X)
            {
                scaleX = size.Width / (MaxPoint.X - MinPoint.X);
            }
            if (MaxPoint.Y != MinPoint.Y)
            {
                scaleY = size.Height / (MaxPoint.Y - MinPoint.Y);
            }

            double spacingX = GridLineSpacingX(_optimalGridLineSpacing.X / scaleX);
            double spacingY = GridLineSpacingY(_optimalGridLineSpacing.Y / scaleY);

            int startXmult = (int)Math.Ceiling(MinPoint.X / spacingX);
            int endXmult   = (int)Math.Floor(MaxPoint.X / spacingX);
            int startYmult = (int)Math.Ceiling(MinPoint.Y / spacingY);
            int endYmult   = (int)Math.Floor(MaxPoint.Y / spacingY);

            double maxYLabelWidth  = 0;
            double maxXLabelHeight = 0;

            // Do a first pass of the x axis labels to make sure we have enough grid spacing to fit them in
            double maxXLabelWidth = 0;

            for (int lineNo = startXmult; lineNo <= endXmult; ++lineNo)
            {
                // Get the x position in graphing coordinates
                double xValue = lineNo * spacingX;
                // Check if there are any grid line overrides for this area of the chart
                if (GridLineOverrides.Any(x => x.Orientation == Orientation.Vertical && x.Range.Contains(xValue)))
                {
                    continue;
                }
                FormattedText formattedText = GetText(XAxisLabelGenerator(xValue), _gridLabelBrush);
                maxXLabelWidth = Math.Max(maxXLabelWidth, formattedText.Width);
            }

            // Adjust the X spacing accordingly
            double minGridLineSpacingX = (maxXLabelWidth + 8) * 2;

            if (minGridLineSpacingX > _optimalGridLineSpacing.X)
            {
                spacingX   = GridLineSpacingX(minGridLineSpacingX / scaleX);
                startXmult = (int)Math.Ceiling(MinPoint.X / spacingX);
                endXmult   = (int)Math.Floor(MaxPoint.X / spacingX);
            }

            // Do a first pass of the y axis labels to make sure we have enough grid spacing to fit them in
            double maxYLabelHeight = 0;

            for (int lineNo = startYmult; lineNo <= endYmult; ++lineNo)
            {
                // Get the y position in graphing coordinates
                double yValue = lineNo * spacingY;
                // Check if there are any grid line overrides for this area of the chart
                if (GridLineOverrides.Any(y => y.Orientation == Orientation.Horizontal && y.Range.Contains(yValue)))
                {
                    continue;
                }
                FormattedText formattedText = GetText(YAxisLabelGenerator(yValue), _gridLabelBrush);
                maxYLabelHeight = Math.Max(maxYLabelHeight, formattedText.Width);
            }

            // Adjust the Y spacing accordingly
            double minGridLineSpacingY = (maxYLabelHeight + 8) * 2;

            if (minGridLineSpacingY > _optimalGridLineSpacing.Y)
            {
                spacingY   = GridLineSpacingY(minGridLineSpacingY / scaleY);
                startYmult = (int)Math.Ceiling(MinPoint.Y / spacingY);
                endYmult   = (int)Math.Floor(MaxPoint.Y / spacingY);
            }

            LastSpacingX = spacingX;
            LastSpacingY = spacingY;

            // Draw all the vertical gridlines

            for (int lineNo = startXmult; lineNo <= endXmult; ++lineNo)
            {
                // Get the x position in graphing coordinates
                double xValue = lineNo * spacingX;

                // Check if there are any grid line overrides for this area of the chart
                if (GridLineOverrides.Any(x => x.Orientation == Orientation.Vertical && x.Range.Contains(xValue)))
                {
                    continue;
                }

                double xPos       = (xValue - MinPoint.X) * scaleX;
                Point  startPoint = new Point(xPos, size.Height);
                Point  endPoint   = new Point(xPos, 0);

                FormattedText formattedText = GetText(XAxisLabelGenerator(xValue), _gridLabelBrush);

                maxXLabelHeight = Math.Max(maxXLabelHeight, formattedText.Height);

                Point textPoint = new Point(xPos - formattedText.Width * .5, size.Height + 1);
                dc.DrawText(formattedText, textPoint);
                dc.DrawLine(_gridLinePen, startPoint, endPoint);
            }

            // Draw all the horizontal gridlines

            for (int lineNo = startYmult; lineNo <= endYmult; ++lineNo)
            {
                // Get the y position in graphing coordinates
                double yValue = lineNo * spacingY;

                // Check if there are any grid line overrides for this area of the chart
                if (GridLineOverrides.Any(y => y.Orientation == Orientation.Horizontal && y.Range.Contains(yValue)))
                {
                    continue;
                }

                double yPos       = (-yValue + MinPoint.Y) * scaleY + size.Height;
                Point  startPoint = new Point(0, yPos);
                Point  endPoint   = new Point(size.Width, yPos);

                FormattedText   formattedText   = GetText(YAxisLabelGenerator(yValue), _gridLabelBrush);
                RotateTransform rotateTransform = new RotateTransform(-90);
                Point           textPoint       = new Point(-formattedText.Height - 1, yPos + formattedText.Width * .5);
                textPoint = rotateTransform.Inverse.Transform(textPoint);
                dc.PushTransform(rotateTransform);
                dc.DrawText(formattedText, textPoint);
                dc.Pop();

                dc.DrawLine(_gridLinePen, startPoint, endPoint);

                maxYLabelWidth = Math.Max(maxYLabelWidth, formattedText.Height);
            }

            foreach (var gridLineOverride in GridLineOverrides)
            {
                switch (gridLineOverride.Orientation)
                {
                case Orientation.Vertical:

                    // Draw the Vertical Lines

                    foreach (var gridLine in gridLineOverride.GridLines)
                    {
                        double xValue = gridLine.Location;
                        if (!RangeX.Contains(xValue))
                        {
                            continue;
                        }
                        double xPos       = (xValue - MinPoint.X) * scaleX;
                        Point  startPoint = new Point(xPos, size.Height);
                        Point  endPoint   = new Point(xPos, 0);
                        dc.DrawLine(gridLine.Pen, startPoint, endPoint);
                    }

                    break;

                case Orientation.Horizontal:

                    // Draw the Horizontal lines

                    foreach (var gridLine in gridLineOverride.GridLines)
                    {
                        double yValue = gridLine.Location;
                        if (!RangeY.Contains(yValue))
                        {
                            continue;
                        }
                        double yPos       = (-yValue + MinPoint.Y) * scaleY + size.Height;
                        Point  startPoint = new Point(gridLine.Extended ? -YGridLineLabelBar.Width : 0, yPos);
                        Point  endPoint   = new Point(size.Width, yPos);
                        dc.DrawLine(gridLine.Pen, startPoint, endPoint);
                    }
                    break;
                }
            }

            // Draw the grid line override Labels

            foreach (var gridLineOverride in GridLineOverrides)
            {
                switch (gridLineOverride.Orientation)
                {
                case Orientation.Vertical:
                    // TODO: Draw the X-Axis labels
                    break;

                case Orientation.Horizontal:
                    // Draw the Y-Axis labels
                    maxYLabelWidth = Math.Max(maxYLabelWidth, DrawYAxisLabels(dc, scaleY, gridLineOverride.GridLabels, 0));
                    break;
                }
            }

            XGridLineLabelBar.Height = maxXLabelHeight + 2;
            YGridLineLabelBar.Width  = maxYLabelWidth + 2;

            // Now render the Y axis label

            YAxisTitleBar.Width  = DrawYAxisLabels(dc, scaleY, YAxisTitles, -YGridLineLabelBar.Width - 2) + 2;
            XAxisTitleBar.Height = DrawXAxisLabels(dc, scaleX, XAxisTitles, XGridLineLabelBar.Height + 2 + this.ActualHeight) + 2;
        }