private Grid _series; // grid for series of points

        #endregion Fields

        #region Methods

        internal static IEnumerable<double> GetLabelValues(Range dataRange, double maxLabel)
        {
            double maxValue = Math.Max(-dataRange.Min, dataRange.Max);
            if (maxValue <= 0.0) // all values equal to 0
            {
                yield return 0.0;
            }
            else
            {
                double nbDigit = Math.Ceiling(Math.Log10(maxValue / maxLabel));
                double step = Math.Pow(10, nbDigit); // increment the label by a power of 10

                if (step * 1.5 > maxValue)
                    step /= 2; // label 5 by 5 else only one label that may be far and thus inelegant

                for (double current = 0.0; current <= dataRange.Max + step / 2.0; current += step)
                {
                    yield return current;
                }
                for (double current = -step; current >= dataRange.Min - step / 2.0; current -= step)
                {
                    yield return current;
                }
            }
        }
        private Grid _series; // grid for series of points

        #endregion Fields

        #region Methods

        /// <summary>
        /// Generates the column chart.
        /// </summary>
        protected override void GenerateChart()
        {
            RootElement.Children.Clear();
            var data = ItemsSource;
            if (data != null && data.Any())
            {
                double max = Math.Max(0, data.Values.Max());
                double min = Math.Min(0, data.Values.Min());
                Range dataRange = new Range(min, max);

                // Get the label values
                var labels = BarChart.GetLabelValues(dataRange, MaxLabel).ToArray();

                // The label might be out of the current range (e.g if max value = '99', there will be a label '100')
                // Extend the range to include the labels
                dataRange.Min = Math.Min(dataRange.Min, labels.Min());
                dataRange.Max = Math.Max(dataRange.Max, labels.Max());

                // Create chart main structure
                GenerateChartStructure();

                // Generate the labels
                foreach (double val in labels)
                    GenerateLabel(val, dataRange);

                // Generate data points
                foreach (var kvp in data)
                {
                    GenerateDataPoint(kvp, dataRange);
                }
            }
        }
        private void GenerateChartStructure(Range range)
        {
            // Main grid
            Grid root = new Grid { Margin = new Thickness(0, 10, 0, 10) }; // Vertical Margin for labels
            root.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0, GridUnitType.Auto) }); // Column for labels
            root.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); // Column for chart
            RootElement.Children.Add(root);

            // Grid for labels (column 0)
            _labels = new Grid();
            root.Children.Add(_labels);

            // Grid for chart (column 1)
            Grid chart = new Grid();
            Grid.SetColumn(chart, 1);
            root.Children.Add(chart);

            // Grid for Axis in coordinate 0
            Grid gridAxis = new Grid();
            double fraction = range.Fraction(0);

            gridAxis.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1 - fraction, GridUnitType.Star) });
            gridAxis.RowDefinitions.Add(new RowDefinition { Height = new GridLength(fraction, GridUnitType.Star) });
            chart.Children.Add(gridAxis);

            // Axis
            Border axisX = new Border
            {
                BorderThickness = new Thickness(0.75),
                BorderBrush = ForegroundColor,
                Opacity = 0.5,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Bottom,
                Margin = new Thickness(-5, 0, -5, 0)
            };
            gridAxis.Children.Add(axisX);

            Border axisY = new Border
                           	{
                           		BorderThickness = new Thickness(0.75),
                           		BorderBrush = ForegroundColor,
                           		Opacity = 0.5,
                           		HorizontalAlignment = HorizontalAlignment.Left,
                           		VerticalAlignment = VerticalAlignment.Stretch,
                           		Margin = new Thickness(0, -5, 0, -5)
                            };
            chart.Children.Add(axisY);

            // Grid for series of points
            _series = new Grid();
            chart.Children.Add(_series);
        }
        // Generate one label
        private void GenerateLabel(double val, Range dataRange)
        {
            double fraction = dataRange.Fraction(val);

            Grid label = new Grid();
            _labels.Children.Add(label);
            label.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1 - fraction, GridUnitType.Star) });
            label.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Pixel) });
            label.RowDefinitions.Add(new RowDefinition { Height = new GridLength(fraction, GridUnitType.Star) });

            StackPanel stackPanel = new StackPanel
                                        {
                                            VerticalAlignment = VerticalAlignment.Center,
                                            HorizontalAlignment = HorizontalAlignment.Right,
                                            Orientation = Orientation.Horizontal,
                                            Margin = new Thickness(0, -20, 0, -20)
                                        };

            Grid.SetRow(stackPanel, 1);
            label.Children.Add(stackPanel);

            TextBlock text = new TextBlock
            {
                Text = FormattedValue(val)
            };
            stackPanel.Children.Add(text);

            Rectangle rect = new Rectangle { Stroke = ForegroundColor, Width = 5, Height = 1, Opacity = 0.5, Margin = new Thickness(2, 0, 0, 0) };
            stackPanel.Children.Add(rect);
        }
        private void GenerateDataPoint(KeyValuePair<string, double> kvp, Range range)
        {
            // Add a column and add a grid in this column
            _series.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            Grid point = new Grid();
            Grid.SetColumn(point, _series.ColumnDefinitions.Count - 1);
            _series.Children.Add(point);

            // Divide the grid in 3 rows
            double val = kvp.Value;
            double negativeFraction = range.Fraction(Math.Min(val, 0));
            double positiveFraction = 1 - range.Fraction(Math.Max(val, 0));

            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(positiveFraction, GridUnitType.Star) });
            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1 - positiveFraction - negativeFraction, GridUnitType.Star), MinHeight = MinBarSize});
            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(negativeFraction, GridUnitType.Star) });

            Border axisX = new Border
            {
                BorderThickness = new Thickness(0.75),
                BorderBrush = ForegroundColor,
                Opacity = 0.5,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = val > 0 ? VerticalAlignment.Bottom : VerticalAlignment.Top,
                Margin = new Thickness(-5, 0, -5, 0)
            };
            Grid.SetRow(axisX, 1);
            point.Children.Add(axisX);

            // Put a bar in the middle row
            Border bar = new Border
            {
                Margin = new Thickness(MarginBetweenBars, 0, MarginBetweenBars, 0),
                Background = GetColorByIndex(0)
            };

            Grid.SetRow(bar, 1);
            SetTooltip(bar, kvp.Key, FormattedValue(kvp.Value));
            point.Children.Add(bar);
        }
        private void GenerateDataPoints(IDictionary<string, double> data, Range dataRange)
        {
            int count = data.Count;

            // Project the data in order to get the points in percent of the drawing space (rectangle 1*1)
            var points = data.Select((kvp, ind) => new { x = (2.0 * ind + 1.0) / (2.0 * count), y = 1.0 - dataRange.Fraction(kvp.Value)});

            // Generate the lines from point to point
            var firstPoint = points.First();
            PathFigure pathFigure = new PathFigure { IsClosed = false, StartPoint = new Point(firstPoint.x, firstPoint.y) };
            foreach (var point in points.Skip(1))
            {
                pathFigure.Segments.Add(new LineSegment {Point = new Point(point.x, point.y)});
            }

            //Add these two empty line segments to force the drawing to stretch properly
            PathFigure pathFigure2 = new PathFigure { StartPoint = new Point(0, 0) };
            pathFigure2.Segments.Add(new LineSegment { Point = new Point(0, 0) });
            PathFigure pathFigure3 = new PathFigure { StartPoint = new Point(1, 1) };
            pathFigure3.Segments.Add(new LineSegment { Point = new Point(1, 1) });

            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.Figures.Add(pathFigure2);
            pathGeometry.Figures.Add(pathFigure3);
            pathGeometry.Figures.Add(pathFigure);
            Path path = new Path
            {
                Stretch = Stretch.Fill, // stretch the polyline to fill the chart drawing space
                Stroke = GetColorByIndex(0),
                StrokeThickness = 2,
                StrokeLineJoin = PenLineJoin.Round,
                Data = pathGeometry,
                Margin = new Thickness(-1,-1,-1,-1) // half of strokeThickness
            };
            _series.Children.Add(path);

            // Add a new grid for the points
            Grid pointsGrid = new Grid();
            _series.Children.Add(pointsGrid);

            // Generate the points
            foreach (var kvp in data)
            {
                GenerateDataPoint(pointsGrid, kvp, dataRange);
            }
        }
        private void GenerateDataPoint(Grid points, KeyValuePair<string, double> kvp, Range range)
        {
            // Add a column and add a grid in this column
            points.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
            Grid point = new Grid();
            Grid.SetColumn(point, points.ColumnDefinitions.Count - 1);
            points.Children.Add(point);

            // Divide the grid in 3 rows
            double val = kvp.Value;
            double fraction = range.Fraction(val);

            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1 - fraction, GridUnitType.Star) });
            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(0, GridUnitType.Pixel) });
            point.RowDefinitions.Add(new RowDefinition { Height = new GridLength(fraction, GridUnitType.Star) });

            // Put a point in the middle row
            Ellipse ellipse = new Ellipse
                              	{
                              		Height = 10,
                              		Width = 10,
                              		Fill = GetColorByIndex(0),
                              		HorizontalAlignment = HorizontalAlignment.Center,
                              		VerticalAlignment = VerticalAlignment.Center,
                              		Margin = new Thickness(0, -20, 0, -20),
                              		Stroke = ForegroundColor,
                              		StrokeThickness = 1
                                };

            Grid.SetRow(ellipse, 1);
            SetTooltip(ellipse, kvp.Key, FormattedValue(kvp.Value));
            point.Children.Add(ellipse);
        }