A SearchWebServices.com member posed the following question to .NET Technologies expert Jim Culbert:
Q: Are there any good code examples for using drag and drop from a ListView to a DataGrid control? I'm building a standard Visual Basic.NET application and would like to implement this.
Here is Jim's reply:
Dunno. I'll make you one. Here goes.
Since you didn't ask about how to create or manipulate ListViews or DataGrids, I am going to assume for this reply that you already know how to do this. Once you've created your project with your ListView and DataGrid you need to do a couple of things to handle drag and drop events. The steps are:
- Enable drag and drop capabilities in your grid and view.
- Implement an event handler for your ListView that handles the drag start event.
- Implement an event handler for your DataGrid that handles the drag enter event.
- Implement an event handler for your DataGrid that handles the drop event.
The flow goes something like this. When an event occurs that your object decides is a drag start event your code responds by collecting up relevant data and passing it to the framework through your object's DoDragDrop method. The triggering event is usually the left mouse down event but some objects (like the ListView) implement special events to handle drag start. In the case of the ListView the object fires a special event called ItemDrag.
Now, while you drag the cursor around, the system is checking each of the objects that the cursor passes over to determine whether it is a drop target or not. The system takes care of your cursor management during the process switching pointers as you mouse from objects that implement the drop capability to those that do not. As you drag an item over objects, each object's DragEnter event is fired. How the object responds to this event determines whether a drop can be performed there. So, to support dropping data onto your DataGrid control, your control must have a DragEnter event handler setup. In this event handler, you can inspect the contents of the item being dragged around and you can decide whether you want to allow the drop or not. You signal your intentions to the framework through an object it passes into the event handler.
If your object signals that it will accept a drop, then when a user drops the item by releasing the mouse button, a DragDrop event is generated for your object. You need to handle this event to implement the drop. The framework hands the event handler the data that was setup by the ListView's ItemDrag event handler. Your object now has the data and can decide to do whatever it wants with it. In the case of the DataGrid, it is likely that you would stuff the data into the grid and possibly update the data source associated with the grid at the same time.
So that's the high-level bit, here is how you would implement it.
I've removed all the auto-generated code. This project is a simple Visual Basic Windows Forms application that has one form. I have instantiated a SQLConnection, SQLDataAdapter and a DataSet named SQLConnection1, SQLDataAdapter1 and DataSet1 respectively. Similarly, I've instantiated ListView1 and DataGrid1 as well. DataGrid1 is bound to DataSet1 so filling the dataset populates my DataGrid for me. You can choose to do this however you want. I've intentionally left that stuff out (as well as any error handling) to emphasize the drag and drop code.
Oh yeah, to enable drag and drop on your grid and view objects (step one mentioned at the beginning of the note), you can either do it in code or just flip the "AllowDrop" property to "True" for each object in their respective properties window in the Visual Studio IDE.
Public Class Form1 Inherits System.Windows.Forms.Form Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load '// Set the password on the connection string, open the '// data source and populate the grid. SqlConnection1.ConnectionString += ";Pwd=scoobydoo" SqlConnection1.Open() SqlDataAdapter1.Fill(Me.DataSet11) End Sub Private Sub ListView1_ItemDrag(ByVal Sender As Object, ByVal e As ItemDragEventArgs) Handles ListView1.ItemDrag '// The item drag prototype is shared between listview and treeview '// so the item passed in the event arguments is a generic object. '// For convenience, we first cast it to a ListViewItem type, then '// we work with the object. Dim lvItem As ListViewItem lvItem = CType (e.Item, ListViewItem) '// Now we collect whatever important data we want. This is just for '// instruction in this example. I'm not actually going to use this '// data, but I put this here to show the steps you might take in '// a non-trivial example where you looked at the data you were '// going to drag before allowing the drag. Dim importantData As String importantData = lvItem.Text '// OK. This is where we pass our data to the framework. This method '// takes two arguments. The first is an object that contains your '// data. This object needs to be a system type or any type that '// supports serialization. The second argument tells the framework '// what drop actions are allowed for this operation. Here we specify '// that we only allow the data to be copied. We'll see the importance '// of this in the DragEnter handler. It's possible to OR together '// multiple allowed acctions (such as more OR copy). Dim foo As DragDropEffects foo = ListView1.DoDragDrop(e.Item, DragDropEffects.Copy) '// The DoDragDrop method does not return until the drag and '// drop operation has completed. The return value indicates '// which of the allowed actions was actually performed. We '// display it for fun... MessageBox.Show(foo.ToString()) End Sub Private Sub DataGrid1_DragEnter(ByVal Sender As Object, ByVal e As DragEventArgs) Handles DataGrid1.DragEnter '// This event is fired when an item is dragged over the DataGrid1 '// object. What we're going to do in this method is inspect the '// contents that are being dragged and determine whether we'll '// allow them to be dropped on the DataGrid. We signal our willingness '// to accept a drop by setting the Effect member on the DragEventArgs '// object that is passed in. In this case, we will only accept a '// drop if the item being dragged is a ListViewItem. e.Effect = DragDropEffects.None Dim o As Object '// The following is broken into two IFs to make documenting '// easier. '// First we check to see that that there is data '// in the event arguments. The Serializable format '// works for any serializable object. If e.Data.GetDataPresent(DataFormats.Serializable) Then '// If we actually have data, the we'll fetch it out '// and validate that the content is actually a ListViewItem o = e.Data.GetData(DataFormats.Serializable) '// If we've got a ListViewItem, then we set the Effect '// member on the DragEventArgs object, indicating '// that a drop here will cause the data to be copied. '// NOTE: Had we tried to set the Effect to another effect '// like "move" the framework would detect the discrepancy '// between the allowed events set in the DoDragDrop call '// in the ItemDrag event handler from ListView. The framework '// would reject the attemt to set the effect and the effect '// would remain "none". If o.GetType()Is GetType (ListViewItem) Then e.Effect = DragDropEffects.Copy End If End If End Sub Private Sub DataGrid1_DragDrop(ByVal Sender As Object, ByVal e As DragEventArgs) Handles DataGrid1.DragDrop Dim message As String Dim lvItem As ListViewItem '// This is where the action happens! '// If we've gotten here, we know that we are being passed '// a ListViewItem because our trusty sentinel (DragEnter) has '// rejected all drags other than those that contain ListViewItems. '// In this handler, we simply extract the data from the event '// arguments and squirrel it away in a ListViewItem object. We're '// now free to do whatever we want with the data. Insert, udpdate, '// delete at will!! lvItem = e.Data.GetData(DataFormats.Serializable) message = "The drop text is: " + lvItem.Text MessageBox.Show(message) End Sub End Class