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.
Related
This is hard to describe in words exactly what the problem is, so I have provided a gif of the issue to hopefully get it across.
link to image:
https://gyazo.com/00ed9cd3e1f2cf377f45e13737fcfc8e
If I disable the scrolling of the frame this does not happen, however this ListView needs to scroll for the entire program to function well.
I have thought about writing my own ListView like system using System.Drawing with panels but I decided to come here first to find a solution that might be less time consuming.
Is there any way I can stop the size from changing when items are moved against the boundaries of the form?
EDIT:
Here's the code for dragging an item:
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
ListViewItem item = e.Item as ListViewItem;
itemStartPos = item.Position;
mouseStartPos = Control.MousePosition;
this.DoDragDrop(item, DragDropEffects.Move);
}
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
ListViewItem item = e.Item as ListViewItem;
itemStartPos = item.Position;
mouseStartPos = Control.MousePosition;
this.DoDragDrop(item, DragDropEffects.Move);
}
private void listView1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(ListViewItem)) != null)
{
e.Effect = DragDropEffects.Move;
}
}
private void listView1_DragOver(object sender, DragEventArgs e)
{
ListViewItem item = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
if(item != null)
{
Point mousePos = Control.MousePosition;
Point itemPos = new Point(itemStartPos.X + mousePos.X - mouseStartPos.X, itemStartPos.Y + mousePos.Y - mouseStartPos.Y);
item.Position = itemPos;
}
}
private void listView1_DragDrop(object sender, DragEventArgs e)
{
//Save pos to registry for loading it back in
}
(hope this formats properly)
The user should be able to freely drag any item within the listview and drop it at the location the release the item at.
I've found an interesting fix to this issue that may only pertain to my specific program but I'll give this question closure.
When the ItemDrag event is called I set the scrollable propety to false, and when DragDrop is called it is set back to true. The reason I say this may only pertain to my program specifically is because when a user drags something the positions are saved to the registry and then subsequently reloaded after the scrollable property is set to true. You see, when the scrollable property is changed it redraws the entire listview so I had to do a bit of a funky work around.
I'm currently trying to get access to information that is passed in through an object sender.
The application I am working on is a winforms application with a list view. I want to get the number of the ListViewItem that the user has pressed on. The ListView item that I have pressed on is correct when I debug.
However I am unaware of how to get the information I want from the object sender. I want to access the ListViewItem number,
look at the posted image ListViewItem: {24919} in this case
so I can use this number as index, when I search in a database.
Does anyone have a fast tip so I can continue with my program ?
private void InvoiceListView_SelectedIndexChanged(object sender, EventArgs e)
{
//Connect to db and search based on the the listviewItemnumber.
}
Currently the object sender containsmethod;
You can type-check the sender and work with the result:
private void InvoiceListView_SelectedIndexChanged(object sender, EventArgs e)
{
if (!(sender is ListView listView)) return;
//work with the listView object from here:
listView.Items = ...
}
You can get the item selected by casting the sender to ListView and then get corresponding value as given below:
private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
ListView lw = (ListView)sender;
foreach(ListViewItem lvi in lw.SelectedItems )
{
MessageBox.Show(lvi.SubItems[0].Text);
}
}
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.
I'm trying to build a simple interface that allows users to drop files into a listBox to add them to a process, and to drag them out to remove them. Everything is working fine, but I'd like to add one feature to make it just a tad more sophisticated.
Right now, I have the removal of the item tied to the DragLeave event, which means that as soon as the mouse leaves the box, the item is removed. But I'd like for users to be able to change their minds. In other words, if they realize they're dragging the wrong file out, I'd like them to be able to move the mouse back into the listBox and release the mouse to cancel the action. I'm thinking that means I need to be able to capture the MouseUp event instead of the DragLeave event. But that hasn't been successful so far.
Below is the code I'm currently using for removing files dragged out. How can I modify to keep the files from being removed form the list until the user lets the mouse button go?
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
if (listBox1.Items.Count == 0)
{
return;
}
int index = listBox1.IndexFromPoint(e.X, e.Y);
string s = listBox1.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s, DragDropEffects.All);
}
private void listBox1_DragLeave(object sender, EventArgs e)
{
ListBox lb = sender as ListBox;
lb.Items.Remove(lb.SelectedItem);
}
Edit 2013/05/16
The comments and answers so far have been useful, but I realize my question isn't clear enough. In this case, I'm displaying a dialog separate from the parent form that is basically as big as the listBox. When someone drags a file out of the list, they're dragging it off the form completely. Have I backed myself into a corner by doing this? I recognize I'm making it harder than it has to be, but I'd still like to see how it would work if it's possible.
Here's a fairly quick hack approach to gaining the functionality you want:
public object lb_item = null;
private void listBox1_DragLeave(object sender, EventArgs e)
{
ListBox lb = sender as ListBox;
lb_item = lb.SelectedItem;
lb.Items.Remove(lb.SelectedItem);
}
private void listBox1_DragEnter(object sender, DragEventArgs e)
{
if (lb_item != null)
{
listBox1.Items.Add(lb_item);
lb_item = null;
}
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
lb_item = null;
if (listBox1.Items.Count == 0)
{
return;
}
int index = listBox1.IndexFromPoint(e.X, e.Y);
string s = listBox1.Items[index].ToString();
DragDropEffects dde1 = DoDragDrop(s, DragDropEffects.All);
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
lb_item = null;
}
Every time the user drags an item out of the box, it's saved temporarily until the user drops it somewhere else or mouses down on a new item in the list.
Note the important part of this is detecting when and where the user let's go of that mouse, which is the rationale behind handling the DragDrop event of Form1, the parent of listBox1.
Depending on the sophistication and density of the rest of your layout, where you handle DragDrop could be much different for you. This is why it's kind of "hacky", but it's also quite simple. It shouldn't matter, though, where or how many times you null lb_item since it pertains only to that specific ListBox.
I suppose another way to do it would be to track the user's mouse states and act accordingly, which may be more appropriate for you if it's inconceivable to handle a lot of DragDrop stuff.
EDIT: If you wanted to be REAL thorough, you could enumerate through every control of the base form using foreach and programmatically append a handler for the DragDrop event to that control, then remove it when done... but that may be getting a little nutty. I'm sure someone has a better approach.
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.