How to implement Drag and Drop - c#

I am working on a project. It does with drag and drop this is the code I have right now.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
listBox1.DoDragDrop(listBox1.SelectedItem.ToString(), DragDropEffects.Move);
}
private void listBox2_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
listBox2.Items.Add(e.Data.GetData(DataFormats.Text));
listBox1.Items.Remove(listBox1.SelectedItem.ToString());
}
It lets you add to the second list box but I am trying to get it where you can also move the item back to first listbox if you want to. Do I repeat the code for the second list box as I did for the first one or is there a line of code I could just add.Also how can you tell if your program is “unbreakable”. Thanks.

Do I repeat the code for the second list box
Pretty much, yeah. Although you can simplify it a bit since the code will be essentially identical by having both listboxes use the same handler for MouseDown, DragEnter and DragDrop and then use the sender to figure out whether it's listBox1 or listBox2.
Also, you might want to think about your MouseDown handler. Most users won't expect a single click to immediately start a drag operation. Usually you would look for the mouse down and then for a mouse move while the button is down before starting the drag.
What I usually do is something like this:
private Size dragSize = SystemInformation.DragSize;
private Rectangle dragBounds = Rectangle.Empty;
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragBounds = new Rectangle(new Point(e.X - dragSize.Width / 2, e.Y - dragSize.Height/2), dragSize);
}
else
{
dragBounds = Rectangle.Empty;
}
}
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && dragBounds != Rectangle.Empty && !dragBounds.Contains(e.X, e.Y))
{
//start drag
listBox1.DoDragDrop(listBox1.SelectedItem.ToString(), DragDropEffects.Move);
dragBounds = Rectangle.Empty;
}
}

For the main question of implementing drag & drop: yes, you would need to create handlers for listbox1 and listbox2 that mirror the functionality you already have:
A MouseDown event handler for listBox2
A DragEnter handler for listBox1
A DragDrop handler for listBox1.
Also you'd need to make sure you assign these handlers to be used for their respective events in the form designer.

You could reapeat the code, but I tend to not want to do that. Yours is an edge case; lots of methods that only have one line in them. But any time I see repetition in code, it signals to me that I need to pull that code out somewhere else. If the repetition is in the same class, move it to its own method on the class. If the repetition is in separate classes, either find another class outside the two where it makes sense to put the new method, or consider creating a new class that both classes can share. In your case, if I decided to move the code, I would do something like this:
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(listbox1);
}
private void listBox2_DragEnter(object sender, DragEventArgs e)
{
HandleDragEnter( e );
}
private void listBox2_DragDrop(object sender, DragEventArgs e)
{
HandleDragDrop( listBox1, listBox2, e );
}
private void listBox2_MouseDown(object sender, MouseEventArgs e)
{
HandleMouseDown(listBox2);
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
HandleDragEnter( e );
}
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
HandleDragDrop( listBox2, listBox1, e );
}
private void HandleMouseDown( ListBox listBox )
{
listBox.DoDragDrop(listBox.SelectedItem.ToString(), DragDropEffects.Move);
}
private void HandleDragEnter( DragEventArgs e )
{
e.Effect = e.AllowedEffect;
}
private void HandleDragDrop( ListBox src, ListBox dst, DragEventArgs e )
{
dst.Items.Add( e.Data.GetData(DataFormats.Text) );
src.Items.Remove( src.SelectedItem.ToString() );
}
The advantage to moving the code is, if those methods grow to more than one line, you can change them in only one place. Of course, for a one line method, I would also remember that I can always move it to its own method later. My personal preference would be to leave the two one-line methods as-is, doing a copy & paste for the second listbox, and splitting out the DragDrop handler into its own method like I did above.

Related

how to detect drag begins in wpf

I know from this question how to handle a drag&Drop
https://stackoverflow.com/a/17872857/982161
but I can not detect when the Drag event begins so I can prepare some resources...
if I print those events the Drop is coming first and after that the Drag..
how can this be cleanly handled
My Code is pretty simple
private void Label_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var lbl = (Label)sender;
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Move);
Console.WriteLine("Drag...");
}
private void Label_Drop(object sender, DragEventArgs e)
{
Console.WriteLine("Drop...");
}
private void Label_DragEnter(object sender, DragEventArgs e)
{
Console.WriteLine("Label_DragEnter...");
}
private void Label_DragLeave(object sender, DragEventArgs e)
{
Console.WriteLine("Label_DragLeave...");
}
Long story short: If you want to prepare resources before you drop the label, write that code before calling the DragDrop method or in the OnPreviewMouseDown event.
Long Story:
Using Snoop I was able to look into the events that are triggering when dragging the label.
It appears that the only events triggering are the PreviewMouseDown and MouseDown.
So we should only implement those events.
private void Lbl_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var lbl = (Label)sender;
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Move);
Console.WriteLine("Drag...");
}
private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Label_PreviewMouseDown...");
}
This will result in first printing "Label_PreviewMouseDown..." when starting to drag the label and "Drag..." when the label is done being dragged.
However, this isn't the complete truth.
Let's modify our code a little. Let's add DateTime.Now.Second to test when the messages are actually triggering. I will then drag the label for a few seconds, then drop it to see the order of printing to console.
private void Lbl_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var lbl = (Label)sender;
Console.WriteLine("Label_OnMouseDown_BeforeDragging..." + DateTime.Now.Second);
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Move);
Console.WriteLine("Label_OnMouseDown_AfterDragging..." + DateTime.Now.Second);
}
private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Label_PreviewMouseDown..." + DateTime.Now.Second);
}
Now let's try dragging again.
Turns out that OnMouseDown happens before you are done dragging. The DoDragDrop method pauses the code there until you drop the label, then you are able to continue and print to the console.
So therefore: If you want to prepare resources before you drop the label, write that code before calling the DragDrop method or in the OnPreviewMouseDown event.
Hope this helps.
If you need a drag event that is not provided by the control then you may consider adding one to your own code. For example
// Event fired immediately before DragDrop
public DragEventHandler DragBegin { get; set; }
private void Label_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var lbl = (Label)sender;
// Create args object and fire event if not null
var args = new DragEventArgs(new DataObject(lbl.Content), DragDropKeyStates.None, DragDropEffects.None, lbl, e.GetPoint(lbl));
DragBegin?.Invoke(sender, args);
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Move);
Console.WriteLine("Drag...");
}
You could then bind to that event from anywhere you have a reference to that code behind, such as
MyUserControl.DragBegin += (sender, args) => /* some behavior */;

C# DataGridView Drag and Drop behaviour? [duplicate]

This question already has an answer here:
make delayed mousedown event
(1 answer)
Closed 6 years ago.
I have a question about the drag and drop behaviour of a windows forms datagridview.
In my scenario a user has to drag single rows from a datagridview and drop it in a listview.
When I start the application this works fine but after I dropped one row i have to click twice onto the datagridview and then I have to click a third time to do a drag and drop operation.
This is not userfriendly.
How can I achieve that after a drag n drop operation it will be possible to do the next drag and drop without having to click onto the datagridview? (click and hold mousebutton to drop in a single click after the last drag and drop)
Here is a sample code:
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
dataGridView1.DoDragDrop(dataGridView1.SelectedRows, DragDropEffects.Move);
dataGridView1.Focus();
dataGridView1.CurrentCell = dataGridView1.SelectedRows[0].Cells[0];
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listBox1_DragDrop(object sender, DragEventArgs e)
{
MessageBox.Show(dataGridView1.SelectedRows[0].Cells[0].Value.ToString());
}
I already tried to fix this with datagridview1.Focus() but it doesn't help.
Further info:
Datagridview is set to readonly, multiselect disabled and fullrowselection.
Thank you!
Instead of using MouseDown which is used for basic functionality of the DataGridView you should rather use MouseMove.
private void lvDrop_DragDrop(object sender, DragEventArgs e)
{
var rows = e.Data.GetData(typeof(DataGridViewSelectedRowCollection))
as DataGridViewSelectedRowCollection;
if(rows.Count > 0)
{
foreach(DataGridViewRow row in rows)
{
lvDrop.Items.Add(row.Cells[0].Value.ToString());
}
}
}
private void lvDrop_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dgvDrag_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
dgvDrag.DoDragDrop(dgvDrag.SelectedRows, DragDropEffects.Move);
}
}
Results in this behaviour:
ToDo's
Care for invalid incoming drops.

C# winforms. Drag events

I need to run code when I dismiss(end) dragging click. How this event is called?
Here is main example, please find the below screenshot for more information:
I made that I can drag the car on other picture boxes like below this:
Repeat again - I need to know what EVENT is then you dismiss to drag on picture box?
There is no event for when the drag is released on a control, but you don't really need one. This is what I did to simulate what (I think) you're looking for. I used code courtesy of this stackoverflow answer
private Point? _mouseLocation;
private void Form1_Load(object sender, EventArgs e)
{
this.pictureBox1.MouseDown += this.pictureBox1_MouseDown;
this.pictureBox1.MouseUp += this.pictureBox1_MouseUp;
this.pictureBox1.MouseMove += this.pictureBox1_MouseMove;
}
void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if ( this._mouseLocation.HasValue)
{
this.pictureBox1.Left = e.X + this.pictureBox1.Left - this._mouseLocation.Value.X;
this.pictureBox1.Top = e.Y + this.pictureBox1.Top - this._mouseLocation.Value.Y;
}
}
void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
this._mouseLocation = null;
}
void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
//Check if you've left-clicked if you want
this._mouseLocation = e.Location;
}
Setting the mouse location to null with this._mouseLocation = null; is your "drag released" code.
I guess you talking about DragDrop. You can find example here: How to: Enable Drag-and-Drop Operations with the Windows Forms RichTextBox Control

Listviews And Drag and Drop in C#

I would like to use drag and drop between two Listviews (AllListView and PreListView). This is how far I did get:
In the function where the AllListView is filled with Items, I use something like that to assosiate the myCustomDataObject to the single ListviewItem:
ListViewItem newItem = new ListViewItem();
newItem.Text = myCustomDataObject.getName();
newItem.Tag = myCustomDataObject;
lst_All.Items.Add(newItem);
There are my Eventhandler for the two Listviews:
AllListView:
private void OnAllDragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.All;
// How Do I add my CustomDataObject?
}
private void OnAllItemDrag(object sender, ItemDragEventArgs e)
{
base.DoDragDrop(lst_All.SelectedItems[0], DragDropEffects.Move);
// Do I have to Do something to pass my CustomDataObject?
}
PreListView:
private void OnPreDragEnter(object sender, DragEventArgs e)
{
//If there one of myCustomDataObject go on
e.Effect = DragDropEffects.Move;
}
private void OnPreDragDrop(object sender, DragEventArgs e)
{
// Get Here myCustomDataObject to generate the new Item
lst_Pre.Items.Add("Done...");
}
So my question is, how to achieve that myCustomDataObject is found in “OnPreDragDrop”. I have tried many versions of e.Data.Getdata() and e.Data.Setdata(), but I did not got very far.
You are dragging an object of type ListViewItem. So you first want to check that the dragged item is indeed of that type. And you probably want to make sure it is a happy kind of item, one that has the proper Tag value. Thus:
private void OnPreDragEnter(object sender, DragEventArgs e) {
if (e.Data.GetDataPresent(typeof(ListViewItem))) {
var item = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
if (item.Tag is CustomDataObject) {
e.Effect = DragDropEffects.Move;
}
}
}
In the Drop event, you actually want to implement the logical "Move" operation, removing the item from the source ListView and adding it to the destination ListView. No need for checks anymore, you already performed them in your DragEnter event handler. Thus:
private void OnPreDragDrop(object sender, DragEventArgs e) {
var item = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
item.ListView.Items.Remove(item);
lst_Pre.Items.Add(item);
}
Note that you probably thought for a minute that the mistake was to drag the ListViewItem instead of the CustomDataObject. It was not, dragging ListViewItem made it easy to remove the item from the source ListView.
List view normally doesn't have drag and drop facility. But you can make it to do drag and drop with some changes with some extra code. Here is a link to help your problem. I hope you'll get something from it.
http://support.microsoft.com/kb/822483
When you call DoDragDrop, you are assigning the data. Make that your custom data object instead of the ListViewItem.
If you need the ListViewItem, then add a reference to it to your custom data class.

Enhanced features with Drag and Drop from Treeview to Textbox in WinForms

I have the following code that successfully drags and drops a treeview node to a textbox in a WFA:
private void _MyTreeView_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item.ToString(), DragDropEffects.Copy);
}
private void textBox1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
textBox1.Text += (System.String)e.Data.GetData(typeof(System.String));
}
}
but I wanted to be able to do a few other things with the drag and drop.
(1) If the node I'm dragging to the textbox has a Text property value of 'MyTreeNode', then the value that appears in the textbox is 'TreeNode: MyTreeNode' and not 'MyTreeNode' i.e. it adds 'TreeNode: ' at the start. How can I fix this?
(2) Is there someway I can prevent all TreeNodes at the root level from being dragged and dropped?
(3) With the code I have above, when I drag and drop a treenode the treenode text is appended to the end of the text that is already in the textbox. Am I able to add a 'drop cursor'(i dont know what you would call it) and have the treenode dropped at the position its actually dropped i.e. the position of the cursor?
The TreeNode.ToString() method doesn't do what you hope it does, you'll have to use the TreeNode.Text property explicitly. Combining 1) and 2):
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) {
var node = (TreeNode)e.Item;
if (node.Level > 0) {
DoDragDrop(node.Text, DragDropEffects.Copy);
}
}
Your DragEnter event handler is too permissive, you allow anything to be dragged to the TextBox. But you only can handle a string, so check for that:
private void textBox1_DragEnter(object sender, DragEventArgs e) {
if (e.Data.GetDataPresent(typeof(string))) e.Effect = DragDropEffects.Copy;
}
And you avoid the concatenation by using a simple assignment instead of +=
private void textBox1_DragDrop(object sender, DragEventArgs e) {
textBox1.Text = (string)e.Data.GetData(typeof(string));
}
Do consider a Label instead of a TextBox if the user should not edit the text himself.

Categories

Resources