Drag and Drop


No matter what kind of controls you're using or building, often you'd like to enable the user to drag data from one to another. This communication protocol, known as drag and drop , has long been standardized and is fully supported in WinForms for both targets and sources.

The Drop Target

Adding drag and drop to your application involves two elements: the drop target and the drop source. First, you must have a control that supports having things dragged and dropped onto it. This kind of control is known as the drop target . You designate your target by setting the AllowDrop property of the control to true.

Next, you make the target control subscribe to one or more of the drag-and-drop events:

  • DragEnter is fired when the mouse enters the area of a control containing drag-and-drop data. It is used by the target to indicate whether it can accept the data.

  • DragOver is called as the user hovers the mouse over the target.

  • DragLeave is called when the mouse leaves the area of a control containing the drag-and-drop data.

  • DragDrop is called when the user drops the data onto the target.

All target controls must implement the DragEnter event, or else they won't be able to accept any dropped data. The DragEnter event comes along with an instance of the DragEventArgs class, which gives the target information about the data:

 
 Class DragEventArgs   Inherits EventArgs   ' Properties   Property AllowedEffect() As DragDropEffects   Property Data() As IDataObject   Property Effect() As DragDropEffects   Property KeyState() As Integer   Property X() As Integer   Property Y() As Integer End Class 

A target control's DragEnter event handler checks the Data property to see whether it can be accepted when dropped. The object returned from the Data property implements IDataObject to make that determination possible:

 
 Interface IDataObject   ' Methods   Overloads Function GetData(format As String, _       autoConvert As Boolean) As Object   Overloads Function GetData(format As String) As Object   Overloads Function GetData(format As Type) As Object   Overloads Sub SetData(format As String, _       autoConvert As Boolean, data As Object)   Overloads Sub SetData(format As String, data As Object)   Overloads Sub SetData(format As Type, data As Object)   Overloads Sub SetData(data As Object)   Overloads Function GetDataPresent(format As String, _       autoConvert As Boolean) As Boolean   Overloads Function GetDataPresent(format As String) As Boolean   Overloads Function GetDataPresent(format As Type) As Boolean   Overloads Function GetFormats(autoConvert As Boolean) As String()   Overloads Function GetFormats() As String() End Interface 

The IDataObject interface is actually defined from its Component Object Model (COM) cousin, where drag and drop was born. WinForms continues to work with the COM-based protocol so that managed and unmanaged applications can participate in drag-and-drop operations between each other.

Furthermore, the COM-based protocol itself is based on the Windows convention for the way the Clipboard works. All data passed around using drag and drop is represented in Clipboard formats. Some Clipboard formats are customized for your own application, and others are well known to allow Clipboard and drag-and-drop operations between applications. The format strings used to specify the well-known formats are predefined as static fields of the DataFormats class:

 
 Class DataFormats   ' Fields   Public Shared ReadOnly Bitmap As String   Public Shared ReadOnly CommaSeparatedValue As String   Public Shared ReadOnly Dib As String   Public Shared ReadOnly Dif As String   Public Shared ReadOnly EnhancedMetafile As String   Public Shared ReadOnly FileDrop As String   Public Shared ReadOnly Html As String   Public Shared ReadOnly Locale As String   Public Shared ReadOnly MetafilePict As String   Public Shared ReadOnly OemText As String   Public Shared ReadOnly Palette As String   Public Shared ReadOnly PenData As String   Public Shared ReadOnly Riff As String   Public Shared ReadOnly Rtf As String   Public Shared ReadOnly Serializable As String   Public Shared ReadOnly StringFormat As String   Public Shared ReadOnly SymbolicLink As String   Public Shared ReadOnly Text As String   Public Shared ReadOnly Tiff As String   Public Shared ReadOnly UnicodeText As String   Public Shared ReadOnly WaveAudio As String   ' Methods   Shared Overloads Function GetFormat(format As String) _      As DataFormats.Format   Shared Overloads Function GetFormat(id As Integer) _      As DataFormats.Format End Class 

In addition to support for well-known data formats, .NET provides a conversion from some .NET types, such as String, to a corresponding format string, such as DataFormats.Text. Using a format string and the GetDataPresent method of the IDataObject, the target can determine whether the type of data being dragged is acceptable for a drop:

 
 Sub textBox3_DragEnter(sender As Object, e As DragEventArgs)   ' Could check against DataForms.Text as well   If e.Data.GetDataPresent(GetType(String)) Then       e.Effect = DragDropEffects.Copy   Else       e.Effect = DragDropEffects.None   End If End Sub 

GetDataPresent checks the format of the data to see whether it matches the Clipboard format (or a .NET type converted to a Clipboard format). To find out whether the data is in a convertible format, you can call the GetFormats() function, which returns an array of formats. Calling any of the IDataObject methods with the autoConvert parameter set to false will disable anything except a direct match of data types.

If the data is acceptable, the DragEnter event handler must set the Effect property of the DragEffectArgs object to one or more flags indicating what the control is willing to do with the data if it's dropped, as determined by the flags in the DragDropEffects enumeration:

 
 Enum DragDropEffects   Copy ' Take a copy of the data   Move ' Take ownership of the data   Link ' Link to the data   Scroll ' Scrolling is happening in the target   All ' All of the above   None ' Reject the data End Enum 

If a drop is allowed and it happens while the mouse is over the target, the target control will receive the DragDrop event:

 
 Sub textBox3_DragDrop(sender As Object, e As DragEventArgs)   textBox3.Text = CType(e.Data.GetData(GetType(String)), String) End Sub 

When you implement the DragDrop handler, the Effect property of the DragEventArgs will be the effect that the source and target agreed on, should multiple effects be allowed. Retrieving the data is a matter of calling GetData ”using either a DataFormat format string or a .NET Type object ”and casting the result.

Drop Targets and COM

When you enable a control as a target, you open yourself up to the possibility that the user will receive the cryptic message shown in Figure 8.24.

Figure 8.24. Cryptic Drag-and-Drop Error Message

Because drag and drop is a feature provided using COM, COM must be initialized on the UI thread for drag and drop to work. Although .NET is smart enough to lazily initialize COM on the running thread as needed, for reasons of efficiency it picks the UI- hostile Multi-Threaded Apartment (MTA) for the thread to join unless told to do otherwise . Unfortunately, for drag and drop, the UI thread must join the Single-Threaded Apartment (STA). To ensure that that's the case, always double-check that the Main function on all your WinForms applications is marked with STAThreadAttribute:

 
 <STAThread()> _ Shared Sub Main()   Application.Run(New Form1()) End Sub 

By default, any VS.NET-generated code will contain this attribute on the Main function (even Console applications), but just in case it somehow goes missing, this is the first thing to check when you see the message box from Figure 8.24.

Drag Source

With the target implemented, what's left is initiating a drag-and-drop operation from the drop source using the DoDragDrop method of the Control class. DoDragDrop is almost always placed in the handler for a MouseDown event:

 
 Sub button3_MouseDown(sender As Object, e As MouseEventArgs)   ' Start a drag-and-drop operation   DoDragDrop(button3.Text, DragDropEffects.Copy) End Sub 

The DoDragDrop method's first parameter is the data, which can be any object. The second parameter is a combination of the drag-and-drop effects that the source supports. For example, Figure 8.25 shows the button initiating the drag and drop.

Figure 8.25. A Drag-and-Drop Operation Showing the None Effect

As the drag-and-drop operation progresses, the DoDragDrop method tracks the mouse as it moves over controls, looking to see whether they are potential drop targets (as set with the AllowDrop property) and firing the DragEnter event to see whether potential targets can accept the data. Depending on whether the target can accept the data, DoDragDrop sets the cursor based on the current effect indicated by the target to communicate to users what would happen if they were to drop at any point. Notice in Figure 8.25 that the button itself is not a drop target, so the cursor indicates that a drop on the button would have no effect.

On the other hand, when the data is dragged over a text box that is enabled to accept string data, the DragEnter event is fired, and the control indicates the effect that it will support. This causes the cursor to be updated appropriately, as shown in Figure 8.26.

Figure 8.26. Drop Target Indicating the Copy Effect

When the user releases the mouse button, dropping the data, the DragDrop event is fired on the target, and the target accepts the data, as shown in Figure 8.27.

Figure 8.27. Completed Drag-and-Drop Copy Operation

When the drag and drop is completed, the DoDragDrop method returns with the effect that was performed. If the effect was a Move, the source knows to remove its copy of the data.

Supporting Multiple Effects

If you want to support more than one effect, such as Copy and Move, you can check the KeyState property of the DragEventArgs structure. KeyState is a set of flags that determines which keys are being pressed. By Windows convention, the lack of modifier keys indicates a Move, the Ctrl modifier indicates a Copy, and the Ctrl+Shift modifier indicates a Link (which your application may or may not support).

Unfortunately, the KeyState property is an integer, and WinForms provides no data type for checking the flags. So you'll need your own, such as this KeyState enumeration: [8]

[8] The FlagsAttribute makes instances of the KeyState enumeration show up in a friendlier manner, such as "LeftMouse, CtrlKey" instead of "9."

 
 ' KeyState Values (not available in WinForms) <Flags()> _ Enum KeyState   LeftMouse = 1   RightMouse = 2   ShiftKey = 4   CtrlKey = 8   MiddleMouse = 16   AltKey = 32 End Enum 

Because users may change the keys they're pressing at any time to get the effect they're looking for, you will want to notify the drag operation of which operation they are trying to do while the mouse is hovering . To do this you check the DragEnter and DragOver events:

 
 Sub textBox3_DragEnter(sender As Object, e As DragEventArgs)   SetDropEffects(e) End Sub Sub textBox3_DragOver(sender As Object, e As DragEventArgs)   SetDropEffects(e) End Sub Sub SetDropEffects(e As DragEventArgs)   Dim mykeyState As KeyState = CType(e.KeyState, KeyState)   ' If the data is a string, we can handle it   If e.Data.GetDataPresent(GetType(String)) Then       ' If only Ctrl is pressed, copy it       If ((mykeyState And KeyState.CtrlKey) = KeyState.CtrlKey) Then           e.Effect = DragDropEffects.Copy       Else ' Else, move it           e.Effect = DragDropEffects.Move       End If   ' We don't like the data, so do not allow anything   Else       e.Effect = DragDropEffects.None   End If End Sub 

The SetDropEffect method makes sure that the data is a string because that is all we are expecting. If it finds a string, it tests to see whether the Ctrl key is pressed. If so, it specifies that the operation is a copy; otherwise, it specifies that it will do a move. Figure 8.28 shows what the drag operation now looks like over the text box without the Ctrl key pressed, indicating a move effect.

Figure 8.28. Dragging without Ctrl, Causing a Move

Figure 8.29 shows the same operation with the Ctrl key pressed, indicating a copy effect.

Figure 8.29. Dragging with Ctrl, Causing a Copy

In our sample, when the user drops the data with no modifiers, indicating a move, the text is removed from the button when it drops the text to the text box, as shown in Figure 8.30.

Figure 8.30. After a Drag-and-Drop Move Operation

To handle multiple effects in the drag source, you must specify which effects are allowed and check the resulting effect after the DoDragDrop method returns:

 
 Sub button3_MouseDown(sender As Object, e As MouseEventArgs)   Dim effect As DragDropEffects = DoDragDrop(button3.Text, _       DragDropEffects.Copy Or DragDropEffects.Move)   ' If the effect was move, remove the text of the button   ' If the effect was copy, we don't have anything to do   If effect = DragDropEffects.Move Then       Button3.Text = ""   End If End Sub 

Drag and drop is a great way to allow your mouse-oriented users to directly manipulate the data that your application presents without an undue development burden on you.



Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net