How can I detect the direction of a dragging event? - c#

So I have a PictureBox and the user is supposed to drag the cursor inside it towards the direction shown by the arrow (in the PictureBox), but I'm not sure how I will set the coordinates to make sure the user dragged in the correct direction (up, down, right, or left).
private void picArrow_MouseDown(object sender, MouseEventArgs e)
{
mPointDown = new Point(e.X, e.Y);
//lblTest.Text = "X: " + mPointDown.X.ToString() + " Y: " + mPointDown.Y.ToString();
}
private void picArrow_MouseUp(object sender, MouseEventArgs e)
{
lblTest.Text += " " + " X: " + e.X.ToString() + " Y: " + e.Y.ToString();
//MessageBox.Show("Entered mouseup");
//rnd_Arr refers to the number of the arrow being shown, 0 = towards the right
if (rnd_Arr == 0 && mPointDown.X >= 0 && mPointDown.Y >= 0 && e.X >= 40 && e.Y >= 0)
{
//some code
}
else
{
MessageBox.Show("DONE!");
}
}
And I know this code doesn't work because even if the user drags down (when he's supposed to drag up), it still accepts it and increments the score.
I'm not putting too much restrictions. It doesn't have to be in a perfectly straight line, or start and end in exact locations. As long as the user is dragging inside the PictureBox and dragging to the correct direction, or at least reaches the minimum length to make sure it is considerable that the user is dragging to the correct direction, like for example:
The arrow shown is pointing to the right. The user doesn't have to drag all the way through the arrow, if he drags horizontally past 40px (and the whole length of the arrow is, say, 80px), then that'll add a point to his score. Nevertheless, I'm deliberating this part, if I should just be more demanding and require the user to drag all the way through.
Should I remove the mouse events for the PictureBox and add mouse events for the form instead?
Thank you!

You're going to want to check the MouseDown.X and compare it to the MouseUp.X (or Y if you want to check vertical direction as well). It is important to note that (0, 0) is the upper left of your screen.
Start by subtracting MouseUp.X from MouseDown.X to get a delta X. This is your change in X pixels over the move operation.
If your X comparison yields a negative delta (MouseUp.X is smaller than MouseDown.X), your mouse has moved left.
If your X comparison yields a positive delta (MouseUp.X is larger than MouseDown.X), your mouse has moved right.
The same concept applies to the Y coord, positive change means you moved the mouse down and a negative changemeans you moved the mouse up. See below code:
Point mouseDownPoint;
Point mouseUpPoint;
float deltaX = mouseUpPoint.X - mouseDownPoint.X;
float deltaY = mouseUpPoint.Y - mouseDownPoint.Y;
if (deltaX > 0)
{
// Moved right
}
else if (deltaX < 0)
{
// Moved left
}
if (deltaY > 0)
{
// Moved down
}
else if (deltaY < 0)
{
// Moved up
}

private void picArrow_MouseUp(object sender, MouseEventArgs e)
{
bool movedUp, movedDown, movedLeft, movedRight;
if (e.X == mPointDown.X) { movedRight = movedLeft = false; }
else { movedRight = e.X < mPointDown.X; movedLeft = !movedRight; }
if (e.Y == mPointDown.Y) { movedUp = movedDown = false; }
else { movedUp = e.Y < mPointDown.Y; movedDown = !movedUp; }
// Code can now use the Booleans above as needed
// . . .
}

Related

Mousemove Custom Scroll Bar maths

I am currently building a Custom Scroll Bar (pictured above) I am able to set the scrollbar value to any number within the allowed range (between Min and Max), but when it comes to mouse scrolling I have a maths problem.
I want the mouse to remain at the same position where the user clicked in the thumb bar, and then drag the thumb up or down, so that the mid position of the scroll bar reflects the value.
I have the following code to rest the mid position of the thumb:
MoveThumb()
{
m_iMidPoint = Mouse.Cursor.Y;
int iHalfThumb = ThumbLength / 2;
m_iMidPoint += iHalfThumb;
// Clamp to allowable range
m_iMidPoint = m_iMidPoint < (TrenchStartPixel + iHalfThumb)
? TrenchStartPixel + iHalfThumb
: m_iMidPoint;
m_iMidPoint = m_iMidPoint > (TrenchEndPixel - iHalfThumb)
? TrenchEndPixel - iHalfThumb
: m_iMidPoint;
Value = MidThumbToValue(m_iMidPoint); // This property gets clamped
}
public int ConvertRange( int originalStart, int originalEnd, int newStart,
int newEnd, int value)
{
double scale = (double)(newEnd - newStart) / (originalEnd - originalStart);
return (int)(newStart + ((value - originalStart) * scale));
}
public int ValueToMidThumb(int iValue)
{
return ConvertRange(Minimum, Maximum, TrenchStartPixel + (ThumbLength/2),
TrenchEndPixel - (ThumbLength/2), iValue);
}
public int MidThumbToValue(int iValue)
{
return ConvertRange(TrenchStartPixel + (ThumbLength /2),
TrenchEndPixel - (ThumbLength /2), Minimum, Maximum, iValue);
}
This code works ok, APART from the fact that Thumb always jumps so that the middle of the thumb tracks the mouse.
How can I get the thumb to track the mouse position without the jumping?
The part I was missing, was the fact that a user presses the mouse button down to begin dragging the thumb. At that point the offset from the thumb middle position can be calculated and used while dragging the thumb. This offset will remain constant until the user releases the mouse. The fact that is remains constant throughout the sequence of events, it can be used during the mouse move.
pseudo-code:
MouseDownEvent(MouseEventArgs e) {
m_mouseOffset = e.Y - Thumb.Top + (ThumbLength / 2);
}
MouseMoveEvent(MouseEventArgs e) {
int iPos = e.Y;
iPos -= m_mouseOffset;
m_iMidPoint= iPos;
}

How to scroll Flow layout panel content as per mouse position

i have taken one Flow layout panel and placed multiple picture box inside in it. now i want when i will place my mouse at the right or left most edge of the Flow layout panel then rest of picture will scroll out. just think about windows 8 start screen where many tiles appear in screen and when we place mouse at right most edge on the screen then rest of the tiles scroll out. i want to simulate same thing in windows form with Flow layout panel.
i want my Flow layout panel will not show scroll bar but images will scroll out when i will place mouse right or left most part on the panel. here is my screen shot
some one told me to do it this way...here is bit code
Set AutoScrollPosition property in MouseMove event of Panel.
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
panel1.AutoScrollPosition = new Point(e.X, e.Y);
}
but this trick was not good. AutoScrollPosition works when scroll bar is visible but in my case i do not want to show scroll bar with Flow layout panel. i want smooth scrolling images from left to right or right to left. anyone can help me to achieve what i am trying to do....if possible guide me with respect of coding. thanks
EDIT
Here i am giving my full code after modification following #Taw suggestion but it is not working fine....rather flickering found when picture move. anyway here is the full code.
namespace ScrollTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
flowLayoutPanel1.MouseMove += MouseScroll;
foreach (Control x in this.Controls)
{
if (x is PictureBox)
{
((PictureBox)x).MouseMove += MouseScroll;
}
}
}
int near = 33;
private void MouseScroll(object sender, MouseEventArgs e)
{
Point mouse = flowLayoutPanel1.PointToClient(MousePosition);
Rectangle C = flowLayoutPanel1.ClientRectangle;
int dLeft = mouse.X - C.Left;
int dTop = mouse.Y - C.Top;
int dRight = C.Right - mouse.X;
int dBottom = C.Bottom - mouse.Y;
int dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
int dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;
if (dX != 0 | dY != 0) scrollFLP(dX, dY);
}
void scrollFLP(int deltaX, int deltaY)
{
flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
System.Threading.Thread.Sleep(11);
}
int getSpeedFromDistance(int delta)
{
int sig = Math.Sign(delta);
int d = Math.Abs(delta);
if (d > near / 2) return sig;
else if (d > near / 3) return near / 10 * sig;
else if (d > near / 4) return near / 8 * sig;
else if (d > near / 5) return near / 5 * sig;
else return near * sig;
}
}
}
basically i am trying achieve something like suppose i have flow layout panel and which has many picture box inside it with many images as the screen shot but scroll bar should not show rather scroll will happen automatically when i will place my mouse at the top or bottom of the flow layout panel like carousel.
see this picture of your application
when place my mouse at the right end then it scroll and form background shown which i do not want. i want picture box will scroll & scroll upto last one not more than that.
any idea how to do it. thanks
2nd Edit
this code i added as per your suggestion
public Form1()
{
InitializeComponent();
for (int i = 0; i < 666; i++)
{
PictureBox pan = new PictureBox();
//pan.MouseMove += MouseScroll;
//pan.MouseLeave += outSideCheck;
pan.Size = new Size(75, 75);
pan.BackColor = Color.FromArgb(255, (i * 2) & 255, (i * 7) & 255, (i * 4) & 255);
flowLayoutPanel1.Controls.Add(pan);
}
//flowLayoutPanel1.MouseMove += MouseScroll;
//this.flowLayoutPanel1.MouseLeave += outSideCheck;
mouseScroller MSC = new mouseScroller();
MSC.registerControl(flowLayoutPanel1); // FLP = your FlowLayouPanel
MSC.timerSpeed = 5; // optional
MSC.nearness = 100; // optional
flowLayoutPanel1.AutoScroll = false;
}
now the apps doing wired behavior after adding new code. if i am making any mistake then guide me please. thanks
This is a two-part problem:
How to grab the event
How to scroll a FlowLayoutPanel with its scrollbars invisible.
Second first. It is not an easy task from what I found, unless you use a simple and rather common trick: Don't actually scroll it! Instead place it into a Panel and then control its position inside that Panel.
To do this you add a Panel panel1 to your Form, Dock or Anchor it as you need to and set its Autoscroll = false (!) (Which is not the way it is usually done, when you want to make, say a PictureBox scrollable. But we don't want the Panel to show it Scrollbars either.)
Set the FLP to its desired size and place it into the Panel, it obviously also has Autoscroll = false, and we're ready to tackle the other problem of setting up the event..:
First you add the MouseScroll event below to your code and then you hook every control up to it you want to work with the mouse move, namely the FLP:
flowLayoutPanel1.MouseMove += MouseScroll;
..and also each of your PictureBoxes, maybe like this
// your creation loop..
PictureBox pbox = new PictureBox();
pbox.MouseMove += MouseScroll; // <<--- hook into to the mousemove
pan.MouseLeave += outSideCheck; // <<--- hook into to the mouseleave
// .. do your stuff.. here I put some paint on to test..
pbox.BackColor = Color.FromArgb(255, 111, (i * 3) & 255, (i * 4) & 255);
flowLayoutPanel1.Controls.Add(pbox);
or however you create them..
Edit 2 I have changed my original code once more. It now includes an outside check, a check for moving towards the closest edge and a workaround for tha mousemove bug. It uses a Timer set to maybe 30ms. The speed mapping is in a function of its own.
flowLayoutPanel1.MouseMove += MouseScroll;
this.flowLayoutPanel1.MouseLeave += outSideCheck;
flowLayoutPanel1.AutoScroll = false;
int near = 33;
Point lastLocation = Point.Empty;
int dX = 0;
int dY = 0;
private void MouseScroll(object sender, MouseEventArgs e)
{
Point mouse = panel1.PointToClient(MousePosition);
Rectangle C = panel1.ClientRectangle;
// mouseMove has a bug, we need to workaround
if (mouse == lastLocation) return;
if (lastLocation == Point.Empty) { lastLocation = mouse; return; }
// distance from each edge
int dLeft = mouse.X - C.Left;
int dTop = mouse.Y - C.Top;
int dRight = C.Right - mouse.X;
int dBottom = C.Bottom - mouse.Y;
// relevant distances with sign
dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;
// we need the closest edge to check if we are moving in or out
List<int> edges = new List<int>() { dLeft, dTop, dRight, dBottom };
var closest = edges.IndexOf(edges.Min());
// if we are moving
if (dX != 0 | dY != 0)
// if moving out: go else stop going
if (!movingIn(mouse, closest)) timer1.Start(); else timer1.Stop();
// remember position
lastLocation = mouse;
}
bool movingIn(Point current, int Edge)
{
switch (Edge)
{
case 0: return current.X > lastLocation.X;
case 1: return current.Y > lastLocation.Y;
case 2: return current.X < lastLocation.X;
case 3: return current.Y < lastLocation.Y;
}
return false;
}
void scrollFLP(int deltaX, int deltaY)
{
flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
Size C = panel1.ClientSize;
if (flowLayoutPanel1.Left > 1) { flowLayoutPanel1.Left = 0; timer1.Stop(); }
if (flowLayoutPanel1.Right < C.Width)
{ flowLayoutPanel1.Left = C.Width - flowLayoutPanel1.Width; timer1.Stop(); }
if (flowLayoutPanel1.Top > 1) { flowLayoutPanel1.Top = 0; timer1.Stop(); }
if (flowLayoutPanel1.Bottom < C.Height)
{ flowLayoutPanel1.Top = C.Height - flowLayoutPanel1.Height; timer1.Stop(); }
}
int getSpeedFromDistance(int delta)
{
int sig = Math.Sign(delta);
int d = Math.Abs(delta);
if (d > near / 2) return sig;
else if (d > near / 3) return 2 * sig;
else if (d > near / 4) return 4 * sig;
else if (d > near / 5) return 6 * sig;
else return 10 * sig;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (insidePanel()) scrollFLP(dX, dY); else timer1.Stop();
}
bool insidePanel()
{
return panel1.ClientRectangle.Contains(panel1.PointToClient(MousePosition));
}
private void outSideCheck(object sender, EventArgs e)
{
if (!insidePanel()) {timer1.Stop(); lastLocation = Point.Empty;}
}
Of course you'll want to play with the various 'magic' numbers :-)
Stop code and direction check are now included.
As usual, key is to know precisely what you want.. I hope this gets you started on ways to achieve it!
It's been a while since this question was asked. I just encountered the problem. My scenario was a little different, but I still think it's a solution to the same problem (at worst a timer control can be used because autoscroll is not turned on).
Here is my scenario: I have one panel control (normal panel). I have an PictureBox in it that I made with zoom. I'm making rectangular selections on top of this image, and when the selections spilled out of the panel, my panel was supposed to slide in the direction I was selecting. (in my scenario, mouse is pressed)(also in my scenario, autoscroll is on). This is how I solved it without writing so much code:
I added two private variable for scroll position (Valid for the whole class scope).
private int xPos;
private int yPos;
private int speed = 5;
and I assigned them the current scroll positions when the form is loaded.
private void Form1_Load(object sender, EventArgs e)
{
//when I change the scrollbar manually or change with zoom I still
//need to add these lines to the related event
xPos = panel1.HorizontalScroll.Value;
yPos = panel1.VerticalScroll.Value;
}
and inside my picturebox's mousemove event
private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
Point mouse = panel1.PointToClient(MousePosition);
if (!panel1.ClientRectangle.Contains( mouse ))
{
Rectangle CRect = panel1.ClientRectangle;
int dLeft = mouse.X - CRect.Left;
int dRight = CRect.Right - mouse.X;
int dTop = mouse.Y - CRect.Top;
int dBottom = CRect.Bottom - mouse.Y;
if(dLeft < 0 && panel1.HorizontalScroll.Value > 0)
{
xPos = -panel1.AutoScrollPosition.X - speed;
}
if (dRight < 0 && panel1.HorizontalScroll.Value < panel1.HorizontalScroll.Maximum)
{
xPos = -panel1.AutoScrollPosition.X + speed;
}
if (dTop < 0 && panel1.VerticalScroll.Value > 0)
{
yPos = -panel1.AutoScrollPosition.Y - speed;
}
if (dBottom < 0 && panel1.VerticalScroll.Value < panel1.VerticalScroll.Maximum)
{
yPos = -panel1.AutoScrollPosition.Y + speed;
}
panel1.AutoScrollPosition = new Point(xPos, yPos);
}
}
}

The logic for collision detection of object with the window form is not working right.

I am trying to detect when the object in a form get bigger than the form itself. When it does that a MessageBox should appear with the text "Game over".
This is what I've done:
For x-axis
private void CollisionXForward()
{
int x = this.Width; //the width of the form is 493
//if the position of x-axis of the rectangle goes over the limit of the form...
if (rc.PositionX >= x )
{
//...game over
MessageBox.Show("Game over");
}
else
{
//move the object +5 every time i press right arrow
rc.MoveXForward();
}
The thing is that the rectangle dissapears because it goes a step further than the frame itself. I have "fixed" the problem by having this statement:
if (rc.PositionX >= x - (rc.Width * 2))
instead of the normal one you see at the code. But it doesn't work when I do the same thing with the y-axis or when I change the size of the rectangle.
Try:
if (rc.PositionX + rc.Width >= ClientRectangle.Width)
and
if (rc.PositionY + rc.Height >= ClientRectangle.Height)
Edit:
private void CollisionXForward()
{
rc.MoveXForward();
//if the position of x-axis of the rectangle goes over the limit of the form...
if (rc.PositionX + rc.Width >= ClientRectangle.Width )
{
//...game over
MessageBox.Show("Game over");
}
}
or
private void CollisionXForward()
{
//if the position of x-axis of the rectangle goes over the limit of the form...
if (rc.PositionX + step + rc.Width >= ClientRectangle.Width ) //step is 5 in your case
{
//...game over
MessageBox.Show("Game over");
}
else
{
//move the object +5 every time i press right arrow
rc.MoveXForward();
}
}
valter
Give your rc object a BoundingBox property returning a Rectangle. Then the test becomes easy
if (this.ClientRectangle.Contains(rc.BoundingBox)) {
rc.MoveXForward();
} else {
MessageBox.Show("Game over");
}
If you need to leave space for a step then test like this:
if (this.ClientRectangle.Contains(rc.BoundingBox.Inflate(step, step))) {
rc.MoveXForward();
} else {
MessageBox.Show("Game over");
}
public class MyRcClass
{
...
public Rectangle BoundingBox
{
get { return new Rectangle(PositionX, PositionY, Width, Height); }
}
}

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 can we get the location relative to window form?

I am implementing an application which can be drag and drop images in a panel and so I want to make sure that the image is placed within the panel and it is visible the whole image when is dropped.In that case I want to get the current cursor position when I doing drag and drop event. So how can I get the cursor location related to panel?
Here is the method of panel dragdrop event.
private void panel1_DragDrop(object sender, DragEventArgs e)
{
Control c = e.Data.GetData(e.Data.GetFormats()[0]) as Control;
if (c != null)
{
if (e.X < 429 && e.X > 0 && e.Y<430 && e.Y>0)
{
c.Location = this.panel1.PointToClient((new Point(e.X, e.Y)));**
this.panel1.Controls.Add(c);
}
}
}
You can get cursor coordinates using Cursor.Position, this gets you screen coordinates. you can then pass these into PointToClient(Point p)
Point screenCoords = Cursor.Position;
Point controlRelatedCoords = this.panel1.PointToClient(screenCoords);
Though, I am fairly certain that DragEventArgs.X and DragEventArgs.Y are already screen coordinates. Your problem probably lies in
if (e.X < 429 && e.X > 0 && e.Y<430 && e.Y>0)
This looks like it would be checking against Panel coordinates, whereas e.X and e.Y are screen coordinates at that point. Instead, thansform it into panel coords before checking against bounds :
Point screenCoords = Cursor.Position;
Point controlRelatedCoords = this.panel1.PointToClient(screenCoords);
if (controlRelatedCoords.X < 429 && controlRelatedCoords.X > 0 &&
controlRelatedCoords.Y < 430 && controlRelatedCoords.Y > 0)
{
}

Categories

Resources