Working with drag and drop isn’t much different from working with the clipboard. The difference is that the data isn’t first copied to an intermediary storage from which it can later be retrieved (multiple times); instead, it’s directly transferred from one application to another by means of a mouse operation—the drag and drop operation.
The following subsections introduce you to the various concepts of drag and drop, including basic drag and drop operations and related events, dragging and dropping files, and implementing a drag and drop source.
Our examination of the clipboard laid the foundation for working with drag and drop. You already know about the types of data that can be passed and how that data is handled through the IDataObject interface. We now have to look at how the drag and drop operation actually takes place between the initiator (the drag and drop source) and the recipient of the data (the drag and drop target).
The drag and drop operation involves the mouse and, less obviously, the keyboard. Depending on which keys are pressed, the drag and drop operation is either a copy, a move, or a link operation. Table 17-4 lists the operations and their modifier keys.
In a copy operation, the data from the source is duplicated at the target. The move operation copies data to the target and removes it from the source. In addition to initially copying the data, the link operation provides a way for the target to update its data whenever the source changes.
These three operations are available to any control in a Windows Forms application. By default, none of these controls is enabled as a drag and drop target, however. You have to do that yourself, by setting the AllowDrop property to true. Although technically your control now allows drops, you can’t yet perform a drag and drop operation.
To be able to accept drag and drop data, your control must implement a few events. Table 17-5 lists all drag and drop–related events; an explanation of which events must be implemented follows.
Event |
Description |
DragDrop |
Fires when the mouse button is released over the drop target |
DragEnter |
Fires when the mouse pointer is moved onto a control that has AllowDrop set to true during an ongoing drag and drop operation |
DragLeave |
Fires when the mouse pointer is moved out of a control during an ongoing drag and drop operation |
DragOver |
Fires when the mouse pointer is moved over a drag and drop control during a drag and drop operation |
GiveFeedback |
Enables the source to give visual feedback about the drag and drop operation by changing the mouse pointer style |
QueryContinueDrag |
Fires during a drag and drop operation and allows the source to determine whether the drag and drop operation should be cancelled |
The first four events in this table are designed for drag and drop targets. DragEnter is called first, followed by DragOver, and then either DragDrop or DragLeave. To implement a target that accepts drops, you must implement DragOver and DragDrop. The other two events are optional; they just give you more information about when a drag and drop operation enters or leaves the control.
The DragOver event serves an important purpose in that it gives you the chance to modify how the drag and drop operation takes place—for example, whether you’re going to accept the operation at all based on the data that’s being offered. The information about the operation and your feedback is packaged in the DragEventArgs parameter that’s passed to your implementation of DragOver. Table 17-6 lists the DragEventArgs properties and their types and accessibilities.
Property |
Type |
Accessibility |
AllowedEffect |
DragDropEffects |
get |
Data |
IDataObject |
get |
Effect |
DragDropEffects |
get/set |
KeyState |
Int |
get |
X |
Int |
get |
Y |
Int |
get |
With the exception of Effect, all of these properties are read-only. You can use the coordinates, data, allowed effects, and key states to determine whether to allow the drag and drop operation on your control. The kind of operation you allow is reported back to the application through the Effect property, which can be any value of the DragDropEffects enumeration, listed in Table 17-7.
Value |
Description |
All |
Combines Copy, Move, and Scroll |
Copy |
Allows data copying to the target |
Link |
Enables the target to link to data in the source |
Move |
Performs a move of data from the source to the target |
None |
Disallows the drop operation |
Scroll |
Indicates that the drop target is about to begin scrolling or is scrolling |
A drag and drop source sets the AllowedEffect property, whereas the target can report back to the application through the Effect property. Most commonly, sources allow copy, link, and move operations. Depending on how sophisticated it is, the target will allow link, copy, and move operations or only copy and move operations.
Your application can determine what the source allows and what in general your target can accept. But what about the user’s intentions, as expressed through the modifier keys? This information is passed in the KeyState property; the bit flags for each key are listed in Table 17-8.
Bit Flag |
Key |
0x01 |
Left mouse button is pressed. |
0x02 |
Right mouse button is pressed. |
0x10 |
Middle mouse button is pressed. |
0x04 |
Shift key is pressed. |
0x08 |
Ctrl key is pressed. |
0x20 |
Alt key is pressed. |
You can check whether a certain key or button was pressed by ANDing the KeyState property with the bit flag, as follows:
if (0 != (e.KeyState & 0x01))
This information enables you to determine what Effect the user wants from your application. Combined with the data type your application can accept as a drag and drop target, you set the respective DragDropEffects values in your event handlers.
Almost any application supports the FileDrop operation in Windows Explorer. In this operation, the user selects one or more files and drags them over the target application. On release, the target application opens the files. To support this operation, you must implement the DragOver and DragDrop events, and of course the AllowDrop property must be set to true.
For demonstration purposes, the simple Notepad-like application FileDropTarget is included on the companion CD. This application contains a multiline TextBox control, with docking style set to Full. The AllowDrop property is set to true, which allows the application to be a drag and drop target. To enable FileDrop, we need to first implement the DragOver event, as shown here:
private void txtMain_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Copy; } }
Because this simple application supports FileDrop only, DragDropEffects.Copy is set only when data in FileDrop format is offered. When other data is dragged to this application, the no-drop mouse pointer will be displayed. Also note that we’re not concerned with the modifier keys with a FileDrop operation.
When the user drops the files on the application, the DragDrop event is fired. For our sample application, the DragDrop event looks like this:
private void txtMain_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] strFiles = (string[])e.Data.GetData(DataFormats.FileDrop); StreamReader reader = new StreamReader(strFiles[0]); this.txtMain.Clear(); this.txtMain.Text = reader.ReadToEnd(); reader.Close(); } }
We need to perform a final check on the data that’s being offered. If the data is in the specified Windows FileDrop format as the application expects, we can retrieve the associated data, which is a string array of file names. Because the application is a Single Document Interface (SDI) application, it opens only the first file and ignores the rest. (The user can drag more than one file.) If you were building Multiple Document Interface (MDI) applications, you would open all the files—so long as your application can open the file format used by the files.
It’s very common for applications to be drag and drop sources as well as drag and drop targets. One familiar application that works as a source and a target is Visual C#. For example, when you drag a control from the Toolbox onto a form, the Toolbox is the drag and drop source and the form is the drag and drop target.
You can make any control you build or use a drag and drop source. The DragDropSample application, on the companion CD, demonstrates a drag and drop source. As shown in Figure 17-10, the drag and drop source is the list box on the left, and the target is the text box on the right.
The user selects an item in the list box and drags it to the text box. When dropped, the text is pasted to the end of the text that already appears in the text box. How much work is involved in making this happen? To start with, take a look at how the ListBox object is filled:
this.lbDragDropSource.Items.Add("<html>"); this.lbDragDropSource.Items.Add("</html>");
The data displayed in the ListBox object also serves as drag and drop data.
Because a drag and drop operation is in process as long as the mouse button is down, the ideal starting point for a drag and drop operation is the MouseDown event of the drag and drop source, as shown here:
private void lbDragDropSource_MouseDown(object sender, MouseEventArgs e) { int nSelectedIndex = lbDragDropSource.SelectedIndex; string strItem = (string)lbDragDropSource.Items[nSelectedIndex]; DragDropEffects dde = lbDragDropSource.DoDragDrop(strItem, DragDropEffects.Copy); if (DragDropEffects.None == dde) MessageBox.Show("Our drag and drop offer was not accepted"); }
Note that in this example, we don’t care which mouse button was pressed; we simply handle any MouseDown event.
You need to determine what data you want the drag and drop source to offer. In this application, the data is the currently selected item in the list box, and the data is offered for copying through the call to DoDragDrop. This method won’t return until either the data is accepted or the drag and drop operation is cancelled. You’re notified what operation took place on your data by the return value of the method, which is of type DragDropEffects. A return value of None indicates that the drag and drop operation didn’t take place because no one accepted the data.
Sometimes it might be desirable for your drag and drop source to influence the ongoing drag and drop operation. This can be done in the QueryContinueDrag event, as shown here:
private void lbDragDropSource_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) { // e.Action = DragAction.Cancel; }
The QueryContinueDragEventArgs argument has three useful properties named Action, EscapePressed, and KeyState. This argument enables you to modify the drag and drop operation based on the internal state of your application or the key states that are passed through EscapePressed and KeyState. The Action property can be any value of the DragAction enumeration, listed in Table 17-9.
In our sample application, there’s no need to actually make use of the event. We’re fine with what our TextBox client does in its DragOver and DragDrop event handlers, as shown here:
private void txtMain_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(typeof(string))) { e.Effect = DragDropEffects.Copy; } } private void txtMain_DragDrop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(typeof(string))) { string strData = (string)e.Data.GetData(typeof(string)); // Append text to the current text in the text box. txtMain.AppendText(strData); } }
You can use the drag and drop source implemented in this section with any other drag and drop source application—for example, to drag and drop text into Visual C#.