Results 1 to 10 of 10

Thread: Drag & Drop in Windows Forms

  1. #1

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Drag & Drop in Windows Forms

    VB version here.

    I'm sure others have posted about this topic before but I'm sure another won't hurt. I'll post various examples of drag & drop within the same control, between controls and between applications.

    If you're interested in performing drag & drop operations in WinForms then I suggest that you read this and this at least.

    I'll start with moving items between two ListBoxes, which is relatively simple.

    1. Create a new WinForms application project.
    2. Add two ListBoxes to the form.
    3. Replace the existing form code with the following:
    Code:
    public Form1()
    {
        InitializeComponent();
    
        // Hook up the form event handler.
        this.Load += new EventHandler(Form1_Load);
    }
    
    private void Form1_Load(object sender, EventArgs e)
    {
        // Allow data to be dropped on both ListBoxes.
        this.listBox1.AllowDrop = true;
        this.listBox2.AllowDrop = true;
    
        // Populate the ListBoxes.
        this.listBox1.Items.AddRange(new string[] {"List 1, Item 1",
                                                   "List 1, Item 2",
                                                   "List 1, Item 3",
                                                   "List 1, Item 4",
                                                   "List 1, Item 5"});
        this.listBox2.Items.AddRange(new string[] {"List 2, Item 1",
                                                   "List 2, Item 2",
                                                   "List 2, Item 3",
                                                   "List 2, Item 4",
                                                   "List 2, Item 5"});
    
        // Hook up the ListBox event handlers.
        this.listBox1.MouseDown += new MouseEventHandler(ListBox_MouseDown);
        this.listBox2.MouseDown += new MouseEventHandler(ListBox_MouseDown);
        this.listBox1.DragEnter += new DragEventHandler(ListBox_DragEnter);
        this.listBox2.DragEnter += new DragEventHandler(ListBox_DragEnter);
        this.listBox1.DragDrop += new DragEventHandler(ListBox_DragDrop);
        this.listBox2.DragDrop += new DragEventHandler(ListBox_DragDrop);
    }
    
    void ListBox_MouseDown(object sender, MouseEventArgs e)
    {
        ListBox source = (ListBox)sender;
    
        for (int index = 0; index < source.Items.Count; index++)
        {
            // Test whether the mouse location is within an item
            if (source.GetItemRectangle(index).Contains(e.Location))
            {
                // The mouse was depressed on an item so allow a move operation.
                source.DoDragDrop(source, DragDropEffects.Move);
    
                break;
            }
        }
    }
    
    void ListBox_DragEnter(object sender, DragEventArgs e)
    {
        ListBox source = (ListBox)sender;
    
        if (e.Data.GetDataPresent("System.Windows.Forms.ListBox", false) &&
            e.Data.GetData("System.Windows.Forms.ListBox", false) != source)
        {
            // The data being dragged is a ListBox but not the one that was just entered.
            e.Effect = DragDropEffects.Move;
        }
    }
    
    void ListBox_DragDrop(object sender, DragEventArgs e)
    {
        ListBox source = (ListBox)sender;
    
        if (e.Data.GetDataPresent("System.Windows.Forms.ListBox", false))
        {
            // Get the ListBox that the data was dragged from.
            ListBox data = (ListBox)e.Data.GetData("System.Windows.Forms.ListBox", false);
    
    
            // Make sure we aren't trying to drag from and drop to the same control.
            if (data != source)
            {
                // Get the item that was dragged.
                object item = data.SelectedItem;
    
                // Remove the item from its original location.
                data.Items.Remove(item);
    
                // Get the current mouse location relative to the control being dropped on.
                Point location = source.PointToClient(new Point(e.X, e.Y));
                int dropIndex = -1;
    
                // Find the item over which the mouse was released.
                for (int index = 0; index < source.Items.Count; index++)
                {
                    if (source.GetItemRectangle(index).Contains(location))
                    {
                        dropIndex = index;
    
                        break;
                    }
                }
    
                if (dropIndex == -1)
                {
                    // The mouse was not released over an item so add the new item to the end.
                    source.Items.Add(item);
                }
                else
                {
                    // Insert the new item above the item it was dropped on.
                    source.Items.Insert(dropIndex, item);
                }
            }
        }
    }
    4. Run the project and start dragging and dropping.

    I'll refine this example and add more over time. If there's anything you specifically want to see then post a request and I'll see if I can get to it.

  2. #2

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Dragging Files From Windows Explorer

    This example demonstrates dragging one or more files from Windows Explorer, or some other application that supports dragging files, into a ListView.

    1. Create a new WinForms project.
    2. Add a ListView and an ImageList to the form.
    3. Replace the contents of the form with the following code:
    Code:
    public Form1()
    {
        InitializeComponent();
    
        this.Load += new EventHandler(Form1_Load);
    }
    
    void Form1_Load(object sender, EventArgs e)
    {
        // Initialise the ListView.
        this.listView1.AllowDrop = true;
        this.listView1.Columns.Add("File name");
        this.listView1.Dock = DockStyle.Fill;
        this.listView1.SmallImageList = this.imageList1;
        this.listView1.View = View.Details;
    
        this.listView1.DragEnter += new DragEventHandler(listView1_DragEnter);
        this.listView1.DragDrop += new DragEventHandler(listView1_DragDrop);
    }
    
    void listView1_DragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent("FileDrop") &&
            (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
        {
            // A file list is being dragged and it can be copied so provide feedback to the user.
            e.Effect = DragDropEffects.Copy;
        }
    }
    
    void listView1_DragDrop(object sender, DragEventArgs e)
    {
        // The data can only be dropped if it is a file list and it can be copied.
        if (e.Data.GetDataPresent("FileDrop") &&
            (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
        {
            // Get the data.
            string[] filePaths = (string[])e.Data.GetData("FileDrop");
    
            ListViewItem[] items = new ListViewItem[filePaths.Length];
            string filePath;
    
            // For each file in the list, create an item, complete with icon.
            for (int index = 0; index < filePaths.Length; index++)
            {
                filePath = filePaths[index];
    
                if (!this.imageList1.Images.Keys.Contains(filePath))
                {
                    // This is the first time this file has been added so add its icon too.
                    this.imageList1.Images.Add(filePath,
                                               Icon.ExtractAssociatedIcon(filePath));
                }
    
                items[index] = new ListViewItem(filePath, filePath);
            }
    
            // Add the items to the ListView.
            this.listView1.Items.AddRange(items);
        }
    }
    4. Run the project.
    5. Select one or more files in Windows Explorer and drag them onto your form.

    Tada! Note that, as before, I've done some setup of design time elements that you'd normally do in the designer. It was easier to do it in code than to explain though, for the purpose of this example.

  3. #3

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Determining How to Retrieve the Data

    I'm going to use that last example as a basis for showing you how to build up drag & drop code. I can hear people now saying "But how would I know to use the FileDrop format and how would I know that it returns a String array". Well, I'm going to show you how you work that out.

    First up, you have to make sure that your control is a drop target, so set its AllowDrop property to True. Next you need to test what formats the data being dragged is available in. To do that you use the GetFormats method. GetFormats returns a String array, so you can loop through it to see what formats you can get the data in:
    CSharp Code:
    1. private void listView1_DragEnter(object sender As Object, DragEventArgs e)
    2. {
    3.     foreach (string format in e.Data.GetFormats())
    4.     {
    5.         MessageBox.Show(format);
    6.     }
    7. }
    Drag your data onto the control and it will start popping up messages for each format. You might prefer to use Console.WriteLine and view the list in the Output window as it's less intrusive.

    You now need to determine what .NET object will be created if you get the data in each of those formats and then decide which of those is most useful to you. To do that, you first need to get the data in each of those formats and then test it's type:
    CSharp Code:
    1. private void listView1_DragEnter(object sender, DragEventArgs e)
    2. {
    3.     foreach (string format in e.Data.GetFormats())
    4.     {
    5.         MessageBox.Show(e.Data.GetData(format).GetType().ToString(), format);
    6.     }
    7. }
    You can then examine each of those objects based on its .NET type. Those that return a MemoryStream are going to be a little harder to interpret, but the String arrays are going to be easy to examine, so let's start with them:
    CSharp Code:
    1. private void listView1_DragEnter(object sender, DragEventArgs e)
    2. {
    3.     string[] strings;
    4.  
    5.     foreach (string format in e.Data.GetFormats())
    6.     {
    7.         strings = e.Data.GetData(format) as string();
    8.  
    9.         if (strings != null)
    10.         {
    11.             MessageBox.Show(String.Join(Environment.NewLine,
    12.                                         strings),
    13.                             format);
    14.         }
    15.     }
    16. }
    So, having done that by dragging multiple files you will now know that the FileName format returns the path of the first file in DOS-compatible format, the FileNameW format returns the full path of the first file and the FileDrop format returns the full paths of all the files. In this case that's as far as we need to go because the full paths of all files is exactly what we need. We now know that we need to test for the FileDrop format, which we do with GetDataPresent, and then get the data in that format, which we do with GetData.

    That's basically how you should approach all drag & drop operations.

  4. #4

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Reordering Items in a ListBox

    1. Create a new Windows Forms project.
    2. Add a ListBox to the form.
    3. Replace the existing code of the form with the following:
    Code:
    public Form1()
    {
        InitializeComponent();
    
        this.Load += new EventHandler(Form1_Load);
    }
    
    private void Form1_Load(object sender, EventArgs e)
    {
        // Initialise the ListBox.
        this.listBox1.AllowDrop = true;
        this.listBox1.Items.AddRange(new string[] {"Item 1",
                                                   "Item 2",
                                                   "Item 3",
                                                   "Item 4",
                                                   "Item 5"});
    
        this.listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
        this.listBox1.DragEnter += new DragEventHandler(listBox1_DragEnter);
        this.listBox1.DragOver += new DragEventHandler(listBox1_DragEnter);
        this.listBox1.DragDrop += new DragEventHandler(listBox1_DragDrop);
    }
    
    private void listBox1_MouseDown(object sender, MouseEventArgs e)
    {
        // Test whether the mouse is over an item.
        if (this.GetItemIndexAtPoint(e.Location) != -1)
        {
            // The mouse is over an item so start dragging.
            this.listBox1.DoDragDrop(this.listBox1, DragDropEffects.Move);
        }
    }
    
    private void listBox1_DragEnter(object sender, DragEventArgs e)
    {
        // Test whether the data being dragged is the ListBox itself.
        if (e.Data.GetDataPresent("System.Windows.Forms.ListBox") &&
            e.Data.GetData("System.Windows.Forms.ListBox") == this.listBox1)
        {
            // Get the location of the mouse relative to the ListBox.
            Point mouseLocation = this.listBox1.PointToClient(new Point(e.X, e.Y));
    
            // Force the location to be within the horizontal bounds of the ListBox.
            if (mouseLocation.X < 0)
            {
                mouseLocation.X = 0;
            }
            else if (mouseLocation.X > this.listBox1.Width)
            {
                mouseLocation.X = this.listBox1.Width;
            }
    
            // Force the location to be within the vertical bounds of the ListBox.
            if (mouseLocation.Y < 0)
            {
                mouseLocation.Y = 0;
            }
            else if (mouseLocation.Y > this.listBox1.Height)
            {
                mouseLocation.Y = this.listBox1.Height;
            }
    
            if (this.GetItemIndexAtPoint(mouseLocation) == this.listBox1.SelectedIndex)
            {
                // Don't allow the selected item to be dropped on itself.
                e.Effect = DragDropEffects.None;
            }
            else
            {
                // Allow the selected item to be moved.
                e.Effect = DragDropEffects.Move;
            }
        }
    }
    
    private void listBox1_DragDrop(object sender, DragEventArgs e)
    {
        // Test whether the data being dragged is the ListBox itself and the selected item can be moved.
        if (e.AllowedEffect == DragDropEffects.Move &&
            e.Data.GetDataPresent("System.Windows.Forms.ListBox") &&
            e.Data.GetData("System.Windows.Forms.ListBox") == this.listBox1)
        {
            int selectedIndex = this.listBox1.SelectedIndex;
    
            // Get the index of the item being dropped on.
            int dropIndex = this.GetItemIndexAtPoint(this.listBox1.PointToClient(new Point(e.X, e.Y)));
    
            // If the item being dropped on is below the selected item, the index of the
            // item being dropped on will decrement once the selected item is removed.
            if (dropIndex > selectedIndex)
            {
                dropIndex -= 1;
            }
    
            object selectedItem = this.listBox1.SelectedItem;
    
            this.listBox1.Items.Remove(selectedItem);
    
            if (dropIndex == -1)
            {
                // The item was dropped after the last item so add it at the end of the list.
                this.listBox1.Items.Add(selectedItem);
            }
            else
            {
                // Insert the item above the item it was dropped on.
                this.listBox1.Items.Insert(dropIndex, selectedItem);
            }
    
            this.listBox1.SelectedItem = selectedItem;
        }
    }
    
    private int GetItemIndexAtPoint(Point location)
    {
        int itemIndex = -1;
    
        for (int index = 0; index < this.listBox1.Items.Count; index++)
        {
            // Test whether the location is within an item.
            if (this.listBox1.GetItemRectangle(index).Contains(location))
            {
                itemIndex = index;
                break;
            }
        }
    
        return itemIndex;
    }
    4. Run the project and start dragging.

    When you drop an item it will be moved to the position above the item you dropped it on. If you drop it after the last item it will be moved to the end of the list.

  5. #5

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Dropping Image Files on a PictureBox

    Quote Originally Posted by Xancholy
    I request sample drag drop code for a picturebox.
    Also how to refuse multiple file drops and limit to single dropped file.

    Thanks.
    The first thing to note is that the AllowDrop property of the PictureBox class is not designer-visible. I'm not sure why this is because the PictureBox does support drag & drop. As a result you will have to set the property in code.

    Now, this first code example will allow you to drop multiple files on the PictureBox but it simply ignores all but the first file:
    vb.net Code:
    1. private void Form1_Load(object sender, EventArgs e)
    2. {
    3.     // Let the PictureBox accept dropped data.
    4.     this.pictureBox1.AllowDrop = true;
    5.     this.pictureBox1.DragEnter += new DragEventHandler(pictureBox1_DragEnter);
    6.     this.pictureBox1.DragDrop += new DragEventHandler(pictureBox1_DragDrop);
    7. }
    8.  
    9. private void pictureBox1_DragEnter(object sender, DragEventArgs e)
    10. {
    11.     // Only allow copying and then only if one or more files are being dragged and the first one is an image file.
    12.     if ((e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy &&
    13.         e.Data.GetDataPresent("FileDrop", true) &&
    14.         this.IsImageFile(((string[])e.Data.GetData("FileDrop", true))[0]))
    15.     {
    16.         e.Effect = DragDropEffects.Copy;
    17.     }
    18. }
    19.  
    20. Private void pictureBox1_DragDrop(object sender, DragEventArgs e)
    21. {
    22.     if (e.Effect == DragDropEffects.Copy)
    23.     {
    24.         // Load the first file.
    25.         this.pictureBox1.Load(((string[])e.Data.GetData("FileDrop", true))[0]);
    26.     }
    27. }
    28.  
    29. /// <summary>
    30. /// Determines whether the specified path contains an image file.
    31. /// </summary>
    32. /// <param name="path">
    33. /// The path of the file to test.
    34. /// </param>
    35. /// <returns>
    36. /// <b>True</b> if <i>path</i> contains an image file; otherwise, <b>False</b>.
    37. /// </returns>
    38. private bool IsImageFile(string path)
    39. {
    40.     bool result;
    41.  
    42.     try
    43.     {
    44.         using (Image img = Image.FromFile(path)) { }
    45.         result = true;
    46.     }
    47.     catch
    48.     {
    49.         result = false;
    50.     }
    51.  
    52.     return result;
    53. }
    Analysing this code, in the DragEnter event handler we test whether it is files that are being dragged and that the first one is an image. If that's the case then a copy cursor is displayed. Even if there are other files that are not images they are simply ignored.

    In the DragDrop event handler we don't need to test the data again because we know that, if the effect is Copy then the data must have passed the test in the DragEnter event handler. That means we simply get the first file path and load it into the PictureBox.

    It should be noted that the IsImageFile method is using a brute force approach to determine whether the file is an image or not. It simply creates an Image object and handles the exception that's thrown if it fails. This is not ideal but there really isn't another way (that I'm aware of) to perform this test.

    For clarity, here's the DragEnter event handler rewritten with the code extended out a bit:
    vb.net Code:
    1. private void pictureBox1_DragEnter(object sender, DragEventArgs e)
    2. {
    3.     // Only allow copying and then only if one or more files are being dragged and the first one is an image file.
    4.     if ((e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy &&
    5.         e.Data.GetDataPresent("FileDrop", true))
    6.     {
    7.         object data = e.Data.GetData("FileDrop", true);
    8.         string[] filePaths = (string[])data;
    9.         string firstFilePath = filePaths[0];
    10.  
    11.         if (this.IsImageFile(firstFilePath))
    12.         {
    13.             e.Effect = DragDropEffects.Copy;
    14.         }
    15.     }
    16. }
    If you want to prevent multiple files being dropped at all then you can do this:
    vb.net Code:
    1. private void pictureBox1_DragEnter(object sender, DragEventArgs e)
    2. {
    3.     // Only allow copying and then only if one file is being dragged and it is an image file.
    4.     if ((e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy &&
    5.         e.Data.GetDataPresent("FileDrop", true))
    6.     {
    7.         object data = e.Data.GetData("FileDrop", true);
    8.         string[] filePaths = (string[])data;
    9.  
    10.         if (filePaths.Length == 1)
    11.         {
    12.             string firstFilePath = filePaths[0];
    13.  
    14.             if (this.IsImageFile(firstFilePath))
    15.             {
    16.                 e.Effect = DragDropEffects.Copy;
    17.             }
    18.         }
    19.     }
    20. }

  6. #6
    New Member
    Join Date
    Oct 2008
    Posts
    14

    Re: Drag & Drop in Windows Forms

    hey jmcilhinney very nice projects!!!can you do me a favour??i keep trying to make the program "Reordering Items in a ListBox" to vb 2005 but it seems that i m failing every time...can you do it for me or at least tell me what do i need to change???thanks in advance...

  7. #7

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Re: Drag & Drop in Windows Forms

    Did you connect the event handlers after adding the code? In VB you have a Handles clause so event handlers get wired up automatically. In C# you have to do it manually by selecting the appropriate method from the drop-down list in the Properties window, after clicking the Events button.

  8. #8
    New Member
    Join Date
    Oct 2008
    Posts
    14

    Re: Drag & Drop in Windows Forms

    it gives me errors at these points

    Me.Load += New EventHandler(Form1_Load)

    and

    Me.ListBox1.MouseDown += New MouseEventHandler(listBox1_MouseDown)
    Me.ListBox1.DragEnter += New DragEventHandler(listBox1_DragEnter)
    Me.ListBox1.DragOver += New DragEventHandler(listBox1_DragEnter)
    Me.ListBox1.DragDrop += New DragEventHandler(listBox1_DragDrop)

    the error is Public Event Load(sender As Object, e As System.EventArgs)' is an event and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

    i tried using AddHandler Me.Load, AddressOf Form1_Load but it won't work

    thanks for the help...sorry for my english

  9. #9

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,297

    Re: Drag & Drop in Windows Forms

    Quote Originally Posted by theopres
    it gives me errors at these points

    Me.Load += New EventHandler(Form1_Load)

    and

    Me.ListBox1.MouseDown += New MouseEventHandler(listBox1_MouseDown)
    Me.ListBox1.DragEnter += New DragEventHandler(listBox1_DragEnter)
    Me.ListBox1.DragOver += New DragEventHandler(listBox1_DragEnter)
    Me.ListBox1.DragDrop += New DragEventHandler(listBox1_DragDrop)

    the error is Public Event Load(sender As Object, e As System.EventArgs)' is an event and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

    i tried using AddHandler Me.Load, AddressOf Form1_Load but it won't work

    thanks for the help...sorry for my english
    I missed in your first reply that you said you were using VB. This is the C# CodeBank forum. This thread contains C# code. My signature contains to links to both C# and VB versions of this topic and the very first line of this very thread provides a link to the VB version. If you're creating a VB application then you'd probably be better off with the VB version of this topic, don't you think?

  10. #10
    PowerPoster Nightwalker83's Avatar
    Join Date
    Dec 2001
    Location
    Adelaide, Australia
    Posts
    13,344

    Re: Drag & Drop in Windows Forms

    Thanks! I was wondering how I could do this in C# after created an old example in Visual Basic 6.0 years ago.
    when you quote a post could you please do it via the "Reply With Quote" button or if it multiple post click the "''+" button then "Reply With Quote" button.
    If this thread is finished with please mark it "Resolved" by selecting "Mark thread resolved" from the "Thread tools" drop-down menu.
    https://get.cryptobrowser.site/30/4111672

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width