0

I want to zoom a control based on the current cursor location. The zoom should keep the point behind mouse cursor on the same position.

I have attached two files:

In Form2.cs, I'm zooming and drawing an image on a Form directly. It works fine as intended but it's not my requirement.

In Form1.cs, I'm zooming a panel using the same logic but it's behaving very differently. Can someone help me fix it?

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;


namespace ImageZoom
{
    public partial class Form1 : Form
    {

        float zoom = 1;
        Size initSize;
        int ctrlX, ctrlY;

        Panel childControl;

        public Form1()
        {
            InitializeComponent();

            string imagefilename = @"..\..\test.tif";
            Image img = Image.FromFile(imagefilename);

            childControl = new Panel
            {
                BackgroundImage = img,
                BackgroundImageLayout = ImageLayout.Stretch,
                BorderStyle = BorderStyle.FixedSingle,
                Location = new Point(100, 100),
                Size = new Size(200, 200)
            };

            Controls.Add(childControl);

            initSize = childControl.Size;

            ctrlX = childControl.Left;
            ctrlY = childControl.Top;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            float oldzoom = zoom;

            if (e.Delta > 0)
                zoom = Math.Min(zoom + 0.1F, 100F);

            else if (e.Delta < 0)
                zoom = Math.Max(zoom - 0.1F, 0.1F);

            Point currLoc = e.Location;

            int x = currLoc.X;    // Where location of the mouse in the pictureframe
            int y = currLoc.Y;

            int oldimagex = (int)(x / oldzoom);  // Where in the IMAGE is it now
            int oldimagey = (int)(y / oldzoom);

            int newimagex = (int)(x / zoom);     // Where in the IMAGE will it be when the new zoom i made
            int newimagey = (int)(y / zoom);

            ctrlX += newimagex - oldimagex;  // Where to move image to keep focus on one point
            ctrlY += newimagey - oldimagey;

            childControl.Width = (int)(initSize.Width * zoom);
            childControl.Height = (int)(initSize.Height * zoom);

            Point loc = new Point(ctrlX, ctrlY);

            childControl.Location = loc;
        }

    }
}
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace ImageZoom
{
    public partial class Form2 : Form
    {
        Image img;
        int imgx = 0;                         // current offset of image
        int imgy = 0;

        float zoom = 1;
        
        public Form2()
        {

            InitializeComponent();
            string imagefilename = @"..\..\test.tif";
            img = Image.FromFile(imagefilename);

            Paint += new PaintEventHandler(this_Paint);
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            float oldzoom = zoom;

            if (e.Delta > 0)
                zoom = Math.Min(zoom + 0.1F, 100F);

            else if (e.Delta < 0)
                zoom = Math.Max(zoom - 0.1F, 0.1F);

            Point currLoc = e.Location;

            int x = currLoc.X;    // Where location of the mouse in the pictureframe
            int y = currLoc.Y;

            int oldimagex = (int)(x / oldzoom);  // Where in the IMAGE is it now
            int oldimagey = (int)(y / oldzoom);

            int newimagex = (int)(x / zoom);     // Where in the IMAGE will it be when the new zoom i made
            int newimagey = (int)(y / zoom);

            imgx += newimagex - oldimagex;  // Where to move image to keep focus on one point
            imgy += newimagey - oldimagey;

            Invalidate();
        }

        private void this_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            e.Graphics.ScaleTransform(zoom, zoom);
            e.Graphics.DrawImage(img, imgx, imgy);
        }
    }
}

What I want is to zoom that panel according to the mouse location. e.g. Like we zoom a canvas in Paint.NET or Adobe Illustrator etc. The area under the mouse stays there.

inferno_69
  • 16
  • 2
  • [This](https://stackoverflow.com/questions/10614807/zooming-stretching-a-picturebox-on-current-mouse-position) was pretty close but it's zooming in at an extreme speed making it useless. – inferno_69 Mar 06 '23 at 18:27
  • [Zoom and translate an Image from the mouse location](https://stackoverflow.com/a/61964222/7444103) -- Note that the zoom (`zoomFactor`) and all other calculations are applied to a RectangleF shape, an image is drawn inside this *shape* after. You don't actually need to draw an image, just scale and zoom this shape. See the `OffsetScaledRectangleOnMousePosition()` method – Jimi Mar 06 '23 at 18:49
  • @Jimi Thank you so much it's quite close but there's a little flicker while zooming in and out. Any thoughts about that? Also `zoomFactor` is current zoom value and `zoomStep` is increment/decrement value, right? – inferno_69 Mar 06 '23 at 19:11
  • That's correct. You may experience some flickering, using a Panel, because it's not double-buffered. You can build a Custom Control derived from Panel or use Reflection to double-buffer it (or take the simple Custom Control shown here: [Transparent Overlapping Circular Progress Bars (Custom Control)](https://stackoverflow.com/a/53379442/7444103)) – Jimi Mar 06 '23 at 19:55
  • @Jimi Thanks again!! Flickering got reduced by setting size first and then location. (IMO It wasn't the problem of not being double buffered.) My next (hopefully final) question is that it doesn't work as intended if mouse is outside the control (It scales from the existing location). Can it be fixed? – inferno_69 Mar 06 '23 at 20:07
  • The Panel needs to be double-buffered if you scale something inside of it. If you scale the Control itself, then its Container -- Probably, you need to track the Mouse position when it's outside the Control. This can be done in a *managed* way implementing IMessageFilter (so you can receive messages before they're forwarded to the intended target). Something like this: [Handle MouseMove, MouseDown, MouseUp Events in a ListView to drag a borderless Form](https://stackoverflow.com/a/71143588/7444103), also handling `WM_MOUSEWHEEL` / `WM_MOUSEHWHEEL` – Jimi Mar 06 '23 at 21:17
  • Take the example from here: [Detect Mouse Wheel on a button](https://stackoverflow.com/a/61846207/7444103), in case it's needed – Jimi Mar 06 '23 at 21:19
  • The problem is not with the event not getting triggered. I'm overriding from's mouse wheel event. Panel doesn't trigger mouse wheel because it's not focusable unless we made it so. It does zoom when I scroll mouse wheel outside the control bounds but it only increases the width and height of panel. It doesn't change the location. – inferno_69 Mar 07 '23 at 16:14

0 Answers0