L.E.O
L.E.O

Reputation: 1109

Chart Control Y axis auto-scale on scrolling

I've been searching the net for some time now yet still haven't found any good solution to my problem. I want to make MS Chart to automatically rescale Y axis on scrolling to make sure that all data points are visible. The twist here is that I need to have the ability to exclude certain series from being used for auto scale. So far I only found solutions that offer to iterate through the entire point collection on AxisViewChanged event, which doesn't work well when you have large collections of points and a few series to iterate through. I was wondering if there was any way to narrow the search by obtaining data points that are between currently visible min and max X values. Any help would be appreciated.

Edit Heres the image. As you can see the candlesticks in the middle aren't entirely visible. enter image description here

Upvotes: 11

Views: 39876

Answers (5)

Boris Zinchenko
Boris Zinchenko

Reputation: 2282

Above answers were very helpful for me. However, I have a chart with multiple charting areas. I have adapted the code to scale up to all chart areas:

    foreach (ChartArea area in chart1.ChartAreas)
    {
      List<double> allNumbers = new List<double>();

      foreach (Series item in chart1.Series)
        if (item.ChartArea == area.Name)
          allNumbers.AddRange(item.Points.Where((x, i) => i >= start && i <= end).Select(x => x.YValues[0]).ToList());

      double ymin = allNumbers.Min();
      double ymax = allNumbers.Max();

      if (ymax > ymin)
      {
        double offset = 0.02 * (ymax - ymin);
        area.AxisY.Maximum = ymax + offset;
        area.AxisY.Minimum = ymin - offset;
      }
    }

Upvotes: 0

user369122
user369122

Reputation: 822

Based on previous answers

private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
    {
        if(e.Axis.AxisName == AxisName.X)
        {
            int start = (int)e.Axis.ScaleView.ViewMinimum;
            int end = (int)e.Axis.ScaleView.ViewMaximum;

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

            foreach(Series item in chart1.Series)
                allNumbers.AddRange(item.Points.Where((x, i) => i >= start && i <= end).Select(x => x.YValues[0]).ToList());

            double ymin = allNumbers.Min();
            double ymax = allNumbers.Max();

            chart1.ChartAreas[0].AxisY.ScaleView.Position = ymin;
            chart1.ChartAreas[0].AxisY.ScaleView.Size = ymax - ymin;
        }
    }

It could be you have more series in the chartarea. In this case you pick the high and low of all series in the area instead of just one.

regards,

Matthijs

Upvotes: 0

Chris J
Chris J

Reputation: 31

This is a minor improvement on the excellent submission from Shivaram K R, to prevent open, close and low dropping off the bottom for the lowest points on financial charts with four Y values: high, low, open close.

// The following line goes in your form constructor
this.chart1.AxisViewChanged += new EventHandler<ViewEventArgs> (this.chart1_AxisViewChanged);


private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
{ 
    if (e.Axis.AxisName == AxisName.X) 
    { 
        int start = (int)e.Axis.ScaleView.ViewMinimum; 
        int end = (int)e.Axis.ScaleView.ViewMaximum; 
        // Use two separate arrays, one for highs (same as temp was in Shavram's original)
        // and a new one for lows which is used to set the Y axis min.
        double[] tempHighs = chart1.Series[0].Points.Where((x, i) => i >= start && i <= end).Select(x => x.YValues[0]).ToArray();
        double[] tempLows = chart1.Series[0].Points.Where((x, i) => i >= start && i <= end).Select(x => x.YValues[1]).ToArray();
        double ymin = tempLows.Min();
        double ymax = tempHighs.Max();

        chart1.ChartAreas[0].AxisY.ScaleView.Position = ymin; 
        chart1.ChartAreas[0].AxisY.ScaleView.Size = ymax - ymin; 
    } 
} 

Upvotes: 3

Shivaram K R
Shivaram K R

Reputation: 91

Use a query to find out which series you want to use for finding ymin and ymax in the code.

private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
    {
        if (e.Axis.AxisName == AxisName.X)
        {
            int start = (int)e.Axis.ScaleView.ViewMinimum;
            int end = (int)e.Axis.ScaleView.ViewMaximum;

            // Series ss = chart1.Series.FindByName("SeriesName");
            // use ss instead of chart1.Series[0]

            double[] temp = chart1.Series[0].Points.Where((x, i) => i >= start && i <= end).Select(x => x.YValues[0]).ToArray();
            double ymin = temp.Min();
            double ymax = temp.Max();

            chart1.ChartAreas[0].AxisY.ScaleView.Position = ymin;
            chart1.ChartAreas[0].AxisY.ScaleView.Size = ymax - ymin;
        }
    }

Upvotes: 6

Stecya
Stecya

Reputation: 23266

you can try this code

        DateTime date = DateTime.Now;
        chart1.ChartAreas[0].AxisX.Minimum = 0;
        chart1.ChartAreas[0].AxisX.Maximum = 20;
        Random r = new Random((int)date.Ticks);

        chart1.Series[0].ChartType = SeriesChartType.Candlestick;
        chart1.Series[0].Color = Color.Green;
        chart1.Series[0].XValueType = ChartValueType.Time;
        chart1.Series[0].IsXValueIndexed = true;
        chart1.Series[0].YValuesPerPoint = 4;
        chart1.Series[0].CustomProperties = "MaxPixelPointWidth=10";
        for (int i = 0; i < 100; i++ )
        {
            DataPoint point = new DataPoint(date.AddHours(i).ToOADate(), new double[] { r.Next(10, 20), r.Next(30, 40), r.Next(20, 30), r.Next(20, 30) });
            chart1.Series[0].Points.Add(point);
        }

        int min = (int)chart1.ChartAreas[0].AxisX.Minimum;
        int max = (int)chart1.ChartAreas[0].AxisX.Maximum;

        if (max > chart1.Series[0].Points.Count)
            max = chart1.Series[0].Points.Count;

        var points = chart1.Series[0].Points.Skip(min).Take(max - min);

        var minValue = points.Min(x => x.YValues[0]);
        var maxValue = points.Max(x => x.YValues[1]);

        chart1.ChartAreas[0].AxisY.Minimum = minValue;
        chart1.ChartAreas[0].AxisY.Maximum = maxValue;

enter image description here

Upvotes: 6

Related Questions