25

I am using Microsoft Chart control in my project and I want to enable zooming feature in Chart Control by using Mouse Wheel, how can I achieve this?

but user don't have to click on chart, It should be like if mouse position is on my Chart than from that point onward by mouse wheel rolling it can zoom in / out

yogendra
  • 341
  • 2
  • 4
  • 11

4 Answers4

35

You'll want to use the MouseWheel event.

First make both axes of your chart zoomable:

chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;

And assign the event:

chart1.MouseWheel += chart1_MouseWheel;

Then in the event handler:

private void chart1_MouseWheel(object sender, MouseEventArgs e)
{
    var chart = (Chart)sender;
    var xAxis = chart.ChartAreas[0].AxisX;
    var yAxis = chart.ChartAreas[0].AxisY;

    try
    {
        if (e.Delta < 0) // Scrolled down.
        {
            xAxis.ScaleView.ZoomReset();
            yAxis.ScaleView.ZoomReset();
        }
        else if (e.Delta > 0) // Scrolled up.
        {
            var xMin = xAxis.ScaleView.ViewMinimum;
            var xMax = xAxis.ScaleView.ViewMaximum;
            var yMin = yAxis.ScaleView.ViewMinimum;
            var yMax = yAxis.ScaleView.ViewMaximum;

            var posXStart = xAxis.PixelPositionToValue(e.Location.X) - (xMax - xMin) / 4;
            var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + (xMax - xMin) / 4;
            var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - (yMax - yMin) / 4;
            var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + (yMax - yMin) / 4;

            xAxis.ScaleView.Zoom(posXStart, posXFinish);
            yAxis.ScaleView.Zoom(posYStart, posYFinish);
        }
    }
    catch { }            
}

The e.Delta property tells you how many wheel "scrolls" you've done, and can be useful.
Scrolling out at all will zoom out the whole way.

There's probably a cleaner way of doing this, but there it is. Hope this helps!

Community
  • 1
  • 1
tmwoods
  • 2,353
  • 7
  • 28
  • 55
  • This doesn't work for me. The Chart's Mousewheel event isn't firing. – F0r3v3r-A-N00b Dec 10 '15 at 01:58
  • 2
    Apparently, you have to do this first for it to work `void friendChart_MouseLeave(object sender, EventArgs e) { if (friendChart.Focused) friendChart.Parent.Focus(); } void friendChart_MouseEnter(object sender, EventArgs e) { if (!friendChart.Focused) friendChart.Focus(); }` [Mousewheel event not firing](http://stackoverflow.com/questions/13782763/mousewheel-event-not-firing) – F0r3v3r-A-N00b Dec 10 '15 at 02:36
  • Just saw this; funny thing is that you linked to one of my questions :) Glad you got it working. – tmwoods Jun 30 '16 at 16:19
  • Works grate but, how to zoom also the background image? – Pedro77 Jul 03 '18 at 02:11
  • 1
    It's been a while since I looked at this, but just assign `xAxis.ScaleView.ZoomReset(); yAxis.ScaleView.ZoomReset();` to a button click event? – tmwoods Aug 14 '19 at 22:23
  • This works well for me using the mouse but when I scroll with the touch pad it is way too touchy. Any suggestions on how to fix this? – user9964422 Aug 15 '19 at 14:24
  • I think if you alter the change factor (change the number `4` to a larger number) it will slow down the zoom. I _think_. Might have to play with it a bit. – tmwoods Aug 15 '19 at 15:28
2

I modificated code from above and added a reverse zooming using Stack.

private class ZoomFrame
{
    public double XStart { get; set; }
    public double XFinish { get; set; }
    public double YStart { get; set; }
    public double YFinish { get; set; }
}

private readonly Stack<ZoomFrame> _zoomFrames = new Stack<ZoomFrame>();
private void chart1_MouseWheel(object sender, MouseEventArgs e)
{
    var chart = (Chart)sender;
    var xAxis = chart.ChartAreas[0].AxisX;
    var yAxis = chart.ChartAreas[0].AxisY;

    try
    {
        if (e.Delta < 0)
        {
            if (0 < _zoomFrames.Count)
            {
                var frame = _zoomFrames.Pop();
                if (_zoomFrames.Count == 0)
                {
                    xAxis.ScaleView.ZoomReset();
                    yAxis.ScaleView.ZoomReset();
                }
                else
                {
                    xAxis.ScaleView.Zoom(frame.XStart, frame.XFinish);
                    yAxis.ScaleView.Zoom(frame.YStart, frame.YFinish);
                }
            }
        }
        else if (e.Delta > 0)
        {
            var xMin = xAxis.ScaleView.ViewMinimum;
            var xMax = xAxis.ScaleView.ViewMaximum;
            var yMin = yAxis.ScaleView.ViewMinimum;
            var yMax = yAxis.ScaleView.ViewMaximum;

            _zoomFrames.Push(new ZoomFrame { XStart = xMin, XFinish = xMax, YStart = yMin, YFinish = yMax });

            var posXStart = xAxis.PixelPositionToValue(e.Location.X) - (xMax - xMin) / 4;
            var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + (xMax - xMin) / 4;
            var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - (yMax - yMin) / 4;
            var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + (yMax - yMin) / 4;

            xAxis.ScaleView.Zoom(posXStart, posXFinish);
            yAxis.ScaleView.Zoom(posYStart, posYFinish);
        }
    }
    catch { }         
}

Hope this helps!

Cyrus
  • 2,261
  • 2
  • 22
  • 37
1

I modificated code from above and added a reverse zooming. So when you rotate a mouse wheel back the chart zoom out. Also i don't recommend use 2^n as divider of the interval because it cause lag.

numberOfZoom - counter of Zooming
private void Chart1_MouseWheel(object sender, MouseEventArgs e)
    {
        var chart = (Chart)sender;
        var xAxis = chart.ChartAreas[0].AxisX;
        var yAxis = chart.ChartAreas[0].AxisY;

        var xMin = xAxis.ScaleView.ViewMinimum;
        var xMax = xAxis.ScaleView.ViewMaximum;
        var yMin = yAxis.ScaleView.ViewMinimum;
        var yMax = yAxis.ScaleView.ViewMaximum;

        int IntervalX = 3;
        int IntervalY = 3;
        try
        {
            if (e.Delta < 0 && numberOfZoom > 0) // Scrolled down.
            {
                var posXStart = xAxis.PixelPositionToValue(e.Location.X) - IntervalX *2/ Math.Pow(2, numberOfZoom);
                var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + IntervalX *2/ Math.Pow(2, numberOfZoom);
                var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - IntervalY*2 / Math.Pow(2, numberOfZoom);
                var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + IntervalY*2 / Math.Pow(2, numberOfZoom);

                if (posXStart < 0) posXStart = 0;
                if (posYStart < 0) posYStart = 0;
                if (posYFinish > yAxis.Maximum) posYFinish = yAxis.Maximum;
                if (posXFinish > xAxis.Maximum) posYFinish = xAxis.Maximum;
                xAxis.ScaleView.Zoom(posXStart, posXFinish);
                yAxis.ScaleView.Zoom(posYStart, posYFinish);
                numberOfZoom--;
            }else if (e.Delta < 0 && numberOfZoom == 0) //Last scrolled dowm
            {
                yAxis.ScaleView.ZoomReset();
                xAxis.ScaleView.ZoomReset();
            }
            else if (e.Delta > 0) // Scrolled up.
            {

                var posXStart = xAxis.PixelPositionToValue(e.Location.X) - IntervalX  / Math.Pow(2, numberOfZoom);
                var posXFinish = xAxis.PixelPositionToValue(e.Location.X) + IntervalX / Math.Pow(2, numberOfZoom);
                var posYStart = yAxis.PixelPositionToValue(e.Location.Y) - IntervalY  / Math.Pow(2, numberOfZoom);
                var posYFinish = yAxis.PixelPositionToValue(e.Location.Y) + IntervalY  / Math.Pow(2, numberOfZoom);

                xAxis.ScaleView.Zoom(posXStart, posXFinish);
                yAxis.ScaleView.Zoom(posYStart, posYFinish);
                numberOfZoom++;
            }

            if (numberOfZoom < 0) numberOfZoom = 0;
        }
        catch { }
    }
user3857680
  • 31
  • 1
  • 4
0

Waaaay late to the party, but I had this challenge myself today. I am sure the following can be improved, still, but it's what I came up with. I tested this with .net 4.5.2 and 4.6, so I am not sure if it works with older frameworks.

I've combined a couple of answers from way in the past and made the following:

using System;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace ExampleApp
{
    public partial class Form1 : Form
    {
        private const float CZoomScale = 4f;
        private int FZoomLevel = 0;

        public Form1()
        {
            InitializeComponent();
            chart1.MouseWheel += Chart1_MouseWheel;
        }

        private void Chart1_MouseEnter(object sender, EventArgs e)
        {
            if (!chart1.Focused)
                chart1.Focus();
        }

        private void Chart1_MouseLeave(object sender, EventArgs e)
        {
            if (chart1.Focused)
                chart1.Parent.Focus();
        }

        private void Chart1_MouseWheel(object sender, MouseEventArgs e)
        {
            try {
                Axis xAxis = chart1.ChartAreas[0].AxisX;
                double xMin = xAxis.ScaleView.ViewMinimum;
                double xMax = xAxis.ScaleView.ViewMaximum;
                double xPixelPos = xAxis.PixelPositionToValue(e.Location.X);

                if (e.Delta < 0 && FZoomLevel > 0) {
                    // Scrolled down, meaning zoom out
                    if (--FZoomLevel <= 0) {
                        FZoomLevel = 0;
                        xAxis.ScaleView.ZoomReset();
                    } else {
                        double xStartPos = Math.Max(xPixelPos - (xPixelPos - xMin) * CZoomScale, 0);
                        double xEndPos = Math.Min(xStartPos + (xMax - xMin) * CZoomScale, xAxis.Maximum);
                        xAxis.ScaleView.Zoom(xStartPos, xEndPos);
                    }
                } else if (e.Delta > 0) {
                    // Scrolled up, meaning zoom in
                    double xStartPos = Math.Max(xPixelPos - (xPixelPos - xMin) / CZoomScale, 0);
                    double xEndPos = Math.Min(xStartPos + (xMax - xMin) / CZoomScale, xAxis.Maximum);
                    xAxis.ScaleView.Zoom(xStartPos, xEndPos);
                    FZoomLevel++;
                }
            } catch { }
        }
    }
}