- 7.1 A Survey of .NET Windows Forms Controls
- 7.2 Button Classes, Group Box, Panel, and Label
- 7.3 PictureBox and TextBox Controls
- 7.4 ListBox, CheckedListBox, and ComboBox Classes
- 7.5 The ListView and TreeView Classes
- 7.6 The ProgressBar, Timer, and StatusStrip Classes
- 7.7 Building Custom Controls
- 7.8 Using Drag and Drop with Controls
- 7.9 Using Resources
- 7.10 Summary
- 7.11 Test Your Understanding
7.8 Using Drag and Drop with Controls
The ability to drag data from one control and drop it onto another has long been a familiar feature of GUI programming. .NET supports this feature with several classes and enumerations that enable a control to be the target and/or source of the drag-and-drop operation.
Overview of Drag and Drop
The operation requires a source control that contains the data to be moved or copied, and a target control that receives the dragged data. The source initiates the action in response to an event—usually a MouseDown event. The source control’s event handler begins the actual operation by invoking its DoDragDrop method. This method has two parameters: the data being dragged and a DragDropEffects enum type parameter that specifies the effects or actions the source control supports (see Table 7-4).
Table 7-4 DragDropEffects Enumeration
Member |
Description |
All |
The data is moved to the target control, and scrolling occurs in the target control to display the newly positioned data. |
Copy |
Data is copied from target to source. |
Link |
Data from the source is linked to the target. |
Move |
The data is moved from the source to the target control. |
None |
The target control refuses to accept data. |
Scroll |
Scrolling occurs or will occur on the target control. |
As the mouse moves across the form, the DoDragDrop method determines the control under the current cursor location. If this control has its AllowDrop property set to true, it is a valid drop target and its DragEnter event is raised. The DragEnter event handler has two tasks: to verify that the data being dragged is an acceptable type and to ensure the requested action (Effect) is acceptable. When the actual drop occurs, the destination control raises a DragDrop event. This event handler is responsible for placing the data in the target control (see Figure 7-18).
Figure 7-18 Sequence of events in drag-and-drop operation
After the DragDrop event handler finishes, the source control performs any cleanup operations. For example, if the operation involves moving data—as opposed to copying—the data must be removed from the source control.
To demonstrate these ideas, let’s create an application that assigns players to a team from a roster of available players (see Figure 7-19). Team A is created by dragging names from the Available Players to the Team A list. Both lists are implemented with list boxes, and the Available Players list is set for single selection.
Figure 7-19 Drag-and-drop example
A name is selected by pressing the right mouse button and dragging the name to the target list. To add some interest, holding the Ctrl key copies a name rather than moving it.
After the form and controls are created, the first step is to set up the source control (lstPlayers) to respond to the MouseDown event and the target control (lstTeamA) to handle the DragEnter and DragDrop events:
lstPlayers.MouseDown += new MouseEventHandler(Players_MouseDown); lstTeamA.DragEnter += new DragEventHandler(TeamA_DragEnter); lstTeamA.DragDrop += new DragEventHandler(TeamA_Drop);
The next step is to code the event handlers on the source and target control(s) that implement the drag-and-drop operation.
Source Control Responsibilities
The MouseDown event handler for the source ListBox first checks to ensure that an item has been selected. It then calls DoDragDrop, passing it the value of the selected item as well as the acceptable effects: Move and Copy. The DragDropEffects enumeration has a FlagsAttribute attribute, which means that any bitwise combination of its values can be passed. The value returned from this method is the effect that is actually used by the target. The event handler uses this information to perform any operations required to implement the effect. In this example, a move operation means that the dragged value must be removed from the source control.
Listing 7-7 Initiating a Drag-and-Drop Operation from the Source Control
private void Players_MouseDown(object sender, MouseEventArgs e) { if ( lstPlayers.SelectedIndex >=0) { string players; int ndx = lstPlayers.SelectedIndex; DragDropEffects effect; players = lstPlayers.Items[ndx].ToString(); if(players != "") { // Permit target to move or copy data effect = lstPlayers.DoDragDrop(players, DragDropEffects.Move | DragDropEffects.Copy); // Remove item from ListBox since move occurred if (effect == DragDropEffects.Move) lstPlayers.Items.RemoveAt(ndx); } } }
Target Control Responsibilities
The destination control must implement the event handlers for the DragEnter and DragDrop events. Both of these events receive a DragEventArgs type parameter (see Table 7-5) that contains the information required to process the drag-and-drop event.
Table 7-5 DragEventArgs Properties
Member |
Description |
||||||
AllowedEffect |
The effects that are supported by the source control. Example to determine if Move is supported: if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move) |
||||||
Data |
Returns the IDataObject that contains data associated with this operation. This object implements methods that return information about the data. These include GetData, which fetches the data, and GetDataPresent, which checks the data type. |
||||||
Effect |
Gets or sets the target drop effect. |
||||||
KeyState |
Returns the state of the Alt key, Ctrl key, Shift key, and mouse buttons as an integer:
|
||||||
X, Y |
x and y coordinates of the mouse pointer. |
The Data, Effect, and KeyState members are used as follows:
- Data.GetDataPresent is used by the DragEnter event handler to ensure that the data is a type the target control can process.
- The DragDrop event handler uses Data.GetData to access the data being dragged to it. The parameter to this method is usually a static field of the DataFormats class that specifies the format of the returned data.
- The DragEnter event handler uses KeyState to determine the status of the mouse and keys in order to determine the effect it will use to process the data. Recall that in this example, pressing the Ctrl key signals that data is to copied rather than moved.
- Effect is set by the DragEnter event handler to notify the source as to how—or if—it processed the data. A setting of DragDropEffects.None prevents the DragDrop event from firing.
Listing 7-8 shows the code for the two event handlers.
Listing 7-8 Handling the DragEnter and DragDrop Events
[FlagsAttribute] enum KeyPushed { // Corresponds to DragEventArgs.KeyState values LeftMouse = 1, RightMouse = 2, ShiftKey = 4, CtrlKey = 8, MiddleMouse = 16, AltKey = 32, } private void TeamA_DragEnter(object sender, DragEventArgs e) { KeyPushed kp = (KeyPushed) e.KeyState; // Make sure data type is string if (e.Data.GetDataPresent(typeof(string))) { // Only accept drag with left mouse key if ( (kp & KeyPushed.LeftMouse) == KeyPushed.LeftMouse) { if ((kp & KeyPushed.CtrlKey) == KeyPushed.CtrlKey) { e.Effect = DragDropEffects.Copy; // Copy } else { e.Effect = DragDropEffects.Move; // Move } } else // Is not left mouse key { e.Effect = DragDropEffects.None; } } else // Is not a string { e.Effect = DragDropEffects.None; } } // Handle DragDrop event private void TeamA_Drop(object sender, DragEventArgs e) { // Add dropped data to TextBox lstTeamA.Items.Add( (string) e.Data.GetData(DataFormats.Text)); }
An enum is created with the FlagsAttributes attribute to make checking the KeyState value easier and more readable. The logical "anding" of KeyState with the value of the CtrlKey (8) returns a value equal to the value of the CtrlKey if the Ctrl key is pressed.
A control can serve as source and target in the same application. You could make this example more flexible by having the list boxes assume both roles. This would allow you to return a player from lstTeamA back to the lstPlayers ListBox. All that is required is to add the appropriate event handlers.