Consider this is a ListView that shows files and folders, I have already wrote code for copy/move/rename/show properties ...etc and I just need one more last thing. how to drag and drop in the same ListView like in Windows Explorer, I have move and copy functions, and I just need to get the items which user drops in some folder or in other way I need to get these two parameters to call copy function
void copy(ListViewItem [] droppedItems, string destination path)
{
// Copy target to destination
}
Start by setting the list view's AllowDrop property to true. Implementing the ItemDrag event to detect the start of a drag. I'll use a private variable to ensure that D+D only works inside of the control:
bool privateDrag;
private void listView1_ItemDrag(object sender, ItemDragEventArgs e) {
privateDrag = true;
DoDragDrop(e.Item, DragDropEffects.Copy);
privateDrag = false;
}
Next you'll need the DragEnter event, it will fire immediately:
private void listView1_DragEnter(object sender, DragEventArgs e) {
if (privateDrag) e.Effect = e.AllowedEffect;
}
Next you'll want to be selective about what item the user can drop on. That requires the DragOver event and checking which item is being hovered. You'll need to distinguish items that represent a folder from regular 'file' items. One way you can do so is by using the ListViewItem.Tag property. You could for example set it to the path of the folder. Making this code work:
private void listView1_DragOver(object sender, DragEventArgs e) {
var pos = listView1.PointToClient(new Point(e.X, e.Y));
var hit = listView1.HitTest(pos);
if (hit.Item != null && hit.Item.Tag != null) {
var dragItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
copy(dragItem, (string)hit.Item.Tag);
}
}
If you want to support dragging multiple items then make your drag object the ListView.SelectedIndices property.
Related
I have a ListBox with a list of filepaths, which has the property SelectionMode set to MultiExtended. Thus, I can select many items from this list.
Now, I want to Drag and Drop files starting from those paths in the destination folder where I drop them.
My code:
private void Form1_Load(object sender, EventArgs e)
{
// populate the FileList
// i.e. FileList.Items.Add("d:/Samples/file-00" + i + ".wav");
this.FileList.MouseDown += new MouseEventHandler(FileList_MouseDown);
this.FileList.DragOver += new DragEventHandler(FileList_DragOver);
}
void FileList_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
void FileList_MouseDown(object sender, MouseEventArgs e)
{
List<string> filesToDrag = new List<string>();
foreach (var item in FileList.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
this.FileList.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
it works perfect if I select and drop 1 single line/file from the ListBox to the destination folder.
Instead, if I do a multiple selection and I try to drag and drop, it can just select that one line where I start to drag. Seems that MouseDown prevent this?
How would you fix the problem?
Doing this with a ListBox seems to be ridiculously hard. Actually I haven't found a solution at all..
Use a ListView instead! It is easy as pie, using the ItemDrag event and is a much better control anyway.. I can't count how often I had to change from a 'cheap' ListBox to ListView because I needed this or that 'little' extra..
Here is your code moved to a ItemDrag:
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
List<string> filesToDrag = new List<string>();
foreach (var item in listView1.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
this.listView1.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
Note that this only solves the problem of the MouseDown changing the selection. It is in itself not a guarantuee that the actual copying will work.
I found this interesting article that proposes a solution. Maybe you don't need it, as you have said that you got copying one file already working..
Yeah...I don't think there's a good way around that problem. When you click again to initiate the drag it will toggle that item. We don't know that the user actually wants to do a drag until the mouse is held down and moved.
One potential solution is to initiate the drag/drop from something else, just somehow make it really clear that is what the user should drag. Here I'm using a Label instead of the ListBox:
void label1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
List<string> filesToDrag = new List<string>();
foreach (var item in FileList.SelectedItems)
{
filesToDrag.Add(item.ToString().Trim());
}
if (filesToDrag.Count > 0)
{
this.FileList.DoDragDrop(new DataObject(DataFormats.FileDrop,
filesToDrag.ToArray()), DragDropEffects.Copy);
}
else
{
MessageBox.Show("Select Files First!");
}
}
}
You have to be picky about the mouse down and mouse move activities. When it is within the graphics rectangle of your listbox, you'll want normal behavior. When it is outside the bounds of this rectangle, you'll want drag/drop functionality. You can try the pseudo code below:
MouseDown(sender, e)
{
var x = <your sender as control>.ItemFromPoint(.....)
this.mouseLocation = x == null ? x : e.Location;
}
MouseMove(sender, e)
{
if control rectangle doesn't contain the current location then
<your sender as control>.Capture = false
DoDragDrop
}
Is there any good examples/tutorials on how to implement drag and drop within a window 8 C# list (listview, listbox …) out there?
What I would like Is a editable “Iphone-list”-experience, where I easily can rearrange items within a list. But I mostly find WinJS examples and I would like to have a c# example for win 8
Firstly You must enable the AllowDragDrop property.
Then write 3 events:
private void myList_ItemDrag(object sender, ItemDragEventArgs e)
{
DoDragDrop(e.Item, DragDropEffects.Link);
}
private void myList_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Link;
}
private void myList_DragDrop(object sender, DragEventArgs e)
{
// do whatever you need to reorder the list.
}
To get index of dropped item:
Point cp = myList.PointToClient(new Point(e.X, e.Y));
ListViewItem dragToItem = myList.GetItemAt(cp.X, cp.Y);
int dropIndex = dragToItem.Index;
If you need to drop onto a ListView or GridView, have the Drop event fire on the DataTemplate for the actual Item, not the whole list. Then you can tell which item it is dropped on.
I have an WPF-Datagrid where I can drop an element. This is a Textelement dropped from an .txt file (open for example with notepad++). Is there any possibility to get information about the .txt file on my Drop-Event ?
Edit:
void OnDragDrop(object sender, DragEventArgs e)
{
String text = e.Data.GetData(DataFormats.Text, true);
}
Here I can get the Text of my drop element, but I found no solution to get the source file, where the drag get started.
ok here you go:
You have to do three things to enable drag'n drop in WPF:
Tell the element to support drag'n drop
Setup a DragOver Event
Setup a Drop Event
ok, let's first look at the XAML:
<DataGrid AllowDrop="True"
DragOver="DataGrid_DragOver"
Drop="DataGrid_Drop"/>
And the event handler code:
private void DataGrid_DragOver(object sender, DragEventArgs e)
{
// check if the element dragged over is one or more files
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// if so, show a link cursor
e.Effects = DragDropEffects.Link;
}
else
{
// otherwise show a "block" cursor
e.Effects = DragDropEffects.None;
}
// IMPORTANT: mark the event as "handled by us", to apply the drag effects
e.Handled = true;
}
private void DataGrid_Drop(object sender, DragEventArgs e)
{
// Check if the data dropped is one or more files
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// get the file pathes from the data object
string[] filePaths = (e.Data.GetData(DataFormats.FileDrop) as string[]);
// do something with the pathes
/* ... */
}
}
For more information see the MSDN documentation.
No.
Think of Drag & Drop the same way as Cut & Paste -- typically only the "data" is being dragged over during the event, and no additional metadata about its source.
One exception to this, is when dragging text from a web page. DataFormats.Html will include the SourceURL that the text came from.
I'm creating listviews in a flowpanel at run time which later will accept drag and dropped files. the reason being is i want these to act as folders so a user double clicks and gets a window displaying the contents.
i'm having difficulty setting up the events for my listviews as they are added.
how do i create some events (like MouseDoubleClick and DragDrop) dynamically for each added listview? can i create a single function for both of these events and have listview1, listview2, listviewX use it?
i have a button that is adding the listviews, which works fine. please advise, i apologize if this is too conceptual and not exact enough.
private void addNewWOButton_Click(object sender, EventArgs e)
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
flowPanel.Controls.Add(newListView);
}
You would have to have the routine already created in your code:
private void listView_DragDrop(object sender, DragEventArgs e) {
// do stuff
}
private void listView_DragEnter(object sender, DragEventArgs e) {
// do stuff
}
and then in your routine, your wire it up:
private void addNewWOButton_Click(object sender, EventArgs e)
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
newListView.DragDrop += listView_DragDrop;
newListView.DragEnter += listView_DragEnter;
flowPanel.Controls.Add(newListView);
}
You would have to check who the "sender" is if you need to know which ListView control is firing the event.
You can also just use a lambda function for simple things:
newListView.DragEnter += (s, de) => de.Effect = DragDropEffects.Copy;
Just make sure to unwire the events with -= if you also remove the ListViews dynamically.
To answer the other half of your question, you can use a single handler for any event, from any source, that has the handler's signature. In the body of the handler, you just have to check the sender argument to determine which control raised the event.
You need a way to tell one control from a different one of the same class, however. One way to do this is to make sure to set the Name property on each control when you create it; e.g., newListView.Name = "FilesListView".
Then, before you do anything else in your event handler, check the sender.
private void listView_DragDrop(object sender, DragEventArgs e) {
ListView sendingListView = sender as ListView;
if(sendingListView == null) {
// Sender wasn't a ListView. (But bear in mind it could be any class of
// control that you've wired to this handler, so check those classes if
// need be.)
return;
}
switch(sendingListView.Name) {
case "FilesListView":
// do stuff for a dropped file
break;
case "TextListView":
// do stuff for dropped text
break;
.....
}
}
If it's not possible, I can also use 2 TreeView controls. I just won't have a hierarchy in the second TreeView control. It's gonna act like some sort of repository.
Any code sample or tutorial would be very helpful.
ListView does not support drag-and-drop naturally, but you can enable it with a small bit of code:
http://support.microsoft.com/kb/822483
Here's an example that specifically does drag-and-drop from a ListView to a TreeView (it's an Experts Exchange link, so just wait a few seconds and then scroll to the bottom, where you'll find the answers):
http://www.experts-exchange.com/Programming/Languages/.NET/Visual_CSharp/Q_22675010.html
Update: Code from the link:
Create a listview and a treeview. ( In my example, the listview is called listView1 and the treeview is called tvMain )
On the treeview, set AllowDrop to true.
Create an ItemDrag event on the listview
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
listView1.DoDragDrop(listView1.SelectedItems, DragDropEffects.Copy);
}
In this example items from the listview are copied to the 'drop' object.
Now, create a DragEnter event on the treeview:
private void tvMain_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
This was easy. Now the hard part starts. The following code adds the selected (and dragged) listview items to an existing node (make sure you have at least one node already in your treeview or the example will fail!)
Create a DragDrop event on the treeview:
private void tvMain_DragDrop(object sender, DragEventArgs e)
{
TreeNode n;
if (e.Data.GetDataPresent("System.Windows.Forms.ListView+SelectedListViewItemCollection", false))
{
Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
TreeNode dn = ((TreeView)sender).GetNodeAt(pt);
ListView.SelectedListViewItemCollection lvi = (ListView.SelectedListViewItemCollection)e.Data.GetData("System.Windows.Forms.ListView+SelectedListViewItemCollection");
foreach (ListViewItem item in lvi)
{
n = new TreeNode(item.Text);
n.Tag = item;
dn.Nodes.Add((TreeNode)n.Clone());
dn.Expand();
n.Remove();
}
}
}
To change the cursor while dragging, you have to create a GiveFeedback event for the ListView control:
private void listView1_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = false;
if (e.Effect == DragDropEffects.Copy)
{
Cursor.Current = new Cursor(#"myfile.ico");
}
}
myfile.ico should be in the same directory as the .exe file.
This is just a simple example. You can extend it any way you like.