c# reliable MouseMove (hop) - c#

I'm using this function to move the cursor.
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
When I use a hotkey to trigger it, the cursor will move to the correct coords and next time I move the mouse it continues from that position. Working as intended.
However I need to trigger SetCursorPos during a MouseMove event. If user moves mouse into a certain area I want it to hop to a different place and carry on from there. But right now it hops to the destination and then immediately hops back (90% of the time). How can I avoid that behavior?
Edit: I decided to work around it by clipping the cursor in 1 by 1 px square for 1 mousemove event. Cursor.Clip(MousePosition, new Rectangle(1, 1));

My guess is that there is another control on top of your form in the area where you want to trigger the event. If so, the control is capturing the MouseMove event.
For example, here I've added a green 200x200 panel at position 0, 0 in the upper left hand corner. If the mouse moves over the panel, the form's MouseMove event will stop capturing the mouse cursor position. In my form's mouse_move event, I set the form's text to display the mouse coordinates. Notice the coordinates in the Window Text are still 200, 200 when the mouse was actually at 0, 0 (can't see my cursor due to having to click on SnippingTool.exe to get the screenshot).
To remedy this, use the same code you placed in your form's MouseMove event in the panel's MouseMove event (or whichever control you are using). This results in the correct coordinates in the form's text.
And here is the code (This could obviously be refactored into a single method):
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.Text = string.Format("X: {0}, Y: {1}", e.X, e.Y);
if (e.X >= 0 && e.X <= 200)
{
if (e.Y >= 0 && e.Y <= 200)
{
SetCursorPos(500, 500);
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
this.Text = string.Format("X: {0}, Y: {1}", e.X, e.Y);
if (e.X >= 0 && e.X <= 200)
{
if (e.Y >= 0 && e.Y <= 200)
{
SetCursorPos(500, 500);
}
}
}
}

Hard to say with such little Information (maybe a screenshot of your GUI would help).
You could try:
private void Button_MouseMove_1(object sender, MouseEventArgs e)
{
SetCursorPos(0, 0);
e.Handled = true;
}

Related

Drag Ovalshape across form

So I have a bit of a problem with moving Ovalshapes around a form. The goal is to have a circle move within the bounds of two other circles, one placed inside the other and the moving circle essentially moving around them.
I am having trouble moving the circle with the mouse. Whenever I click and hold the circle, the circle moves to the coordinates of the location I clicked on the circle, such that if I click in the middle of an Ovalshape of size 10, it would set the circle's location to (5,5).
Here is what I have:
public partial class Form1 : Form
{
int smallRadius;
int largeRadius;
int movingRadius;
int distanceFromCenterToLocation;
bool mouseDown;
Point movingCenter;
Point returnPoint;
public Form1()
{
mouseDown = false;
InitializeComponent();
smallRadius = (ovalShape1.Right - ovalShape1.Left) / 2;
largeRadius = (ovalShape2.Right - ovalShape2.Left) / 2;
Point center = new Point(ovalShape1.Left + smallRadius, ovalShape1.Top + smallRadius);
ovalShape3.Height = largeRadius - smallRadius;
ovalShape3.Width = largeRadius - smallRadius;
movingRadius = (ovalShape3.Right - ovalShape3.Left) / 2;
ovalShape3.Location = new Point(center.X - (movingRadius), center.Y - largeRadius);
movingCenter = new Point(ovalShape3.Left + movingRadius, ovalShape3.Top + movingRadius);
distanceFromCenterToLocation = Convert.ToInt32(Math.Sqrt(Math.Pow(movingRadius, 2.0) + Math.Pow(movingRadius, 2.0)));
int middleRadius = center.X - movingCenter.X;
}
private void ovalShape3_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
private void ovalShape3_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = true;
}
private void ovalShape3_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
ovalShape3.Location = e.Location;
}
}
}
For some reason OvalShape isn't derived from Control and doesn't behave like a control.
When a control receives a mouse event the Location property of the MouseEventArgs holds the coordinates relative to the upper-left corner of the form. Shapes however receive the coordinates relative to their own upper-left corner.
When a mouse button is pressed over a control it will capture the mouse so that subsequent events are sent to the same control until you release the button. Shapes only receive mouse events if the mouse is over the shape no matter if a mouse button is pressed. Once you move the mouse to the top or left the shape doesn't receive any mouse events. Therefore the shape doesn't follow the mouse and also the MouseUp event is not handled.
All shapes in a form are embedded in a single control of type ShapeContainer. This is created automatically when you add a shape to a form in the Visual Studio designer. To get the expected behavior you need to find that ShapeContainer (probably called shapeContainer1) and handle its mouse events instead:
public Form1()
{
InitializeComponent();
// do your initialization
...
// assign the events to the ShapeContainer
// don't forget to remove the handlers from ovalShape3 in the designer!!!
ShapeContainer container = ovalShape3.Parent;
container.MouseUp += ovalShape3_MouseUp;
container.MouseDown += ovalShape3_MouseDown;
container.MouseMove += ovalShape3_MouseMove;
}

Picturebox OnMouseClick coordinates relevance to the control and not the form

I have a windows form, inside it there is a PictureBox control, I populate the control with an image.
When went to the MouseDown event and was able to get the coordinates but in terms of the whole form and not just the control, so instead of getting a coordinate of (10,15) I get (110,40) that is because it got me the mouse position of the form.
Can I get a coordinates enclosed to the PictureBox control only?
Try this .........
private void PictureBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
int x = e.x - PictureBox1.Left ;
int y = e.y - PictureBox1.Top ;
MessageBox.Show(x.tostring + "," + y.tostring);
}
Use x - PictureBox.Left and y - PictureBox.Top.
OR
Write the code in the event handler for the PictureBox, not the form.

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);
}
}

How to make the ToolTip follow the mouse?

I want the ToolTip to follow my mouse moving over one control. For example, let's take a panel. When mouse location is inside the Rectangle(100, 100, 50, 50) I want ToolTip to be visible and always on the right down of the mouse. When it's outside this rectangle, I want ToolTip to be invisible.
I tried to do this like that:
ToolTip toolTip = new ToolTip();
int x, y;
protected override void OnMouseMove(MouseEventArgs e)
{
if ((x == e.X) && (y == e.Y) && (new Rectangle(100, 100, 50, 50).Contains(e.Location))
toolTip.Show("some text", this, x + 10, y + 10);
else
{
x = e.X;
y = e.Y;
toolTip.Hide(this);
}
}
But there's a problem - when my toolTip shows up - it gets the focus and after that OnMouseMove(MouseEventArgs e) doesn't work any more. I tried to get the focus to the panel in the end of that function, but it doesn't work. I also tried some tricks with OnMouseHover, but it was the same effect.
Don't use a ToolTip for that - if the Panel is drawn on, draw your own ToolTip; otherwise, use a Panel and respond to MouseMove events from both, but ignore e.Location and instead use System.Windows.Forms.Cursor.Position and PointToClient.

Move a PictureBox with mouse

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;
}

Categories

Resources