Problem with moving a winform using C# - c#

My form doesn't have a title bar, so I am implementing the code to drag
the entire form around the screen. I am using the below code to do it, which works fine.
I have two panels in my form, PanelA and PanelB. During the startup I show
PanelA where the dragging works perfectly. Later when the user clicks
the button in PanelA, I need to make PanelA invisible and show PanelB
However, the dragging does not work when PanelB is shown. What's the
problem here?
private void SerialPortScanner_MouseUp(object sender, MouseEventArgs e)
{
this.drag = false;
}
private void SerialPortScanner_MouseDown(object sender, MouseEventArgs e)
{
this.drag = true;
this.start_point = new Point(e.X, e.Y);
}
private void SerialPortScanner_MouseMove(object sender, MouseEventArgs e)
{
if (this.drag)
{
Point p1 = new Point(e.X, e.Y);
Point p2 = this.PointToScreen(p1);
Point p3 = new Point(p2.X - this.start_point.X,
p2.Y - this.start_point.Y);
this.Location = p3;
}
}

Edit: I've realized that you're not asking about drag and drop, but rather about moving your form around the screen. (Thanks to #Veer.) I've edited your question to help clarify this. Drag and drop is a completely different thing, since it is dragging information from one control to another.
The same principle of my answer still applies though, since mouse events are also handled at the Control level - you might need to handle the mouse events from PanelB as well.

Related

Redrawing issues when moving a control

I am trying to move some controls around on a WinForm with the mouse. I am using the code below. To see my issue start a new project in VS add the code below. Set the form BackGroundImage to any image then add any control. Set the control events for MouseUp, MouseDown, and MouseMove. Start debugging and click and move the control. The image in the form starts getting erased. I have tried several different suspend drawing classes and methods I have found on the net but nothing I have found lets me move the controls around without serious flickering or not being able to see the move. Any help would be appreciated.
P.S. If you set the same events to the up, move, and down events of the form, it moves fine with out any flickering.
private bool _mouseDown;
private Point _startPoint;
private void Event_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_mouseDown = true;
_startPoint = new Point(e.X, e.Y);
}
}
private void Event_MouseUp(object sender, MouseEventArgs e)
{
_mouseDown = false;
}
private void Event_MouseMove(object sender, MouseEventArgs e)
{
if (_mouseDown)
{
Control s = sender as Control;
s.Location = new Point(e.X + s.Left - _startPoint.X, e.Y + s.Top - _startPoint.Y);
}
}

C# Can't drag my button with mouse

So i searched a little around on google to find some code to drag buttons with the mouse. I found a lot, even though none of these actually worked for me.
So i'm asking you! :-)
The code i'm trying to get working in my form:
bool isDragged = false;
Point ptOffset;
private void button1_MouseDown( object sender, MouseEventArgs e )
{
if ( e.Button == MouseButtons.Left )
{
isDragged = true;
Point ptStartPosition = button1.PointToScreen(new Point(e.X, e.Y));
ptOffset = new Point();
ptOffset.X = button1.Location.X - ptStartPosition.X;
ptOffset.Y = button1.Location.Y - ptStartPosition.Y;
}
else
{
isDragged = false;
}
}
private void button1_MouseMove( object sender, MouseEventArgs e )
{
if ( isDragged )
{
Point newPoint = button1.PointToScreen(new Point(e.X, e.Y));
newPoint.Offset(ptOffset);
button1.Location = newPoint;
}
}
private void button1_MouseUp( object sender, MouseEventArgs e )
{
isDragged = false;
}
I of course changed the pictureBox1 to button1.
But i just can't get this to work.
Anyone who might know why?
Oh, and i want to use this at all my buttons, so what should i replace button1 with to make it work at all of the buttons?
-Btw, i use Visual studio Express.
Thank you in advance!
even though i'm really just copy pasting when it comes to windows
forms
The code you posted won't actually do anything by itself!
To work, the MouseDown() and MouseMove() events of the control have to be wired up to those methods:
Select the control on the form (pictureBox1).
In the Properties Pane (bottom right by default), click the
"Lightning Bolt" icon to get a list of the events for that control.
Find the MouseDown entry and change the dropdown to the right of
it to pictureBox1_MouseDown.
Find the MouseMove entry and change the dropdown to the right of
it to pictureBox1_MouseMove.
Now run it and drag the pictureBox1 around.
EDIT: Here is how to make the code work for multiple controls, as outlined in my comment below.
bool isDragged = false;
Point ptOffset;
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
isDragged = true;
Button btn = (Button)sender;
ptOffset = new Point(btn.Location.X - Cursor.Position.X, btn.Location.Y - Cursor.Position.Y);
}
else
{
isDragged = false;
}
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (isDragged)
{
Point newPoint = Cursor.Position;
newPoint.Offset(ptOffset);
Button btn = (Button)sender;
btn.Location = newPoint;
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
isDragged = false;
}
Ok, the good news: hopefully we'll have a CSI desktop layout once you're done.
The not so good news: I think you're trying to do the wrong thing.
I'm assuming you're using WinForms, but the idea should be the same for WPF.
When you run your program, the layout is set (either dynamically, for example it changes when you resize the window, or statically, for example when you set it with absolute X,Y position).
What you want to do is kind of: I want to get the benefits of them creating all the boiler plate code, but then, I want to dynamically change it at run time ...
I think you need to rethink your approach, and take this one step at a time (after all, it took Microsoft years to get the GUI to where it's at at the moment ... It'll take some time to get the cool futuristic GUI's out).
Take baby steps, but keep in mind the big picture:
Start with having a Canvas defined.
Then you can place a button on it.
Then fool around with moving that button on the canvas. (The code you have in your question should work for an object on a canvas).
From there, keep taking steps that will get you closer to your destination ...

Prevent drag and drop outside of the current control (TreeNodes in a TreeView)

I'm maintaining a Windows app that has multiple forms in the one window (form1, form2, form3). I'm treating the other form2 and form3 as black boxes at the moment. In form1 I have a TreeView, and I'm implementing drag and drop functionality within that TreeView.
How can I prevent a drop outside of the form1 control?
I'm implementing 3 events handlers:
private void treeView_ItemDrag (...)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
private void treeView_DragEvent (...)
{
e.Effect = DragDropEffects.Move;
}
private void treeView_DragDrop (...)
{
//the node move logic here
}
form2 and form3 have a drag and drop relationship between them, so when I drag a node from form1 into form3 by default it allows the move (bad). I want to be able to prevent this from the form1 control code.
How can I prevent a drop outside of the form1 control? I've looked at the _DragLeave event, but I'm unsure how to control the operation without the DragEventArgs.
There is this little know property in the Cursor object that can restrict the mouse movement only to a certain rectangle.
this as a global variable for Form1
Rectangle _originalClip;
this goes in your Form1_Load event
_originalClip = Cursor.Clip;
this could be in your treeView.ItemDrag, forcing the cursor inside the form1 client area
Cursor.Clip = form1.RectangleToScreen(form1.ClientRectangle);
Now you need to restore the original clip area. A good place will be in the treeView.DragDrop. But to be on the safe side put also in your Form1_Closing event
Cursor.Clip = _originalClip;
You can check if mouse drag action is going outside the allowed area, and if so, cancel the drag action.
There's a nice sample in MSDN that uses the QueryContinueDrag event for that purpose. I think you can use that on the base of your solution.
Link: DragAction Enumeration
I know this is an old topic, but since I've never found a good answer to how to prevent dragging a control outside of a panel, I thought that I'd throw in the solution that I put together. I used some the tips from above and some work of my own.
private void Form1_Load(object sender, EventArgs e)
{
_originalClip = Cursor.Clip;
}
private void pb_MouseMove(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (e.Button == MouseButtons.Left)
{
Size sz = new Size(panel1.RectangleToScreen(panel1.ClientRectangle).Width - (pb.Width), panel1.RectangleToScreen(panel1.ClientRectangle).Height - (pb.Height));
Point loc = new Point(panel1.RectangleToScreen(panel1.ClientRectangle).X + (pb.Width / 2), panel1.RectangleToScreen(panel1.ClientRectangle).Y + (pb.Height / 2));
Rectangle rct = new Rectangle(loc, sz);
Cursor.Clip = rct;
pb.Left += (e.X - x);
pb.Top += (e.Y - y);
}
}
private void pb_MouseUp(object sender, MouseEventArgs e)
{
Cursor.Clip = _originalClip;
}
What this does is uses the Cursor.Clip method along with a Rectangle object with it's size set to be the size of the panel ("panel1" in the code) containing a bunch of Pictureboxes ("pb" in the code). The new rectangle's size is set to the parent panel minus the width and height of the Picturebox and it's location set to the location of panel1 minus half of the Picturebox's width and height. This gives you a rectangle which will constrain the Picturebox from being drug outside the panel.

.NET C# MouseEnter listener on a Control WITH Scrollbar

As long as the mouse is over a specific control, we show some form. When the mouse leaves the control, we hide the control after a small timeout. This is standard hover behavior.
However, when a control (for example a Treeview) has a scrollbar, and the mouse is ON or OVER the scrollbar, the events don't fire ...
If we could get a reference to the scrollbar control, this would solve our problem, as we would add the same listener events to the scrollbar. However, the scrollbar isn't accessible as far as I know ...
How can we solve this problem ?
The scrollbar is in the tree view's non-client area. When the mouse moves there, it starts generating non-client messages like WM_NCMOUSEMOVE and WM_NCMOUSELEAVE. You would have to sub-class the TreeView and override WndProc() to detect these message.
This doesn't really solve your problem though, you'll still have a hard time with edge cases. A low-tech approach with a Timer always works:
private Form frmPopup;
private void treeView1_MouseEnter(object sender, EventArgs e) {
timer1.Enabled = true;
if (frmPopup == null) {
frmPopup = new Form2();
frmPopup.StartPosition = FormStartPosition.Manual;
frmPopup.Location = PointToScreen(new Point(treeView1.Right + 20, treeView1.Top));
frmPopup.FormClosed += (o, ea) => frmPopup = null;
frmPopup.Show();
}
}
private void timer1_Tick(object sender, EventArgs e) {
Rectangle rc = treeView1.RectangleToScreen(new Rectangle(0, 0, treeView1.Width, treeView1.Height));
if (!rc.Contains(Control.MousePosition)) {
timer1.Enabled = false;
if (frmPopup != null) frmPopup.Close();
}
}
I think there are several different ways to do this, but the key is your desire to have a timeout on the action. I think a combination of two techniques might work:
Put the control on a panel, docked to fill, and use the MouseEnter of the panel to turn on your behavior -- this will include the control's scrollbar. You can use the MouseLeave event of the panel as well, but you'll have to check the cursor's position to ensure it hasn't moved into the contained control. This method is mostly reliable, but moving the mouse quickly can confuse it.
If you combine this with a timer that starts when your shown/hidden control is shown and check the cursor position periodically. This will work, but your timeout before hiding the control won't necessarily be consistent (because the timer starts when they enter the control). You could stop/start the timer on mousemoves in the control to alleviate this somewhat.
I put together a project of the different methods I tried here: http://lovethedot.s3.amazonaws.com/100609StackoverflowScrollbarQuestion.zip
By docking the control you want to track in the panel, it essentially wraps it and you'll get MouseEnter at the very edge of the tracked control:
private void panel1_MouseEnter(object sender, EventArgs e)
{
this.Text = "in";
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), panel1.Size).Contains(panel1.PointToClient(Control.MousePosition)))
this.Text = "out";
}
You're tracking entry into the panel surrounding the control, and exit from that panel provided the cursor isn't inside the tracked control.
To get a better "leave" experience, it's combined with a Timer that checks to see where the cursor is as well:
private void listBox3_MouseEnter(object sender, EventArgs e)
{
button1.Visible = true;
visibleTimer.Stop();
visibleTimer.Start();
}
void visibleTimer_Tick(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), listBox3.Size).Contains(listBox3.PointToClient(Control.MousePosition)))
{
visibleTimer.Stop();
button1.Visible = false;
}
}

Invalidate vs Update

I have code that lets be drag around a borderless form in winforms that I've been using for several months now, which works extremely well.
But when I first was given the code, they used this.Invalidate(); in the MouseMove event of the Form, and the Form flickered a little and was slow when dragging around. So, I replaced Invalidate() with Update() in the MouseMove event and, to my surprise, the Form can now be dragged very smoothly and has no flickering whatsoever.
Can somebody explain to me why Update makes the code work better than Invalidate, even when Invalidate sounds like it's the right one to be using?
Thanks :)
P.S. Maybe it would help more if I added the code... Adding it now.
Edit - Here's the code:
private void titlebar_MouseDown(object sender, MouseEventArgs e)
{
this.IsMouseDown = true;
this.LastCursorPosition = new Point(e.X, e.Y);
if (this.BackColor == Color.White)
{
this.BackColor = Color.GhostWhite;
tbox.BackColor = Color.GhostWhite;
tbox.ForeColor = Color.Black;
}
else
{
this.BackColor = Color.FromArgb(20, 20, 20);
tbox.BackColor = Color.FromArgb(20, 20, 20);
tbox.ForeColor = Color.White;
}
}
private void titlebar_MouseMove(object sender, MouseEventArgs e)
{
if (this.IsMouseDown == true)
{
//Move the form
this.Location = new Point(this.Left - (this.LastCursorPosition.X - e.X), this.Top - (this.LastCursorPosition.Y - e.Y));
// Update works better than Invalidate();.
Update();
}
}
private void titlebar_MouseUp(object sender, MouseEventArgs e)
{
this.IsMouseDown = false;
this.BackColor = fc;
tbox.BackColor = fc;
}
Invalidate() simply adds a region to the update region of the control. The next time WM_PAINT is received, the area you invalidated plus any other invalidated regions, are marked for painting. When RedrawWindow() is called, that will normally post a WM_PAINT message to the application queue. The system is free to do what it wants with that, usually more pressing business, and paint when it can.
If you call Update(), you get GDI+'s UpdateWindow() which won't mark a region for repainting, but pushes a WM_PAINT directly to WNDPROC(), bypassing the application queue.
If you need an immediate refresh of a control, use Refresh(), which invalidates the region then immediately calls Update().
Invalidate marks the window as needing to be refreshed (at some point). Update does it there and then if I remember correctly
Here is a link to explain the difference better than I'd be able to

Categories