Drag and Drop - Moving a Label in Winforms - c#

I've created a label override that will allow my new control to be moved around the screen easily.
I've attached the code below, but when I run the application or attempt to move the label it's always off. It also will sometimes just completely vanish, leave a trail or reset to the 0,0 location. Have a look at the screenshot.
I used to have this working 100%, but after some recent tweaking it has gone to the dogs again and I'm not sure how to get it working.
Screenshot:
Code:
internal sealed class DraggableLabel : Label
{
private bool _dragging;
private int _mouseX, _mouseY;
public DraggableLabel()
{
DoubleBuffered = true;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_dragging)
{
Point mposition = PointToClient(MousePosition);
mposition.Offset(_mouseX, _mouseY);
Location = mposition;
}
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_dragging = true;
_mouseX = -e.X;
_mouseY = -e.Y;
BringToFront();
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (_dragging)
{
_dragging = false;
Cursor.Clip = new Rectangle();
Invalidate();
}
base.OnMouseUp(e);
}
}

The OnMouseMove() code is bad. Do it like this instead:
private Point lastPos;
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
int dx = e.X - lastPos.X;
int dy = e.Y - lastPos.Y;
Location = new Point(Left + dx, Top + dy);
// NOTE: do NOT update lastPos, the relative mouse position changed
}
base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
lastPos = e.Location;
BringToFront();
this.Capture = true;
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
this.Capture = false;
base.OnMouseUp(e);
}
The screen shot also shows evidence of the form not redrawing properly. You didn't leave any clue as to what might cause that.

I had a problem like this recently, and I solved it by setting the background color of the label to Color.Transparent.
If this doesn't solve it, then you should consider monitoring your event handling. It could be that you're registering more than one mouse move event and thus each method would interfere with the other.
EDIT :
Have you also tried overriding the OnPaint method of its parent class?
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}

Maybe you should put focus on your element like
label1.Focus();

Related

How do I draw a rectangle based on the movement of the mouse?

I found sample code here for drawing on a form:
http://msdn.microsoft.com/en-us/library/aa287522(v=vs.71).aspx
As a followup to this requirement (discovering which controls are beneath a rectangle described by
the user dragging the mouse):
There seems to be a mismatch between the location of my controls and the location of my MouseDown and -Up events
...I want to provide the user instant/constant feedback about just what they are about to select
(when/if they release the mouse button). I want to not just draw a line following the mouse's
movement, but draw the rectangle that is being described by their mousewrangling efforts.
I'm thinking the MouseMove event, coupled with code from the two links above, could do the trick, but is that fired too often/would that have a malevolent impact on performance? If so, what would be a preferable event to hook, or would a timer be the way to go here?
UPDATE
This code, adapted from John's example below (the only difference is the StackOverflow-inducing calls to base.* are commented out, and I changed the color from red to black (no reference to Stendahl intended)), works except that previously drawn rectangles display again after releasing the mouse. IOW, the first rectangle draws perfectly - it disappears with the mouse up click (as intended). However, when I describe a second rectangle by depressing the left mouse key and dragging down and to the right, the first rectangle displays again! And this continues to happen - every previously drawn rectangle is remembered and brought back to the fore when a new rectangle is being drawn.
public partial class Form1 : Form
{
private Point? _start;
private Rectangle _previousBounds;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
_start = e.Location;
//base.OnMouseDown(e);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (_start.HasValue)
DrawFrame(e.Location);
//base.OnMouseMove(e);
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
ReverseFrame();
_start = null;
//base.OnMouseUp(e);
}
private void ReverseFrame()
{
ControlPaint.DrawReversibleFrame(_previousBounds, Color.Black, FrameStyle.Dashed);
}
private void DrawFrame(Point end)
{
ReverseFrame();
var size = new Size(end.X - _start.Value.X, end.Y - _start.Value.Y);
_previousBounds = new Rectangle(_start.Value, size);
_previousBounds = this.RectangleToScreen(_previousBounds);
ControlPaint.DrawReversibleFrame(_previousBounds, Color.Black, FrameStyle.Dashed);
}
}
ControlPaint.DrawReversibleFrame() will do what you want. Performance is not generally a problem - just keep it small and clean.
--
EDIT: Added a code sample. StackOverflowException indicates something is wrong - but without seeing yours, can't answer directly.
private Point? _start;
private Rectangle _previousBounds;
protected override void OnMouseDown(MouseEventArgs e)
{
_start = e.Location;
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if( _start.HasValue ) {
ReverseFrame();
DrawFrame(e.Location);
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
ReverseFrame();
_start = null;
base.OnMouseUp(e);
}
private void ReverseFrame()
{
ControlPaint.DrawReversibleFrame(_previousBounds, Color.Red, FrameStyle.Dashed);
}
private void DrawFrame(Point end)
{
ReverseFrame();
var size = new Size(end.X - _start.Value.X, end.Y - _start.Value.Y);
_previousBounds = new Rectangle(_start.Value, size);
_previousBounds = this.RectangleToScreen(_previousBounds);
ControlPaint.DrawReversibleFrame(_previousBounds, Color.Red, FrameStyle.Dashed);
}

How to inherit events between objects

I need inherit events and properties. For example, I need to move a picture around a form.
I have this code to move one picture but I need to create multiple images with the same behavior.
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
x = e.X;
y = e.Y;
}
}
private void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
pictureBox.Left += (e.X -x);
pictureBox.Top += (e.Y - y);
}
}
Create custom control:
public class MovablePictureBox : PictureBox
{
private int x;
private int y;
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
x = e.X;
y = e.Y;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Left += (e.X - x);
Top += (e.Y - y);
}
}
}
UPDATE:
Instead of attaching a delegates, you should override inherited event functionality, as Microsoft recommends here.
After creating this control just compile program and drag your MovablePictureBoxes from Toolbox to form. They all will be draggable (or movable, if you wish).
What you really want to do is have your multiple PictureBoxes share the same event handlers:
private void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// the "sender" of this event will be the picture box who fired this event
PictureBox thisBox = sender as PictureBox;
thisBox.Left += (e.X -x);
thisBox.Top += (e.Y - y);
}
}
Each PictureBox you create on your form keep hooking them up to the same, already created, event. If you look at the above code you'll notice that it determines which PictureBox called it and affects just that picture box.

How to draw a selectable line?

I want to create an application which the user is able to manipulate the line he draw. Something like Deleting the line or Selecting it. How should I do that?
Thanks in advance
I managed to do it using a hard coded rectangle. But I don't still have an idea how to do it using the drawLine() Can I use drawPath to do the hit test?
Here is the code:
private bool selectGraph = false;
private Rectangle myrec = new Rectangle(50, 50, 100, 100);
private Graphics g;
private void panel1_Paint(object sender, PaintEventArgs e)
{
SolidBrush sb = new SolidBrush(Color.Blue);
Pen p = new Pen(Color.Blue, 5);
e.Graphics.DrawRectangle(p, myrec);
e.Graphics.FillRectangle(sb, myrec);
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
Point mPT = new Point(e.X, e.Y);
if (e.Button == MouseButtons.Left)
{
if (myrec.Contains(mPT))
{
selectGraph = true;
button1.Enabled = true;
}
else
{
selectGraph = false;
button1.Enabled = false;
}
}
Invalidate();
}
Well you could start with something like a simple Line class:
public class Line
{
public Point Start { get; set; }
public Point End { get; set; }
}
Then you could have your form:
private Line Line = new Line();
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.Red, this.Line.Start, this.Line.End);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Line.Start = e.Location;
this.Refresh();
}
else if (e.Button == MouseButtons.Right)
{
this.Line.End = e.Location;
this.Refresh();
}
}
So basically then they could delete the this.Line maybe on "MiddleButton" click or something. This should be enough to get you started.
I've created a sample that shows how this can be done. Set some break points and see how things are done.
There is no easy one line solution for this. You would have to program this yourself.
You have to keep track of every object you have drawn. In the onmousedown event you have to find out if the mouse has clicked on or near an object you want to move/delete by comparing coordinates. Then you need to draw some visual guide that the line is 'selected'. Deleting is now quite easy by removing the object from the collection.
For drag and drop you have to do something similar by changing the coordinates of the object according to the mouse move.

Really strange ToolStripButton event problem

I am making a CustomControl based on a ToolStripButton control, I am trying to know when the mouse is Hover the button to draw it differently. Here is a quick view of my code :
private bool m_IsHover = false;
...
protected override void OnMouseEnter(EventArgs e)
{
m_IsHover = true;
Debug.WriteLine("Mouse IN");
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
m_IsHover = false;
Debug.WriteLine("Mouse OUT");
base.OnMouseLeave(e);
}
...
protected override void OnPaint(PaintEventArgs e)
{
// Define rectangle used to draw
Rectangle borderRec = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
if (m_IsHover)
{
// Draw border
e.Graphics.DrawRectangle(m_BorderPen, borderRec);
...
}
else
{
// Default draw
base.OnPaint(e);
}
}
My problem is that I clearly see in the debug panel that Mouse IN and Mouse OUT are right, so variable should be correctly set, but in the OnPaint event, I never enter in the m_IsHover conditionnal ...
I really don't understand what the problem is, it seem so easy ...
The ToolStripItem.Select() method runs on MouseEnter. Call this.Invalidate() to force a repaint.

C# - Moving a control to the mouse's position

I am trying to get a control to follow the cursor when the user clicks and drag the control. The problem is that 1.) the control doesn't go to the mouse's position, and 2.) the control flickers and flies all over the place. I've tried a few different methods of doing this, but all so far have failed.
I've tried:
protected override void OnMouseDown(MouseEventArgs e)
{
while (e.Button == System.Windows.Forms.MouseButtons.Left)
{
this.Location = e.Location;
}
}
and
protected override void OnMouseMove(MouseEventArgs e)
{
while (e.Button == System.Windows.Forms.MouseButtons.Left)
{
this.Location = e.Location;
}
}
but neither of these work. Any help is appreciated, and thanks in advance!
Here's how to do it:
private Point _Offset = Point.Empty;
protected override void MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_Offset = new Point(e.X, e.Y);
}
}
protected override void MouseMove(object sender, MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Point newlocation = this.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
this.Location = newlocation;
}
}
protected override void MouseUp(object sender, MouseEventArgs e)
{
_Offset = Point.Empty;
}
_Offset is used here for two purposes: keeping track of where the mouse was on the control when you initially clicked it, and also keeping track of whether the mouse button is down or not (so that the control doesn't get dragged when the mouse cursor goes over it and the button isn't down).
You definitely don't want to switch the ifs in this code to whiles, as it will make a difference.
there are mistakes in the answer 1
1.Set mouse handlers to control ,not to form
like button1_MouseMove
2.do not use this vector,but your control instead(Point newlocation = button1.Location;)
3.you do not need to override handlers.
in my test after these changes button(or other control) moves fine.
Chigook
Try this to move the object according to the mouse's position and the code below given is to gather the mouse's move path and the location saved in the arraylist to obtain the path where mouse point is moving. You have to declare the arraylist globally.
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ArrayList inList = new ArrayList();
inList.Add(e.X);
inList.Add(e.Y);
list.Add(inList);
}
}
When the user clicks the button the control has to move in the path that the user dragged in the screen
private void button1_Click_2(object sender, EventArgs e)
{
foreach (ArrayList li in list)
{
pic_trans.Visible = true;
pic_trans.Location = new Point(Convert.ToInt32(li[0]), Convert.ToInt32(li[1]));
pic_trans.Show();
}
}
private Point ptMouseDown=new Point();
protected override void MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ptMouseDown = new Point(e.X, e.Y);
}
}
protected override void MouseMove(object sender, MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Pointf[] ptArr=new Pointf[]{this.Location};
Point Diff=new Point(e.X-ptMouseDown.X,e.Y-ptMouseDown.Y);
Matrix mat=new Matrix();
mat.Translate(Diff.X,Diff.Y);
mat.TransFromPoints(ptArr);
this.Location=ptArr[0];
}
}
protected override void MouseUp(object sender, MouseEventArgs e)
{
_Offset = Point.Empty;
}

Categories

Resources