Forgive the rather poor code here.
I have a picture box PictureBox and a section Panel. I am trying to start a drag and drop operation so you click and drag the picture box, and dropping it into the Section creates a copy. This is fine.
But the problem is, lets say you click the picture box 9 times, no drag, just clicks. Then on the 10th turn, you do a correct drag and drop operation.... Then you get 10 duplicates added to the Section.
I am guessing that I need to respond to when a DragAndDrop is invalid and stop the drag and drop operation to prevent these duplicate buildups, but I do not know where to look to achieve this.
private void collectablePictureBox_MouseDown(object sender, MouseEventArgs e)
{
this.SelectedSection.AllowDrop = true;
this.SelectedSection.DragEnter += new DragEventHandler(this.CollectableSelectedSection_DragEnter);
this.SelectedSection.DragDrop += new DragEventHandler(this.CollectableSelectedSection_DragDrop);
this.collectablePictureBox.DoDragDrop(this.SelectedClassModel, DragDropEffects.Copy);
}
private void CollectablePictureBox_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
Console.WriteLine(e.Action);
}
private void CollectableSelectedSection_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void CollectableSelectedSection_DragDrop(object sender, DragEventArgs e)
{
Point position = this.SelectedSection.PointToClient(new Point(e.X, e.Y));
position.X -= this.SelectedClassModel.Width >> 1;
position.Y -= this.SelectedClassModel.Height >> 1;
this.SelectedSection.AllowDrop = false;
this.SelectedSection.DragEnter -= this.CollectableSelectedSection_DragEnter;
this.SelectedSection.DragDrop -= this.CollectableSelectedSection_DragDrop;
this.SelectedSection.AddItem(this.SelectedClassModel, position, this.SectionCanvasSnapToGridCheckBox.Checked);
}
Your problem comes from the fact that on every MouseDown event you add handlers to the appropriate events, so when you actually do the Drag and Drop those handlers will be called more than once. Currently I see two different ways of solving this:
One way to solve the problem would be to not start the Drag and Drop on the MouseDown event handler, but - based on this MSDN article - start it in a MouseMove handler instead, like this:
private void collectablePictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (sender != null && e.LeftButton == MouseButtonState.Pressed)
{
this.SelectedSection.AllowDrop = true;
this.SelectedSection.DragEnter += new DragEventHandler(this.CollectableSelectedSection_DragEnter);
this.SelectedSection.DragDrop += new DragEventHandler(this.CollectableSelectedSection_DragDrop);
this.collectablePictureBox.DoDragDrop(this.SelectedClassModel, DragDropEffects.Copy);
}
}
Another way would be to also handle the MouseUp event of the PictureBox, and do a similar cleanup as in your CollectableSelectedSection_DragDrop handler.
Related
I have a WPF datagrid where a user can drag and drop various rows to reorder them. However, I need an indicator that will display where the row will be inserted.
I've tried various 'hacky' ways of doing it, including detecting mouse move events and inserting a blank indicator row where the mouse is. Unfortunately, it seems that while dragging rows, none of my mouse move events are getting fired.
The UIElement class provides several events that you can use.
Those are:
DragEnter
DragOver
DragLeave
Drop
The first two are the one you can use.
<DataGrid DragOver="MyGrid_DragOver"
DragEnter="MyGrid_DragEnter"
I had to use both to get my drag and drop working.
I start the Drag operation by reacting to a move mouve event:
private void MyGrid_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var dragSource = ...;
var data = ...;
DragDrop.DoDragDrop(dragSource, data, DragDropEffects.Move);
}
}
Then you code the event handlers on the target control (your DataGrid):
private void MyGrid_DragOver(object sender, DragEventArgs e)
{
this.DragOver_DragEnter(sender, e);
}
private void MyGrid_DragEnter(object sender, DragEventArgs e)
{
this.DragOver_DragEnter(sender, e);
}
Then you can set the Effects propery to give a visual feedback to the user:
private void DragOver_DragEnter(object sender, DragEventArgs e)
{
// code here to decide whether drag target is ok
e.Effects = DragDropEffects.None;
e.Effects = DragDropEffects.Move;
e.Effects = DragDropEffects.Copy;
e.Handled = true;
return;
}
Check out this response on MSDN too: Giving drag and drop feedback using DragEventArgs.Effect
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);
}
}
A good morning to you all!
Yesterday I ran into a problem while trying to implement a custom DragDrop for my own controls in a WinForms application.
I have a form which can dynamically create instances of two of my own controls. These controls consist of some controls themselves, such as buttons, labels and listboxes/treeviews. The controls serve as a representation for a certain dataset. Now, we all know the class diagrams in VS. There you have these boxes representing classes. You can move the boxes around on the canvas by doing - what I would call - dragging them around, much like you would drag around files. To accomplish this with my own controls I have done the following:
public partial class MyControl: UserControl
{
private Control activeControl;
private void GeneralMouseDown(MouseEventArgs e)
{
activeControl = this;
previousLocation = e.Location;
Cursor = Cursors.Hand;
}
private void GeneralMouseMove(Control sender, MouseEventArgs e)
{
if (activeControl == null || activeControl != sender)
return;
var location = activeControl.Location;
location.Offset(e.Location.X - previousLocation.X, e.Location.Y - previousLocation.Y);
activeControl.Location = location;
}
private void GeneralMouseUp()
{
activeControl = null;
Cursor = Cursors.Default;
}
}
The controls on my control which I want to "grab" for dragging MyControl have their MouseDown-, MouseMove- and MouseUp-events pointing to these three methods. As a result I can move my control about on the form freely, just as I want to.
Here comes the tricky bit:
The datasets I have controls for can be in hierarchical dependencies, which means, one control represents detailling of a component of the other, which is why my controls have Listboxes or TreeViews. To establish such a hierarchical dependency I would very much like to DragDrop the lower-order-control on the listbox of my higher-order-control, causing data to be transfered.
I know how to set up my DragEnter and DragDrop methods for the listbox, as I have done so previously with files. Just for completeness:
private void lst_MyControl_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(MyControl)))
e.Effect = DragDropEffects.Move;
else e.Effect = DragDropEffects.None;
}
Here's the problem: As I am moving my control about (which gets repainted at every position, giving a very much wanted effect!), when I "drag" it over the target-listbox, the DragEnter-event does not get fired. I thought I could work around this problem by telling Windows "Hey, I'm, Dragging'n'Dropping here!", thus adding to my GeneralMouseDown-method:
this.DoDragDrop(this, DragDropEffects.Move);
This, on the one hand, gets the DragEnter-event to fire => Yeah! On the other hand is the moving-around-part only working after I release the mouse, causing the control to hang on the mousepointer forever => Anti-Yeah!
Here's the question: Is there a way, to have both actions at the same time? So that I can move my control around, seing it at every position as I do now and fire the DragEnter-event when I get to that area of the other control?
Moving your Control around interferes with the automatic DragDrop handling.
I'd recommend to staying with the normal DragDrop procedures, that is leaving all visuals to the system: It will display a cursor that indicates when a valid target is entered, then change to one that indicates the operation.
You need just 3 lines, no hassle and the user won't seen bulky controls moving around.
Here is a version where I drag a PictureBox onto a ListBox:
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
listBox1.Items.Add( e.Data.GetData(DataFormats.Text));
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
pictureBox1.DoDragDrop(pictureBox1.ImageLocation, DragDropEffects.Copy);
}
Obviously you will set up and receive your data in your own ways..
Edit:
Now, if on the other hand you need to move controls around to rearrange them, maybe you should give up on Drag&Drop to handle the additional data transfers and code this portion on your own as well. You could use the MouseEnter event of a receiving control..
After a bit of fiddeling I did it. I switched the level on which the dragging is handled.
First I need just the MouseDown-event
public Point GrabPoint;
private void GeneralMouseDown(MouseEventArgs e)
{
GrabPoint = e.Location;
this.DoDragDrop(this, DragDropEffects.Move);
}
I set the point where I grab the control and initiate a DragDrop. On my form I handle all the dragging:
private void frmMain_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(MyControl1)) || e.Data.GetDataPresent(typeof(MyControl2)) || e.Data.GetDataPresent(typeof(MyControl3)))
e.Effect = DragDropEffects.Move;
else e.Effect = DragDropEffects.None;
}
private void frmMain_DragOver(object sender, DragEventArgs e)
{
Point DragTarget = new Point(e.X, e.Y);
Point GrabPoint = new Point(0, 0);
if (e.Data.GetDataPresent(typeof(MyControl1)))
GrabPoint = ((MyControl1)e.Data.GetData(typeof(MyControl1))).GrabPoint;
else if (e.Data.GetDataPresent(typeof(MyControl2)))
GrabPoint = ((MyControl2)e.Data.GetData(typeof(MyControl2))).GrabPoint;
else if (e.Data.GetDataPresent(typeof(MyControl3)))
GrabPoint = ((MyControl3)e.Data.GetData(typeof(MyControl3))).GrabPoint;
DragTarget.X -= GrabPoint.X;
DragTarget.Y -= GrabPoint.Y;
DragTarget = this.PointToClient(DragTarget);
if (e.Data.GetDataPresent(typeof(MyControl1)))
((MyControl1)e.Data.GetData(typeof(MyControl1))).Location = DragTarget;
else if (e.Data.GetDataPresent(typeof(MyControl2)))
((MyControl2)e.Data.GetData(typeof(MyControl2))).Location = DragTarget;
else if (e.Data.GetDataPresent(typeof(MyControl3)))
((MyControl3)e.Data.GetData(typeof(MyControl3))).Location = DragTarget;
}
At the moment I don't need the DragDrop-event, since nothing should happen when any control is dropped on the form. This way I always paint my control while it is being dragged => Yeah!
The next part is easy: Since I am really dragging the control, this bit of code does DragDrop-handling on my listbox Edit: ListView:
private void lst_SubControls_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(MyControl2)))
e.Effect = DragDropEffects.Move;
else e.Effect = DragDropEffects.None;
}
private void lst_SubControls_DragDrop(object sender, DragEventArgs e)
{
lst_SubControls.Items.Add(((MyControl2)e.Data.GetData(typeof(MyControl2))).SpecificDrive);
((MyControl2)e.Data.GetData(typeof(MyControl2))).DeleteThisControl();
}
This results in an entry added to the list and deletion of the dragged control. At this point there could be a check, wether the ctrl-key is pressed to copy the contents and not to delete the control.
I'm confused. I am basically trying to tell when the user has clicked something in the listbox, held the button, and left the listbox. Here is a somewhat dumbed down version of what I am doing:
private bool itemHeld;
private void listOriginal_MouseDown(object sender, MouseEventArgs e)
{
itemHeld = true;
}
private void listOriginal_MouseUp(object sender, MouseEventArgs e)
{
itemHeld = false;
}
private void listOriginal_MouseLeave(object sender, EventArgs e)
{
if (itemHeld)
MessageBox.Show("OHH YEAH");
}
To me that seems like it should turn itemHeld true when you press the mousebutton, turn it false only if you lift it, and display ohh yeah if the value is true. If I break on the mouse down event to check the value, it is true and if I continue from there it displays the message. If I do not break, it does nothing. Is there something else at work here?
Edit:
Brief description: It would be difficult to explain what I am really trying to accomplish but imagine something almost like dragging a file off of a window. I need to simply be able to recognize when the user clicks inside of the listbox and then drags out of the listbox if that makes sense
You can not debug windows events by break point because when the Visual Studio get active to debug, the mouse leave event will be fired for the hovered control.
You can use Debug.WriteLine which writes information about the debug to the trace listeners.
private void button1_MouseLeave(object sender, EventArgs e)
{
Debug.WriteLine("Mouse leave");
}
private void button1_MouseEnter(object sender, EventArgs e)
{
Debug.WriteLine("Mouse enter");
}
private void button1_MouseHover(object sender, EventArgs e)
{
Debug.WriteLine("Mouse hover");
}
what about this?
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.X > listBox1.Width - 1 || e.Y > listBox1.Height - 1 || e.X < 0 || e.Y < 0)
{
Console.WriteLine("drag out");
}
else
Console.WriteLine("mouse move {0}/{1}", e.X, e.Y);
}
it uses the fact that the Control is not left before the mousebutton is released ... but be aware that the drag out part will occur more than once so you probably will want to have a flag set the first time ... and have that flag cleared on mouse up or leave
For every mouse click, your MouseDown event will fire AND your MouseUp event will fire, so the sequence of operations is equivalent to
itemHeld = true;
itemHeld = false;
if(itemHeld)
MessageBox.Show("yay");
If you press the mouse button on the listbox and move the cursor out without releasing the button, switching focus to another window (e.g. Visual Studio) is what triggers the MouseLeave event to fire. This is why you're seeing the message box pop up when you're debugging.
I'm not sure what you're trying to accomplish, so I can't recommend another solution.
After I added drag & drop to a DataGridView, the CellDoubleClick event stopped working. In the CellMouseDown event I have the following code:
private void dataGridView2_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
var obj = dataGridView2.CurrentRow.DataBoundItem;
DoDragDrop(obj, DragDropEffects.Link);
}
How do I correct this to enable CellDoubleClick event?
Yes, that cannot work. Calling DoDragDrop() turns mouse control over to the Windows D+D logic, that's going to interfere with normal mouse handling. You need to delay starting the D+D until you see the user actually dragging. This ought to solve the problem:
Point dragStart;
private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) {
if (e.Button == MouseButtons.Left) dragStart = e.Location;
}
private void dataGridView1_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
var min = SystemInformation.DoubleClickSize;
if (Math.Abs(e.X - dragStart.X) >= min.Width ||
Math.Abs(e.Y - dragStart.Y) >= min.Height) {
// Call DoDragDrop
//...
}
}
}