Zoom an image in a picture box - c#

I know there are many discussion this topic already but still i could not find an answer. Hope someone have a idea.
I load a bitmap in a PictureBox (SizeMode Normal, DockStyle Fill) and use the Paint event to draw the image.
To zoom I use MouseWheel event and increase / decrease zoom factor (Like _zoomFac += 1). The goal is to zoom at the mouse position. So in the MouseWheel event I also save the mouse position (_imageZoomLocation). This is my code (only little test implementation):
public partial class Form1 : Form
{
private Image _image;
private float _zoomFac;
private PointF _imageZoomLocation;
public Form1()
{
InitializeComponent();
_image = null;
_zoomFac = 1F;
_imageZoomLocation = PointF.Empty;
pictureBox.MouseWheel += new MouseEventHandler(OnMouseWheel);
}
private void pb_Paint(object sender, PaintEventArgs e)
{
if (_image == null)
return;
e.Graphics.TranslateTransform(-_imageZoomLocation.X + _imageZoomLocation.X / _zoomFac, -_imageZoomLocation.Y + _imageZoomLocation.Y / _zoomFac);
e.Graphics.ScaleTransform(_zoomFac, _zoomFac, MatrixOrder.Append);
e.Graphics.DrawImage(_image, new Point(0,0));
}
private void fileToolStripMenuItem_Click(object sender, EventArgs e)
{
... Code for loading image
}
private void OnMouseWheel(object sender, MouseEventArgs e)
{
if (_image == null)
return;
_imageZoomLocation = e.Location;
if (e.Delta > 0)
_zoomFac += 1F;
else
if (_zoomFac - 1F < 1F)
_zoomFac = 1F;
else
_zoomFac -= 1F;
Refresh();
}
private void pb_MouseEnter(object sender, EventArgs e)
{
pictureBox.Focus();
}
}
This works well as long as _imageZoomLocation is not changed, means the mouse is not moved i can zoom in and out perfectly (I can change mouse position when zoom factor is 1 and everything still works). However when I move the mouse to different position in a zoomed state (e.g. zoom factor is 2) and scroll, the image jumpes once to a different position than the mouse is and after this "jump" everything works well again.
Can anyone explain this behaviour?

In the following line, you are dividing imageZoomLocation.X and .Y by the zoom factor only half the time. Instead, maybe you need a separate variable to keep the previous mouse location.
e.Graphics.TranslateTransform(-_imageZoomLocation.X + _imageZoomLocation.X / _zoomFac, _
-_imageZoomLocation.Y + _imageZoomLocation.Y / _zoomFac);

Related

How to fix the delay between mouse wheel up and down on the picture box refresh while scaling a rectangle or a shape on the GraphicsPath object in C#?

1.I'm trying to scale a rectangle while a user does mouse up and mouse down on the picture box.
2.After doing 5 mouse wheel up and if you do mouse wheel down the rectangle still keeps scales up(expands).
3.Any solution to it?
GraphicsPath path=new GraphicsPath();
private float scale=1.0F;
private bool ActiveWheel=false;
public Form1()
{
path.AddRectangle(new Rectangle(10,10,50,100));
}
private void PictureBox1_Paint(object sender,PaintEventArgs e)
{
if(ActiveWheel)
{
ActiveWheel=false;
ScaleRectangle(e);
}
else
{
e.Graphics.DrawPath(Pens.Red,path);
}
}
private void PictureBox1_MouseWheel(object sender,MouseEventArgs e)
{
ActiveWheel=true;
scale=Math.Max(scale+Math.Sign(e.Delta)*0.1F,0.1F);
pictureBox1.Refresh();
}
}
private void ScaleRectangle(PaintEventArgs e)
{
var matrix=new Matrix();
matrix.Scale(scale,scale,MatrixOrder.Append);
path.Transform(matrix);
e.Graphics.DrawPath(Pens.Blue,path);
}
Any solution or idea how to scale down or scale up a shape suddenly without a delay in between mouse wheel ups and mouse wheel downs(see 2. if want to see actually o/p).
Just adjust the scale value in the MouseWheel() event, then ScaleTransform() the GRAPHICS surface (not the Path itself) in the Paint() event and draw:
public partial class Form1 : Form
{
private GraphicsPath path = new GraphicsPath();
private float scale = 1.0F;
public Form1()
{
InitializeComponent();
path.AddRectangle(new Rectangle(10, 10, 50, 100));
pictureBox1.MouseWheel += PictureBox1_MouseWheel;
pictureBox1.Paint += pictureBox1_Paint;
}
private void PictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
scale = Math.Max(scale + Math.Sign(e.Delta) * 0.1F, 0.1F);
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.DrawPath(Pens.Red, path);
}
}
--- EDIT ---
It scales entirely, could you show me how to scale only graphics path
object and top, left have to be fixed, meaning without scaling the
top, left point?
In that case, translate to the top left point of your rectangle, scale, then translate back to the origin. Now draw your UNCHANGED rectangle:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(10, 10);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.TranslateTransform(-10, -10);
e.Graphics.DrawPath(Pens.Red, path);
}
If you have other elements that are being drawn at different positions and/or scales, then you can reset the graphics surface before and after (each "element" can do the same type of thing to position itself and scale itself):
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
// possibly other drawing operations
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(10, 10);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.TranslateTransform(-10, -10);
e.Graphics.DrawPath(Pens.Red, path);
e.Graphics.ResetTransform();
// possibly other drawing operations
}
This approach is nice as it keeps the original information about your rectangle; the change is simply visual.

MouseMove event doesn't fired with TransparencyKey

I'm creating a simple application that draws a horizontal and a vertical line following the mouse.
The form is transparent using TransparencyKey, and the lines are drawn using the Paint event:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Lime);
e.Graphics.DrawLine(pen, 0, py, this.Size.Width, py);
e.Graphics.DrawLine(pen, px, 0, px, this.Size.Height);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
px = e.X; // get cursor X pos
py = e.Y; // get cursor Y pos
Invalidate(); // fire Paint event
}
But the MouseMove event only is fired when the mouse is over the lines drawn. How to make the form catch mouse events when transparent? (Only the mouse move, I want the form still click-through)
As you can read here: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.transparencykey%28v=vs.110%29.aspx - "Any mouse actions, such as the click of the mouse, that are performed on the transparent areas of the form will be transferred to the windows below the transparent area". One way to accomplish your goal is to write your own method, that will check the cursor relative position to the form in a loop, here's what I mean (it worked for me with TransparencyKey, resizing and moving the form):
private void MouseMovedWithTransparency()
{
Point lastCursorPos = new Point(-1, -1); // Starting point.
while (this.Visible)
{
Point currentCursorPos = Cursor.Position;
if (this.ContainsFocus && currentCursorPos.X > this.Left && currentCursorPos.X < this.Left + this.Width &&
currentCursorPos.Y > this.Top && currentCursorPos.Y < this.Top + this.Height)
{
if ((currentCursorPos.X != lastCursorPos.X) || (currentCursorPos.Y != lastCursorPos.Y))
{
// Do actions as in MouseMoved event.
// Save the new position, so it won't be triggered, when user doesn't move the cursor.
lastCursorPos = currentCursorPos;
}
}
Application.DoEvents(); // UI must be kept responsive.
Thread.Sleep(1);
}
}
All you have to do now is to invoke this method from the Shown event - this.Visible has to be true, so it's the easiest way to make it work.

I have a pictureBox1_MouseDown event and when i click the mouse left button it dosent work good

The idea is that in my program i have a button. When i click the button its drawing in the picutreBox1 paint event the point and add it on the pictureBox1 center + 10 pixels randomly around the center.
For example I clicked 5 times in a row and I see now 5 points.
In the button1 click event I also add the button location X,Y to two Lists<>
List<> X have the X location of the button and Y the Y location.
Now in the mouse down event I'm calculating the nearest point to the mouse location when I click the pictureBox1 and I also check if the lowest value distance is lower then 50 so if I click on a point ill be able to drag the point around the pictureBox1 but only if I clicked on the point somewhere between the point center and 5 pixels from the center. That way I know which point I want to drag.
In the mouse down event I also get the index of the lowest value distance and I set a flag movePoint to true and then in the mouse move event I update all the time the selectedIndex so only the selected clicked point will move. in the mouse move event I also check if the flag movePoint is true and then only start the movement.
In the mouse up I update once again the selectedIndex with the mouse location.
And I also set the flag movePoint to false.
The problem is when I'm running the program add one or more points but i don't click with the mouse on a point I click on EMPTY SPACE somewhere in the pictureBox1 and just click on it once or click and try to drag this empty space nothing happens since the flag movePoint is false.
** But in fact for some reason something does happen and if i click and drag empty space in the pictureBox1 area and then i click on one of the points and try to drag them the point jump to the location of where i dragged the empty space before !
I cant figure out why it happen. **
Its like I drag nothing its empty space i also don't see any point move. But then when I try to drag a point its jumping or another point is jumping to the location of where I ended dragging the empty space !!!!
This is the code of the mouse down mouse move mouse up and the paint events:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// find the index that is closest to the current mouse location
float MinDist = float.MaxValue;
for (int idx = 0; idx < Point_X.Count; ++idx)
{
float dx = Point_X[idx] - e.X;
float dy = Point_Y[idx] - e.Y;
float dist = (float)Math.Sqrt(dx * dx + dy * dy);
if (dist < MinDist)
{
MinDist = dist;
selectedIndex = idx;
}
}
if (MinDist < 5)
{
mouseMove = true;
OriginalX = Point_X[(int)selectedIndex];
OriginalY = Point_Y[(int)selectedIndex];
}
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (mouseMove == true)
{
Point NewPoint = e.Location;
Point_X[(int)selectedIndex] = NewPoint.X;
Point_Y[(int)selectedIndex] = NewPoint.Y;
pictureBox1.Refresh();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Point NewPoint = e.Location;
Point_X[(int)selectedIndex] = NewPoint.X;
Point_Y[(int)selectedIndex] = NewPoint.Y;
mouseMove = false;
}
private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
SolidBrush brush = new SolidBrush(Color.Red);
for (int idx = 0; idx < Point_X.Count; ++idx)
{
Point dPoint = new Point((int)Point_X[idx], (int)Point_Y[idx]);
dPoint.X = dPoint.X - 5; // was - 2
dPoint.Y = dPoint.Y - 5; // was - 2
Rectangle rect = new Rectangle(dPoint, new Size(10, 10));
g.FillEllipse(brush, rect);
}
}
** I made a small video yesterday that show the problem. look from the beginning but the problem start at second 17-19 thats where i click and drag empty space in the pictureBox1 and then try to drag the points again and cant move them and if i cant move one of them the other points jump to the location of the empty space i dragged.
http://www.youtube.com/watch?v=qZr6wdF8MNA&feature=youtu.be **
The method private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
Should contain the logic
if (mouseMove == true)
{
//Do stuff
}
That should fix the issue.

How to move PictureBox in C#?

i have used this code to move picture box on the pictureBox_MouseMove event
pictureBox.Location = new System.Drawing.Point(e.Location);
but when i try to execute the picture box flickers and the exact position cannot be identified. can you guys help me with it. I want the picture box to be steady...
You want to move the control by the amount that the mouse moved:
Point mousePos;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) {
mousePos = e.Location;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
int dx = e.X - mousePos.X;
int dy = e.Y - mousePos.Y;
pictureBox1.Location = new Point(pictureBox1.Left + dx, pictureBox1.Top + dy);
}
}
Note that this code does not update the mousePos variable in MouseMove. Necessary since moving the control changes the relative position of the mouse cursor.
You have to do several things
Register the start of the moving operation in MouseDown and remember the start location of the mouse.
In MouseMove see if you are actually moving the picture. Move by keeping the same offset to the upper left corner of the picture box, i.e. while moving, the mouse pointer should always point to the same point inside the picture box. This makes the picture box move together with the mouse pointer.
Register the end of the moving operation in MouseUp.
private bool _moving;
private Point _startLocation;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
_moving = true;
_startLocation = e.Location;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
_moving = false;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (_moving) {
pictureBox1.Left += e.Location.X - _startLocation.X;
pictureBox1.Top += e.Location.Y - _startLocation.Y;
}
}
Try to change SizeMode property from AutoSize to Normal

Mouse Cursor position changes

Hi I have a windows form application which i want to move my mouse then dragdrop will function but i have tried using mousemove mouse event to do it , but seems like dragdrop is very sensitive.So what i'm asking is whether is it possible to detect if the mouse cursor moves at least a certain distance away from the current cursor then it does the dragdrop code.
I understand you have a drag & drop code you want to execute only if the mouse has moved a certain distance. If so:
You can hook to the mouse move event once the user has performed the initial action (mouse down on the item to drag ?). Then compare the mouse coordinates on the mouse move event and trigger the "dragdrop code" once the coordinates difference is higher then the arbitrary value you set.
private int difference = 10;
private int xPosition;
private int yPosition;
private void item_MouseDown(object sender, MouseEventArgs e)
{
this.MouseMove += new MouseEventHandler(Form_MouseMove);
xPosition = e.X;
yPosition = e.Y;
}
private void Form_MouseMove(object sender, MouseEventArgs e)
{
if (e.X < xPosition - difference
|| e.X > xPosition + difference
|| e.Y < yPosition - difference
|| e.Y > yPosition + difference)
{
//Execute "dragdrop" code
this.MouseMove -= Form_MouseMove;
}
}
This would execute dragdrop when the cursor moves out of a virtual 10x10 square.
I do not really get your question and the effect you are trying to acheive.
BTW, if my interpretation is correct, you oare trying to do something only if the "drag distance" is grather than a certain amount.
this code below do not use the drag&drop event, but the mouseup, mousedown and mousemove events.
It keeps track of the distance the mouse travels while the left button is pressed.
When the distance is grater than a fixed amount, it changes the mouseCursor(during the dragaction)
When the mouseButton is released, if the dstance traveled is greater than the minimum offset, we perform our fake drop action.
Create a new windows form project and substitute the Form1 autogenerated Class with the one below
Hope this helps.
public partial class Form1 : Form
{
private bool isDragging; //We use this to keep track that we are FakeDragging
private Point startPosition; //The start position of our "Fake" dragging action
private double dragDistance = 0; //The distance(absolute) from the drag action starting point
private const int MINIMUM_DRAG_DISTANCE = 100; //minimum FakeDrag distance.
private Label label1 = new Label();
public Form1()
{
#region Support form generation code
InitializeComponent();
this.label1 = new System.Windows.Forms.Label();
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(54, 242);
this.label1.Name = "Distance:";
this.label1.Size = new System.Drawing.Size(35, 13);
this.Controls.Add(label1);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseUp);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);
#endregion
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
//if the mouse button is pressed then
//there's the chanche a dragAction
//is being performed and we take note
//of the position of the click
//(we will use this later on the mouseMove event
//to calculate the distance mouse has traveled
if (e.Button.Equals(MouseButtons.Left))
startPosition = e.Location;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
///at the mouse up event we can execute code that does something
if (isDragging && dragDistance > MINIMUM_DRAG_DISTANCE)
{
MessageBox.Show("we have fakedragged!\nDo something useful here");
//Do your Drag & Drop code here.
}
///but WE MUST signal our system that the Fake Drag has ended
///and reset our support variables.
this.Cursor = Cursors.Default;
isDragging = false;
dragDistance = 0;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
///when the mouse moves we control if the mouse button is still pressed.
///if it is so, we surely are FakeDragging and we set our boolean to true
///(it will be useful in the mouseUP event).
///Then we calculate the absolute distance the mouse has traveld since the
///user has pressed the left mouseButton.
Point currentPosition = e.Location;
if (e.Button.Equals(MouseButtons.Left))
{
isDragging = true;
dragDistance =
Math.Abs(
Math.Sqrt(
(
Math.Pow(
(currentPosition.X - startPosition.X), 2)
+
Math.Pow(
(currentPosition.Y - startPosition.Y), 2)
)));
}
//we set the label text displaying the distance we just calculated
label1.Text =
String.Format(
"Distance: {0}",
dragDistance.ToString());
Application.DoEvents();
///At last, if the distance il greater than our offset, we change the
///mouse cursor(this is not required in a real case scenario)
if (dragDistance > MINIMUM_DRAG_DISTANCE)
this.Cursor = Cursors.Hand;
else
this.Cursor = Cursors.Default;
}
}

Categories

Resources