I'm trying to create a simple Window that allows viewing/zooming/moving an image. It is starting to behave similar to what it should with the image resizing to adjust to the window as we resize.
The weird thing is that as you reduce the width of the window, at a certain point, the height of the picture starts reducing when it shouldn't, and then it bounces into a different position. In the code, however, the height of the image remains the same so "something" else is altering the layout.
The other weird thing is that as you increase the width, the image almost remains centered except that it gradually tilts towards the right.
So my question is: what is screwing up my layout like this?
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NaturalGroundingPlayer.ImageViewerWindow"
x:Name="Window" Title="Image Viewer" Width="640" Height="480" SizeChanged="Window_SizeChanged">
<Grid x:Name="LayoutRoot">
<Canvas x:Name="ImgCanvas" ClipToBounds="True">
<ContentControl x:Name="ImgContentCtrl" Height="300" Width="400">
<Grid x:Name="ImgGrid">
<Image x:Name="ImgObject"/>
<Thumb x:Name="ImgThumb" Opacity="0" DragDelta="ImgThumb_DragDelta" MouseWheel="ImgThumb_MouseWheel"/>
</Grid>
</ContentControl>
</Canvas>
</Grid>
</Window>
public partial class ImageViewerWindow : Window {
public static ImageViewerWindow Instance(string fileName) {
ImageViewerWindow NewForm = new ImageViewerWindow();
NewForm.LoadImage(fileName);
NewForm.Show();
return NewForm;
}
private double scale;
public ImageViewerWindow() {
InitializeComponent();
}
public void LoadImage(string fileName) {
BitmapImage NewImage = new BitmapImage();
NewImage.BeginInit();
NewImage.UriSource = new Uri(fileName);
NewImage.EndInit();
ImgObject.Source = NewImage;
//ImgContentCtrl.Width = NewImage.Width;
//ImgContentCtrl.Height = NewImage.Height;
BestFit();
}
private void ImgThumb_DragDelta(object sender, DragDeltaEventArgs e) {
double ImgLeft = Canvas.GetLeft(ImgContentCtrl);
double ImgTop = Canvas.GetTop(ImgContentCtrl);
Canvas.SetLeft(ImgContentCtrl, (ImgLeft + e.HorizontalChange));
Canvas.SetTop(ImgContentCtrl, (ImgTop + e.VerticalChange));
}
private void ImgThumb_MouseWheel(object sender, MouseWheelEventArgs e) {
// Zoom in when the user scrolls the mouse wheel up and vice versa.
if (e.Delta > 0) {
// Limit zoom-in to 500%
if (scale < 5)
scale += 0.1;
} else {
// When mouse wheel is scrolled down...
// Limit zoom-out to 80%
if (scale > 0.8)
scale -= 0.1;
}
DisplayImage();
}
private void BestFit() {
// Set the scale of the ContentControl to 100%.
scale = 1;
// Set the position of the ContentControl so that the image is centered.
Canvas.SetLeft(ImgContentCtrl, 0);
Canvas.SetTop(ImgContentCtrl, 0);
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e) {
DisplayImage();
}
private void DisplayImage() {
double RatioWidth = LayoutRoot.ActualWidth / ImgObject.Source.Width;
double RatioHeight = LayoutRoot.ActualHeight / ImgObject.Source.Height;
if (RatioHeight > RatioWidth) {
ImgContentCtrl.Width = LayoutRoot.ActualWidth * scale;
ImgContentCtrl.Height = LayoutRoot.ActualHeight * RatioHeight * scale;
} else {
ImgContentCtrl.Height = LayoutRoot.ActualHeight * scale;
ImgContentCtrl.Width = LayoutRoot.ActualWidth * RatioWidth * scale;
}
}
}
Sjips, you first need to put any picture in it to display.
dkozl, that works indeed, I wasn't sure the Image control would respect proportions automatically as the demo I found wasn't respecting proportions while resizing. Simply putting this code in DisplayImage works perfect, even for repositioning properly when zooming. I guess I was much closer to the answer than I thought!
private void DisplayImage() {
ImgContentCtrl.Width = LayoutRoot.ActualWidth * scale;
Canvas.SetLeft(ImgContentCtrl, LayoutRoot.ActualWidth * (1 - scale) / 2);
ImgContentCtrl.Height = LayoutRoot.ActualHeight * scale;
Canvas.SetTop(ImgContentCtrl, LayoutRoot.ActualHeight * (1 - scale) / 2);
}
That being said, I still don't undestand the weird behaviors that happened before...
Related
I have WPF window with a fixed size when showing it on other screen with lower resolution its height become out of desktop area.
I came up with this way in order to get around this issue: Is this a good way to achieve that or there is a better one?
Window.xaml
Loaded="Window_Loaded" ResizeMode="CanMinimize" Width="1250" Height="750" WindowStyle="None" WindowStartupLocation="CenterScreen">
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<ScaleTransform x:Name="GridTrans"/>
</Grid.LayoutTransform>
<!--Content-->
</Grid>
Window.xaml.cs
public MyWindow()
{
InitializeComponent();
double OldHeight = Height;
double OldWidth = Width;
double AspectRatio = Width / Height;
Height = SystemParameters.PrimaryScreenHeight * 0.87;
Width = Height * AspectRatio;
GridTrans.ScaleY = Height / OldHeight;
GridTrans.ScaleX = Width / OldWidth;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Calculate the Ratio (Calculated Once) => 0.87
MessageBox.Show((ActualHeight / SystemParameters.PrimaryScreenHeight).ToString());
}
I am currently having trouble when rotating an image in WPF, using RotateTransform and LayoutTransform. When an image, that has a pixel height size greater than the monitors height and is rotated at 90º or 270º, the window size will be higher than the monitors screen resolution size.
Example screenshots:
Application running with image at 90º
Application running with image at 0º
I am using the code below (simplified), with mainWindow.img being a System.Windows.Control.Image:
static void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = degrees };
mainWindow.img.LayoutTransform = rt;
}
It is for a a picture viewer project, the full source code is available at https://github.com/Ruben2776/PicView
I have tried shifting the Width and Height values of the image, but it produces an undesired result (skewed proportion).
The sizing calculation, for the image size, is made based on the user's screen height, using the following trimmed code:
int interfaceHeight = 90;
double maxWidth = Math.Min(MonitorInfo.Width, width);
double maxHeight = Math.Min((MonitorInfo.Height - interfaceHeight), height);
double AspectRatio = Math.Min((maxWidth / width), (maxHeight / height));
mainWindow.img.Width = (width * AspectRatio);
mainWindow.img.Height = (height * AspectRatio);
with height and width being the image's dimensions and MonitorInfo being a class that retrieves the current monitors resolution.
Update
Below is the minimal code for a sample WPF app illustrating the issue:
MainWindow.xaml
<Window x:Class="RotateTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow" >
<Grid>
<Image x:Name="img" Stretch="Fill" Source="https://w.wallhaven.cc/full/nk/wallhaven-nkrwz1.jpg"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace RotateTest
{
public partial class MainWindow : Window
{
int Degrees;
public MainWindow()
{
InitializeComponent();
ContentRendered += MainWindow_ContentRendered;
KeyDown += MainWindow_KeyDown;
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
Rotate(true);
break;
case Key.Down:
Rotate(false);
break;
}
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
int interfaceHeight = 90;
double maxWidth = Math.Min(SystemParameters.PrimaryScreenWidth, img.Source.Width);
double maxHeight = Math.Min(SystemParameters.PrimaryScreenHeight - interfaceHeight, img.Source.Height);
double AspectRatio = Math.Min((maxWidth / img.Source.Width), (maxHeight / img.Source.Height));
img.Width = (img.Source.Width * AspectRatio);
img.Height = (img.Source.Height * AspectRatio);
}
void Rotate(int degrees)
{
var rt = new RotateTransform { Angle = Degrees = degrees };
img.LayoutTransform = rt;
}
void Rotate(bool right)
{
switch (Degrees)
{
case 0:
if (right)
{
Rotate(270);
}
else
{
Rotate(90);
}
break;
case 90:
if (right)
{
Rotate(0);
}
else
{
Rotate(180);
}
break;
case 180:
if (right)
{
Rotate(90);
}
else
{
Rotate(270);
}
break;
case 270:
if (right)
{
Rotate(180);
}
else
{
Rotate(0);
}
break;
}
}
}
}
The root of the problem is that you are only calculating your scaling ratios based on the the image's size when it's not rotated. Once you rotate the image, img.ActualHeight effectively becomes its width and img.ActualWidth effectively becomes its height, and your calculation from when the image was un-rotated is no longer correct.
Here are the changes and additions I made to your code:
private double normalRatio;
private double rotatedRatio;
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
double interfaceHeight = this.ActualHeight - img.ActualHeight;
normalRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Width, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Height);
rotatedRatio = Math.Min(SystemParameters.WorkArea.Width / img.Source.Height, (SystemParameters.WorkArea.Height - interfaceHeight) / img.Source.Width);
ScaleImage();
}
private void ScaleImage()
{
double ratio = Degrees == 0 || Degrees == 180 ? normalRatio : rotatedRatio;
img.Width = (img.Source.Width * ratio);
img.Height = (img.Source.Height * ratio);
}
void Rotate(bool right)
{
if (right)
{
Degrees -= 90;
if (Degrees < 0) { Degrees += 360; }
}
else
{
Degrees += 90;
if (Degrees >= 360) { Degrees -= 360; }
}
ScaleImage();
Rotate(Degrees);
}
//I left the other methods, including Rotate(int degrees), the same as in your question
Here's an explanation of what I changed:
interfaceHeight is calculated by subtracting the height of the image from the height of the window, the difference being the aggrigate size of everything else.
Instead of using MonitorInfo, I'm using SystemParameters.WorkArea, because it takes into account the size and placement of the Windows taskbar.
I calculate two scale ratios: normalRatio, for when the image is not rotated or is vertically flipped (180°), and rotatedRatio, for when the image is rotated 90° in either direction. I calculate the later by swapping img.Source.Height and img.Source.Width.
I added a ScaleImage() method to do the actual image scaling based on the intended rotation, so I can call it from two different places.
I simplified Rotate(bool right) to calculate the new angle using math, instead of listing out each possible rotation.
The above results in an image that is always as big as possible for the screen while maintaining the original aspect ratio. It will grow and shrink as it's rotated to fit the screen. If you want the image to stay a constant size instead, just use Math.Min(normalRatio, rotatedRatio).
Note that the above only works if you call Rotate(bool right), not if you call Rotate(int degrees) directly. This is because the logic of using two ratios only works because there are only two possible sizes for the image (portrait and landscape), which is only the case if you restrict the rotation to increments of 90°. If you want to set the angle to something else, like 20°, the math to calculate the image's effective size becomes a bit more complicated and you would need to start calculating it dynamically based on the angle.
How do get a grid to rotate on mouse drag as it shows in the gif in WPF c#?
I've tried this code but I'm getting a very bad result and I do not have any idea how should I do the sizing.
double angle = 30;
Point oldPoint;
Point newPoint;
int d;
RotateTransform transe = new RotateTransform();
private void Border_MouseMove(object sender, MouseEventArgs e)
{
Grid b = sender as Grid;
if (Mouse.Captured == b)
{
newPoint = Mouse.GetPosition(mu);
if (oldPoint.Y != newPoint.Y)
{
if (oldPoint.Y > newPoint.Y)
transe.Angle = (oldPoint.Y - newPoint.Y);
else
transe.Angle += (newPoint.Y - oldPoint.Y);
gt.RenderTransform = transe;
}
return;
}
}
private void Border_MouseLeftButtonDown_2(object sender, MouseButtonEventArgs e)
{
Grid b = sender as Grid;
if (Mouse.Captured != b)
{
oldPoint = Mouse.GetPosition(mu);
Mouse.Capture(b);
}
}
private void Border_MouseLeftButtonUp_1(object sender, MouseButtonEventArgs e)
{
oldPoint = new Point(0, 0);
newPoint = new Point(0, 0);
Mouse.Capture(null);
}
The contol in xaml:
<Grid x:Name="mu" Height="90" Width="128" MouseUp="mu_MouseUp" MouseMove="Drase_MouseMove"
Canvas.Left="30" Canvas.Top="45" Cursor="Arrow" Background="White">
<Grid x:Name="gt" HorizontalAlignment="Left" Height="6" VerticalAlignment="Center"
Background="Black" Width="{Binding ActualWidth, ElementName=mu, Mode=OneWay}"
MouseMove="Border_MouseMove" MouseLeftButtonDown="Border_MouseLeftButtonDown_2"
MouseLeftButtonUp="Border_MouseLeftButtonUp_1" RenderTransformOrigin="0.5,0.5"/>
</Grid>
This is what I have managed to get so far. As you can see it's rotating uncontrollably.
Your approach isn't quite right. Here's my working version of the code:
private void Border_MouseMove(object sender, MouseEventArgs e)
{
Grid b = sender as Grid;
if (Mouse.Captured == b)
{
Point origin = new Point(mu.ActualWidth / 2, mu.ActualHeight / 2);
var rawPoint = Mouse.GetPosition(mu);
var transPoint = new Point(rawPoint.X - origin.X, rawPoint.Y - origin.Y);
var radians = Math.Atan2(transPoint.Y, transPoint.X);
var angle = radians * (180 / Math.PI);
transe.Angle = angle;
gt.RenderTransform = transe;
}
}
I'm not the best when it comes to the math, but I'll try to what I'm doing. The idea behind my code is that the angle of the rotate transform should always match the angle of the mouse relative to the vertical center-line of the mu.
Point origin = new Point(mu.ActualWidth / 2, mu.ActualHeight / 2); gives me the center of the mu, which is the mathematical origin point for the calculations.
var transPoint = new Point(rawPoint.X - origin.X, rawPoint.Y - origin.Y); translates the location of the mouse cursor so that it's relative to the center of mu (i.e. origin), as opposed to the top-left corner.
Then, stealing a bit of code I don't quite understand from this answer, I use Atan2 to calculate the angle.
I’m new to windows phone development and trying to use the Microsoft.Phone.Controls.Toolkit for zooming and panning on an image. My issue is that zooming and panning only work on the first try. If I zoom/pan on the image, go elsewhere within the app and then go back to the image it doesn’t do anything. The OnPinchStarted method never runs when it is supposed to be envoked. I’ve used several different ways that I’ve found from searching here and elsewhere for the gesturelistener. Code is posted below. Before I go another route and toss out this way I wanted to see if there is something I’m missing with what I have because it works just fine the first time.
First try:
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Image x:Name="Map"
Source="Images/park.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener
PinchStarted="OnPinchStarted"
PinchDelta="OnPinchDelta"
DragDelta="OnDragDelta"/>
</toolkit:GestureService.GestureListener>
<Image.RenderTransform>
<CompositeTransform
ScaleX="1" ScaleY="1"
TranslateX="0" TranslateY="0"/>
</Image.RenderTransform>
</Image>
</Grid>
CS:
// these two fields fully define the zoom state:
private double TotalImageScale = 1d;
private Point ImagePosition = new Point(0, 0);
private const double MAX_IMAGE_ZOOM = 5;
private Point _oldFinger1;
private Point _oldFinger2;
private double _oldScaleFactor;
// Initializes the zooming operation
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_oldFinger1 = e.GetPosition(Map, 0);
_oldFinger2 = e.GetPosition(Map, 1);
_oldScaleFactor = 1;
}
//Computes the scaling and translation to correctly zoom around your fingers.
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
var scaleFactor = e.DistanceRatio / _oldScaleFactor;
if (!IsScaleValid(scaleFactor))
return;
var currentFinger1 = e.GetPosition(Map, 0);
var currentFinger2 = e.GetPosition(Map, 1);
var translationDelta = GetTranslationDelta(
currentFinger1,
currentFinger2,
_oldFinger1,
_oldFinger2,
ImagePosition,
scaleFactor);
_oldFinger1 = currentFinger1;
_oldFinger2 = currentFinger2;
_oldScaleFactor = e.DistanceRatio;
UpdateImageScale(scaleFactor);
UpdateImagePosition(translationDelta);
}
//Moves the image around following your finger.
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
var translationDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (IsDragValid(1, translationDelta))
UpdateImagePosition(translationDelta);
}
//Computes the translation needed to keep the image centered between your fingers.
private Point GetTranslationDelta(
Point currentFinger1, Point currentFinger2,
Point oldFinger1, Point oldFinger2,
Point currentPosition, double scaleFactor)
{
var newPos1 = new Point(
currentFinger1.X + (currentPosition.X - oldFinger1.X) * scaleFactor,
currentFinger1.Y + (currentPosition.Y - oldFinger1.Y) * scaleFactor);
var newPos2 = new Point(
currentFinger2.X + (currentPosition.X - oldFinger2.X) * scaleFactor,
currentFinger2.Y + (currentPosition.Y - oldFinger2.Y) * scaleFactor);
var newPos = new Point(
(newPos1.X + newPos2.X) / 2,
(newPos1.Y + newPos2.Y) / 2);
return new Point(
newPos.X - currentPosition.X,
newPos.Y - currentPosition.Y);
}
//Updates the scaling factor by multiplying the delta.
private void UpdateImageScale(double scaleFactor)
{
TotalImageScale *= scaleFactor;
ApplyScale();
}
//Applies the computed scale to the image control.
private void ApplyScale()
{
((CompositeTransform)Map.RenderTransform).ScaleX = TotalImageScale;
((CompositeTransform)Map.RenderTransform).ScaleY = TotalImageScale;
}
//Updates the image position by applying the delta.
//Checks that the image does not leave empty space around its edges.
private void UpdateImagePosition(Point delta)
{
var newPosition = new Point(ImagePosition.X + delta.X, ImagePosition.Y + delta.Y);
if (newPosition.X > 0) newPosition.X = 0;
if (newPosition.Y > 0) newPosition.Y = 0;
if ((Map.ActualWidth * TotalImageScale) + newPosition.X < Map.ActualWidth)
newPosition.X = Map.ActualWidth - (Map.ActualWidth * TotalImageScale);
if ((Map.ActualHeight * TotalImageScale) + newPosition.Y < Map.ActualHeight)
newPosition.Y = Map.ActualHeight - (Map.ActualHeight * TotalImageScale);
ImagePosition = newPosition;
ApplyPosition();
}
//Applies the computed position to the image control.
private void ApplyPosition()
{
((CompositeTransform)Map.RenderTransform).TranslateX = ImagePosition.X;
((CompositeTransform)Map.RenderTransform).TranslateY = ImagePosition.Y;
}
//Resets the zoom to its original scale and position
private void ResetImagePosition()
{
TotalImageScale = 1;
ImagePosition = new Point(0, 0);
ApplyScale();
ApplyPosition();
}
//Checks that dragging by the given amount won't result in empty space around the image
private bool IsDragValid(double scaleDelta, Point translateDelta)
{
if (ImagePosition.X + translateDelta.X > 0 || ImagePosition.Y + translateDelta.Y > 0)
return false;
if ((Map.ActualWidth * TotalImageScale * scaleDelta) + (ImagePosition.X + translateDelta.X) < Map.ActualWidth)
return false;
if ((Map.ActualHeight * TotalImageScale * scaleDelta) + (ImagePosition.Y + translateDelta.Y) < Map.ActualHeight)
return false;
return true;
}
//Tells if the scaling is inside the desired range
private bool IsScaleValid(double scaleDelta)
{
return (TotalImageScale * scaleDelta >= 1) && (TotalImageScale * scaleDelta <= MAX_IMAGE_ZOOM);
}
Second Try(from http://www.wintellect.com/blogs/jprosise/building-touch-interfaces-for-windows-phones-part-4)
XAML:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid x:Name="ContentPanel" Grid.Row="1" RenderTransformOrigin="0.5,0.5" >
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener PinchDelta="OnPinchDelta"
PinchStarted="OnPinchStarted" DragDelta="OnDragDelta" />
</toolkit:GestureService.GestureListener>
<Grid.RenderTransform>
<CompositeTransform x:Name="HambyTransform" />
</Grid.RenderTransform>
<Image Source="Images/trails.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stretch="Uniform" />
</Grid>
</Grid>
CS:
private double _cx, _cy;
private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e)
{
_cx = HambyTransform.ScaleX;
_cy = HambyTransform.ScaleY;
}
//scale the map
private void OnPinchDelta(object sender, PinchGestureEventArgs e)
{
//compute the new scaling factors
double cx = _cx * e.DistanceRatio;
double cy = _cy * e.DistanceRatio;
// If they're between 1.0 and 4.0, inclusive, apply them
if (cx >= 1.0 && cx <= 4.0 && cy >= 1.0 && cy <= 4.0)
{
HambyTransform.ScaleX = cx;
HambyTransform.ScaleY = cy;
}
}
private void OnDragDelta(object sender, DragDeltaGestureEventArgs e)
{
HambyTransform.TranslateX += e.HorizontalChange;
HambyTransform.TranslateY += e.VerticalChange;
}
After many attempts to find a fix, which I never did, I started all over with a new project and the issue went away. Zoom/pan works fine. I don't know what happened or if something internal in my project was corrupt, but its working now. I hope this helps anybody who runs into the same issue. I had to do alot of copying/pasting code, but it worked.
I have a userControl library, which consists of the main Panel and a PictureBox, I want to make a zoomable PictureBox tool, I zoom in and out using mouseWheel event of the main Panel, the problem that I can't figure out how do I zoom in by the mouse position on the image, so whenever I zoom in, the zoom goes the Top-Left corner of the panel, so how do I fix that?
private double ZOOMFACTOR = 1.15; // = 15% smaller or larger
private int MINMAX = 5;
void picPanel_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
{
ZoomIn();
}
else
{
ZoomOut();
}
}
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
}
}
private void picBox_MouseEnter(object sender, EventArgs e)
{
if (picBox.Focused) return;
picBox.Focus();
}
Update :
I have tried this, it looks like working, but not exactly as it should be!! Any ideas?
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
Point p = this.AutoScrollPosition;
int deltaX = e.X - p.X;
int deltaY = e.Y - p.Y;
this.AutoScrollPosition = new Point(deltaX, deltaY);
}
}
This is the example of Zoom image on mouse position....
tested verified.
protected override void OnMouseWheel(MouseEventArgs ea)
{
// flag = 1;
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (picmap1.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (ea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width < (15 * this.Width)) && (picmap1.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width * 1.25);
picmap1.Height = (int)(picmap1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 1.25 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 1.25 * (ea.X - picmap1.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width > (imagemappan.Width)) && (picmap1.Height > (imagemappan.Height)))
{
// Change the size of the picturebox, divide it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width / 1.25);
picmap1.Height = (int)(picmap1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 0.80 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 0.80 * (ea.X - picmap1.Left));
}
}
}
}
The problem is that your control is acting like a viewport - the origin is top left, so every time you stretch the image you're doing it from that corner - the upshot is you wind up zooming into the top left corner, you need to offset the stretched image and centre the point the user zoomed in on.
image size: 200,200
user clicks 100,50 and zooms in x2
stretch the image
image size 400,400, and the place the user clicked is now effectively at 200,100
you need to slide the image 100 px left and 50 px up to correct for re-sizing the image
You'll need to override the paint event handler to draw the image offset:
RectangleF BmpRect = new RectangleF((float)(Offset.X), (float)(Offset.Y), (float)(ZoomedWidth), (float)(ZoomedHeight));
e.Graphics.DrawImage(Bmp, ViewPort , BmpRect, GraphicsUnit.Pixel);
Bmp is your image; ViewPort is a Rectangle defined by your pictureBox control
Here is a thread that might help.