Move a PictureBox with mouse - c#

I'm developing an app for windows mobile (Compact Framework 2.0). It has a WinForms with a PictureBox.
I want to move the image of the PictureBox but I don't know how to do it so I choose to move the hole PictureBox.
To do it I use this event:
private void imagenMapa_MouseMove(object sender, MouseEventArgs e)
{
imagenMapa.Left = e.X;
imagenMapa.Top = e.Y;
this.Refresh();
}
But when I move the PictureBox it blinks and moves every where.
What I'm doing wrong?

Actual Code (Requires .NET Framework 3.5 and beyond, not sure if this is available in the Compact Framework)...
// Global Variables
private int _xPos;
private int _yPos;
private bool _dragging;
// Register mouse events
pictureBox.MouseUp += (sender, args) =>
{
var c = sender as PictureBox;
if (null == c) return;
_dragging = false;
};
pictureBox.MouseDown += (sender, args) =>
{
if (args.Button != MouseButtons.Left) return;
_dragging = true;
_xPos = args.X;
_yPos = args.Y;
};
pictureBox.MouseMove += (sender, args) =>
{
var c = sender as PictureBox;
if (!_dragging || null == c) return;
c.Top = args.Y + c.Top - _yPos;
c.Left = args.X + c.Left - _xPos;
};

The e.X and e.Y are relative to the picture box (e.g. if the mouse is in the upper left of the picture box, that's 0,0) .
The values for imagenMapa.Left and imagenMapa.Top are relative to the form (or whatever control contains imagenMapa)
If you try to mix values from these two systems without conversion, you're going to get jumps (like you're seeing).
You're probably better off converting the mouse position to the same coordinate system used by the thing that contains the picture box.
You could use imagenMapa.PointToScreen to get the mouse coordinates in screen coordinates (or Cursor.Position to get the position directly), and yourForm.PointToClient to get them back in the form coordinates.
Note that depending on your needs, you could accomplish "moving an image within a control" by overriding/handling the Paint event of a control and drawing the image yourself. If you did this, you could keep everything in the picturebox coordinates, since those are likely what you would use when you called graphicsObject.DrawImage.

e.X & e.Y is in the coordinate space of the pictureBox, imagenMapa.Left & imagenMapa.Top is in the coordinate space of the Form. :-)

Also don't forget to set your form to double buffered, that might help with the flickering, but for the actual positioning of it, I like Daniel L's suggestion

Embrace math!
control.Left = control.Left - (_lastMousePos.X - currentMousePos.X);
control.Top = control.Top - (_lastMousePos.Y - currentMousePos.Y);
Quick explanation:
You get the difference from the mouse positions and apply it to the object you want to move.
Example:
If the old mouse X position is 382, and the new one is 385, then the difference is -3. If the controls current X position is 10 then 10 - (-3) = 13
Why:
It works for anything, and is much cheaper than constantly converting coordinates back and forth.

Actually what you have done is correct. But you gave the MouseMove property to the picturebox. You should give that property to the Form(background).
ex:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
imagenMapa.Left = e.X;
imagenMapa.Top = e.Y;
}

Related

Place the mouse or cursor on a coordinate

I have a PictureBox that loads me a web page, I need to be able to place the mouse or cursor at certain coordinates within the PictureBox. However, I can't do it, use several ways to get the coordinates but once the web page loads, it doesn't work.
private void m_WebView_MouseClick(object sender, MouseEventArgs e)
{
label1.Text = "X = " + e.X + " ; Y = " + e.Y;
}
private void m_WebView_MouseMove(object sender, MouseEventArgs e)
{
label1.Text = e.Location.X.ToString();
label1.Text = e.Location.Y.ToString();
}
does not work when loading the web page and event is set
If I leave the picture box empty, if it shows me the coordinates
I need to locate the mouse in the exact coordinates that I see
Is it possible?
that its i try coords not work
var simulator = new InputSimulator();
Point position = PointToScreen(pictureBox1.Location) +
new Size(pictureBox1.Location.X / 2 , pictureBox1.Location.Y / 7 );
SetCursorPos(position.X, position.Y);
for #Olivier Jacot-Descombes
works the question is how do I locate the coordinates of a and b without having to adjust the values ​​of
e.Width / 2, pictureBox1.Size.Height / 7));
and start the program close edit open close edit, until I manage to locate them that is my main problem
with this I get coordinates but they don't work
private void m_WebView_MouseMove (object sender, MouseEventArgs e)
{
label1.Text = e.Location.X.ToString ();
label2.Text = e.Location.Y.ToString ();
}
point A = x 106 and Y 106
point B = x 564 and y 225
How do I put those coordinates since they don't work
To place the cursor inside the picture, you must add a fraction of Size.Width and Size.Height to the location of the picture box.
This works for me
Cursor.Position = pictureBox1.PointToScreen(
new Point(pictureBox1.Size.Width / 2, pictureBox1.Size.Height / 7));
Note that this uses the PointToScreen method of the picture box instead of the form. Therefore, the location of the picture box is used as a reference automatically. This save us some calculations.

Visual Studio C# how to move a button's X position by mouse without flicker

So I'm trying to make a slider. I'm using my cursor to move a button's x position.
I have 3 functions, the mouseDown, mouseUp and the mouseMove function. In the mouseUp and mouseDown functions I set a variable to true and false to tell the program that the mouse is clicked or not. In the mouseMove function I tell the program to set the button's x position to the x position of the mouse when the mouse is clicked. This works but has 2 problems.
The first problem is that when I press the button and move it, the button moves along with the mouse's x but it has a space between the mouse and the button. It looks a bit like this:
CURSOR.......BUTTON
The space between the cursor and button change when I change the resolution of the form.
The second problem is that when I move the button it flickers a bit. It only does this at higher speeds but it is a problem in my case.
My code looks like this:
bool mouseDown = false;
private void volumeGrabBT_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseDown = true;
}
}
private void volumeGrabBT_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseDown = false;
}
}
private void volumeGrabBT_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown == true)
{
Point volumeBTPoint = new Point();
volumeBTPoint.X = Cursor.Position.X;
volumeBTPoint.Y = volumeGrabBT.Location.Y;
volumeGrabBT.Location = volumeBTPoint;
}
}
The volumeGrabBT is the button I'm trying to move along with the mouse.
The volumeBTPoint is the point of the button I'm trying to set the button's position to.
I hope someone can help me fix these problems. Thanks in advance!
I believe that flickering can be fixed by setting some additional form styles: SetStyle(ControlStyles.AllPaintingInWmPaint |ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
in form's constructor. It will use double buffering and generally just draws faster.
For the Cursor class, it's relative to the screen, not your form. You can use this.PointToClient() function to get client's space position of cursor, like this:
Point clientCursor = this.PointToClient(Cursor.Position);
and then use clientCursor to get exact X in your client space.
You have to translate screen coordinates to client coordinates.
Point volumeBTPoint = new Point();
Point point = this.PointToClient(Cursor.Position);
volumeBTPoint.X = point.X;
volumeBTPoint.Y = volumeGrabBT.Location.Y;
volumeGrabBT.Location = volumeBTPoint;
Instead of this you should use button's parent control (Panel, GroupBox, etc).

TranslateTransform offsets element a bit too much after multiple transforms in Windows 8 store application

I have a draggable element in a StackPanel in a Windows 8 store app. My goal is simple: drag the item somewhere on the screen and immediately after the user stops dragging it the element should return to its original starting position.
I have the following code which is meant to accomplish this task:
private void grdCover_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
Grid coverControl = sender as Grid;
double xOffset = -e.Cumulative.Translation.X;
double yOffset = -e.Cumulative.Translation.Y;
if (coverControl.RenderTransform is TranslateTransform)
{
TranslateTransform existingTransform = coverControl.RenderTransform as TranslateTransform;
existingTransform.X += xOffset;
existingTransform.Y += yOffset;
}
else
{
TranslateTransform transform = new TranslateTransform();
transform.X = xOffset;
transform.Y = yOffset;
coverControl.RenderTransform = transform;
}
}
The code sort of works. The screen looks like this upon application start:
The top element, which looks like a H is well aligned with the bottom element which looks like a U. When I first drag the H element it jumps back to its well aligned position, or at least any misalignment is so little that it's hardly perceivable to the naked eye. As I keep dragging and releasing the H element it gets more and more misaligned like this:
After 15-20 dragging moves the H element gets completely misplaced:
The problem might be due to some rounding error of the double values in the Point objects when they are translated into pixels, but that's only a wild guess. Also, on a high resolution device, such as my PC it takes more moves for the misalignment to become visible than on a lower resolution one, such as the Windows Simulator in Visual Studio.
Some other code that may be related to this issue:
I perform the dragging operation with the following code:
private void Grid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
TranslateTransform translateTransform = (sender as Grid).RenderTransform as TranslateTransform;
translateTransform.X += e.Delta.Translation.X;
translateTransform.Y += e.Delta.Translation.Y;
}
I also have the following event handler in place to stop the element from moving too much on the screen after the user is done dragging the element:
private void grdCover_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingRoutedEventArgs e)
{
e.TranslationBehavior.DesiredDeceleration = 10000000000000000;
e.Handled = true;
}
In case you're wondering I wanted to set the desired deceleration to Double.Max to block any further "sliding" movement on the screen but I got an exception saying it was out of bounds. So instead I chose this arbitrary large value.
I have no extra margins or padding on the layout root or the element to be moved or its container.
Any ideas are welcome.
Thanks, Andras
OK, I got it to work. The problem was that there was a slight mismatch between the Point coordinates in the following code bits:
TranslateTransform translateTransform = (sender as Grid).RenderTransform as TranslateTransform;
translateTransform.X += e.Delta.Translation.X;
translateTransform.Y += e.Delta.Translation.Y;
and
double xOffset = -e.Cumulative.Translation.X;
double yOffset = -e.Cumulative.Translation.Y;
The solution was to retrieve the value of
e.Delta.Translation.X
and
e.Delta.Translation.Y
and assign their values to the variables xOffset and yOffset. Retrieving the values of
e.Cumulative.Translation.X
and
e.Cumulative.Translation.Y
gave slightly different coordinates so the element kept jumping back to the wrong location.

How do I move a game piece(control) during runtime?

I am doing a simple C# program of the game Knights Tour in C# the hard way to learn all I can of C#. I have a board and a knight piece and the knight is a custom panel with the picture of the knight.
What I am attempting to do is allow the user to click and drag the knight piece control during run time (exactly the way you can move the control in design time to place it), but for whatever reason I have getting some very undesired results.
private void KTmain_Load(object sender, EventArgs e)
{
boolKnightmoves = false;
}
private void kpcKnight_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
boolKnightmoves = true;
intCurMouseX = e.X;
intCurMouseY = e.Y;
break;
case MouseButtons.Right:
case MouseButtons.Middle:
case MouseButtons.XButton1:
case MouseButtons.XButton2:
case MouseButtons.None:
default:
boolKnightmoves = false;
break;
}
}
private void kpcKnight_MouseUp(object sender, MouseEventArgs e)
{
switch (e.Button)
{
case MouseButtons.Left:
boolKnightmoves = false;
break;
case MouseButtons.Right:
case MouseButtons.Middle:
case MouseButtons.XButton1:
case MouseButtons.XButton2:
case MouseButtons.None:
default:
boolKnightmoves = false;
break;
}
}
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (boolKnightmoves)
{
txtTest.Text = e.X + ", " + e.Y;
txtTest.Text += Environment.NewLine + kpcKnight.Location;
int i = e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1;
int j = e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1;
txtTest.Text += Environment.NewLine + i.ToString() + ", " + j.ToString();
kpcKnight.Location = new Point(
kpcKnight.Location.X + i,
kpcKnight.Location.Y + j);//e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1);
//e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1,
intCurMouseX = e.X;
intCurMouseY = e.Y;
}
}
private void kpcKnight_MouseLeave(object sender, EventArgs e)
{
boolKnightmoves = false;
}
private void kpcKnight_LocationChanged(object sender, EventArgs e)
{
kpcKnight.Refresh();
}
I see no real reason why this code would not do the same thing, but I am obviously missing something. When I click on the knight and move it, it does not move at the same speed as the mouse, it moves much slower. It also fades while moving it where you cant see it.
How do i make the knight piece move the same way it does in the form designer in a way that makes sense moving a chess piece across a chess board?
Any assistance will be appreciated.
I updated the code a bit and it does seem to help, but the animation aspect of it is still quite choppy and the panel picks up a bit of the background as it moves and placed.
how does it do it in the form designer so smoothly?
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (boolKnightmoves)
{
txtTest.Text = e.X + ", " + e.Y;
txtTest.Text += Environment.NewLine + kpcKnight.Location;
int x = kpcKnight.Location.X + e.X - intCurMouseX;
int y = kpcKnight.Location.Y + e.Y - intCurMouseY;
kpcKnight.Location = new Point(x, y);
kpcKnight.Refresh();
/*
int i = e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1;
int j = e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1;
txtTest.Text += Environment.NewLine + i.ToString() + ", " + j.ToString();
kpcKnight.Location = new Point(
kpcKnight.Location.X + i,
kpcKnight.Location.Y + j);//e.Y == intCurMouseY ? 0 : e.Y > intCurMouseY ? 1 : -1);
//e.X == intCurMouseX ? 0 : e.X > intCurMouseX ? 1 : -1,
intCurMouseX = e.X;
intCurMouseY = e.Y;*/
}
}
Why don't you just set the Knights position the same as the mouse position in the Mouse_Move method?
Something like:
kpcKnight.Location = new Point(e.X, e.Y)
Obviously you can make it move nicer by knowing where the Knight got initially clicked and move smoothly according to that delta without having the initial jitter.
It's drawing so slowly because you're moving a panel with every mouse motion. That means the form needs to redraw itself several times, and a complete form redraw is expensive.
The basic solution is - don't change the panel's position that often.
I see two ways of doing it.
The first way is simple, but may look jerky. Don't draw every mouse movement. Draw every 5th one, or some arbitrary number you set. Basically keep a counter that you set to 0 on mouse down, and every time you get a mouse move, check if ++counter % n == 0. If so, do the actual drawing. Just make sure to draw on mouse up, as well. Or, just as effectively, only draw when one mouse movement is a certain number of points in x or y away from the previous position.
The second idea is more complicated, but should be the fastest, least jerky thing you can do. Don't move the panel at all while the mouse is moving. Instead, make the panel disappear, and set the cursor to a custom cursor showing the knight. On mouse up, reset the cursor, move the panel and make it visible. This should be about as fast as you can get.
Edit
I'm going to go into the realm of metaphor here, just to get a few things across. Animation is an aspect of C#, but it's not one of the features of it. (i.e., you can accomplish it, but it doesn't have much to make these things easy on you, and it's not a simple key feature.) So... metaphor.
Think of the controls you've placed on your screen to make your board and knight as a bunch of cars packed tight into a parking lot. All you're doing is looking at the parking lot from a helicopter high up. What you're telling the runtime to do, every time you move a component, is completely bulldoze the cars off the parking lot, then place them with a crane in new positions. That's the scope that I'm talking about when I say "a complete form redraw is expensive."
Instead, what you want to do from your helicopter is percieve that the cars are magically changing position. Rather than have a bulldozer and a crane, just blank out your helicopter view, take a snapshot of what you want to see, and change the snapshot little by little, until it looks the way you want. That's what the second suggestion is - don't consantly force the form to recalculate each component's look. Instead, put the animation above the form, and only change the form when you're done.
The keywords you want to search for are "gdi+" (the .NET graphics package), and animation. MouseMove wouldn't hurt, and Paint is the event where you may need to do the animations. There are plenty of places you can find, though How to draw rectangle on MouseDown/Move c# could be a good start.
Edit #2
One last suggestion I have. You can use this in addition to any animation you make. Functionally, it satisfies your requirements on its own, but it's probably not what you're looking for. Track the mouse, and modify the background image of whatever panel the mouse is hovering over. In that case, you'll want to be looking at ImageList, and simple properties like your control BackgroundImage. This could be nice even if you do have a better animation working. You can easily use this to show "the knight can't move here" or "the knight has already moved here." Since it's changing your component instead of moving your component, it's really inexpensive to do, and can easily keep up with your mouse movement. It may be sufficient to imply the movement you want, and it will teach you aspects of winforms that are more important and frequently used than animation and GDI+ rendering.
All you need is:
private int intCurMouseX, intCurMouseY;
private void kpcKnight_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
intCurMouseX = e.X;
intCurMouseY = e.Y;
}
}
private void kpcKnight_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
kpcKnight.Location = new Point(
kpcKnight.Location.X + (e.X - intCurMouseX),
kpcKnight.Location.Y + (e.Y - intCurMouseY));
}
}
Transparency in .Net is a bit of a misnomer. The background simply becomes the color of the parent container. When your controls overlap with each, as will likely be the case when the pieces are dragged across the board, then you'll see the "background" of the piece because controls are rectangular. One option would be to actually CLIP the PictureBox so it is an irregular shape. This can be accomplished by creating a Region() from a GraphicsPath() and then assigning that to the Region() property of the PictureBox. A simplistic approach is to use whatever color is in the top left of the Image and use that as the "mask" color. Next walk the entire image and only add locations where pixels are not the mask color to the GraphicsPath(). This only needs to be done once with the PictureBox after the Image() has been assigned. Again, this approach requires that the "background" of your image (the parts you do NOT want to keep) are all the same color, AND also that this color is not present anywhere as part of the image you want to keep. Here's an example:
private void Form1_Load(object sender, EventArgs e)
{
// perform this for all your PictureBox pieces:
this.ClipPictureBoxPiece(this.kpcKnight);
// ...
// ...
}
private void ClipPictureBoxPiece(PictureBox pb)
{
if (pb != null && pb.Image != null)
{
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
using (Bitmap bmp = new Bitmap(pb.Image))
{
Color mask = bmp.GetPixel(0, 0);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
}
pb.Region = new Region(gp);
}
}

C# Mouse Tracking Drawing Odd Behavior

I've just made some simple code to track the mouse offset from where Mouse Down to its current position on Mouse Move within a PictureBox. I'm outputting the difference to a label and it works fine.
So say I mousedown at X: 20 Y: 20 then move mouse left by 5. My result is X: 15 Y:20.
Now the issue is when I take these results (diffX and diffY) and add them to an integer (testOne and testTwo). The result is exponentially different.
Most relevant is that when I keep the mouse in the same position without moving it but just holding the button. The results continue to increase.
I have reduced my problem to the following code:
Point startPoint = new Point();
bool dragging = false;
int testOne = 30;
int testTwo = 30;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
int diffX = (pictureBox1.PointToClient(e.Location).X - startPoint.X);
int diffY = (pictureBox1.PointToClient(e.Location).Y - startPoint.Y);
label9.Text = diffX.ToString(); //Works, shows desired result
label10.Text = diffY.ToString(); //also works fine
testOne = (testOne + diffX); //Issue here
testTwo = (testTwo + diffY); //and here
label11.Text = (testOne).ToString(); //Unexpected results output
label12.Text = (testTwo).ToString();
}
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (!dragging) //Incase the mouse down was repeating, it's not
{
startPoint = pictureBox1.PointToClient(e.Location);
dragging = true;
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (dragging)
dragging = false;
}
I'm using C# WinForms in VS 2008, Framework 3.5
any insight would be great, maybe this is a bug or I've simply overlooked something simple. Any ideas or if you can re-produce.
Cheers
Craig
It looks as though you are subtracting the current point from the start point, not the last point. Set startPoint to the current point at the end of your mousemove function.
startPoint = pictureBox1.PointToClient(e.Location);
Whe you don't move mouse you should not be getting MouseMove events... Also clicking mouse button will send you MouseMove. In genral MouseMove is send whenever it seems practical and you should be ready to handle 0 movements too.
The value of testOne and testTwo measures "Sum of all mouse movements" which will grow as long as mouse offset (diffX/Y) is positive (essentially it is integral of mose movements). What your expectations for this values?

Categories

Resources