I'm trying to implement a simple "zoom to point" functionality in WinForms. When the mouse is at the same point and the mouse wheel is scrolled in/out, it works great. When the mouse position is changed between scrolls, it jumps to that position and is very wonky. Here's the code for the control that you can add to a Form
for testing:
public class Canvas : Control
{
private Bitmap Image;
private TextureBrush ImageBrush;
private Point Origin;
private Matrix TransformMatrix;
public float ZoomScale
{
get;
set;
}
public Canvas()
{
this.SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true);
this.Image = // Load your picture here.
this.ImageBrush = new TextureBrush(this.Image, WrapMode.Clamp);
this.ZoomScale = 1.0f;
this.TransformMatrix = new Matrix();
}
protected override void OnPaint(PaintEventArgs e)
{
float zs = this.ZoomScale;
var matrix = this.TransformMatrix.Clone();
e.Graphics.Transform = matrix;
var c = e.ClipRectangle;
// Transform the clip rectangle, is this even right?
var x = (int)Math.Round(c.X / zs - matrix.OffsetX / zs);
var y = (int)Math.Round(c.Y / zs - matrix.OffsetY / zs);
var w = (int)Math.Round(c.Width / zs);
var h = (int)Math.Round(c.Height / zs);
// Draw the image scaled and translated.
e.Graphics.FillRectangle(this.ImageBrush, x, y, w - 1, h - 1);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
if (e.Delta > 0)
{
this.ZoomScale += 0.1f;
}
else
{
this.ZoomScale -= 0.1f;
}
this.ZoomToPoint(e.Location);
this.Refresh();
}
private void ZoomToPoint(Point origin)
{
this.Origin = origin;
float zoomScale = this.ZoomScale;
// The important part.
var matrix = new Matrix(1, 0, 0, 1, 0, 0);
matrix.Reset();
matrix.Translate(-origin.X, -origin.Y, MatrixOrder.Append);
matrix.Scale(zoomScale, zoomScale, MatrixOrder.Append);
matrix.Translate(origin.X, origin.Y, MatrixOrder.Append);
this.TransformMatrix = matrix;
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.ZoomToPoint(this.Origin);
}
}
So the important part is obviously the affine transformation. I know it's in Adobe Flex, but I got the idea of Translate
, Scale
, Translate
from this website (great example of what I'm trying to do). I didn't realize that MatrixOrder.Append
was the key in .NET
until I tried. But the problem still remains. Try loading the control in a WinForms application and you'll see what I mean. Does anyone know what the issue is with this?
EDIT: The reason I need to manually calculate the rectangle is because I'm going to be drawing more than just an image. It's a canvas that performs like Visio. I'll also need a method for transforming a specific Point
.
EDIT 2: I understand that I can use Invert()
on the Matrix
then TransformPoints()
to get a proper transformed point but it still doesn't solve the problem of the wonky mouse movement. I was thinking of calculating the Origin
from the location of the top corner of the image but it didn't work.