Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Enable Your Windows Forms Applications to Drag-and-Drop Data Objects : Page 7

This article demonstrates how to import files from the Windows shell and how to enhance some UI controls to make them accept input via drag-and-drop. Notable examples are the TextBox and the PictureBox controls.

Dragging Text Out of a TextBox
It's pretty common that a drag operation starts when a MouseDown event is fired. Note that this is only the most intuitive scenario, certainly not a rule. Any event could be used to initiate a drag-and-drop procedure. Also note that certain controls have custom drag-specific events, as is the case with the ListView and TreeView controls. These controls fire the ItemDrag event to notify you that a drag operation is in course.

If you want to extend an existing control with drag-and-drop functionality, the first step is identifying the start event. For dragging text out of a TextBox, choose the MouseDown event. Setting up a drag operation is, in theory, pretty straightforward. You write a handler for the start event, place a call to the DoDragDrop method that all Control and Form objects expose, and finalize the operation when the method returns and you know the outcome of the operation (copy, move, or whatever). Does this sound like fun? Well, it's not for all controls. Try writing a MouseDown event handler for a TextBox and you'll run into trouble right away.

The TextBox control, in fact, does a lot of work in the MouseDown window event. To implement drag capabilities, you need to override the default behavior regarding text selection. To accomplish this, you can't just write handlers for known events; you really need to derive a new class and override the WndProc method. Overriding WndProc gives you enough power to control the message flow that makes each Windows Forms control reside and interact within the core Windows UI infrastructure. To write a successful WndProc override, you need to know a lot about Win32 SDK programming and messages.

Let's define a new TextBox class.

Namespace Wintellect Public Class TextBox Inherits System.Windows.Forms.TextBox End Class End Namespace

You also need to import a few constants. I don't know why Microsoft didn't create an enum type with all the Win32 SDK messages exposed as mnemonic constants. At any rate, here are the definitions you need for the messages of interest. I've extracted them out of the winuser.h header file that you find in the Visual Studio 98 installation, for example.

Private Const WM_LBUTTONDOWN As Integer = &H201 Private Const WM_LBUTTONUP As Integer = &H202 Private Const WM_SETCURSOR As Integer = &H20

The WndProc method is defined as follows:

Overrides Sub WndProc(ByRef m As Message) Select Case m.Msg Case WM_LBUTTONDOWN MyMouseDown(m) Case WM_SETCURSOR MySetCursor(m) Case Else MyBase.WndProc(m) End Select End Sub

You overrule the behavior of the TextBox in case a MouseDown message is received or a SetCursor. WM_LBUTTONDOWN arrives when the left mouse button is depressed, but before the mouse is moved or the button is released. WM_SETCURSOR is sent whenever the control needs to render the cursor. (The role of this message will be clear in a moment.) In all other cases, the control behaves as usual and the base WndProc method is invoked.

So what's going on and what are the goals of this code? The idea is to enable the following scenario: the user selects some text and moves the mouse over the selected text. When this happens, the mouse pointer changes to an arrow. (It is an I-beam by default.) To implement this feature effectively, you need to handle the low-level WM_SETCURSOR message. Incidentally, this is also the behavior of Word when you drag-and-drop text within a document window. The code below illustrates the SetCursor handler.

Private Sub MySetCursor(ByVal m As Message) If IsOverSelectedText(m.HWnd) Then Cursor.Current = Cursors.Arrow Else Cursor.Current = Cursors.IBeam End If End Sub

The Message class represents the information associated with each Windows message. The HWnd property is the handle of the underlying window. In this case, it corresponds to the Handle property of the TextBox. IsOverSelectedText is a helper function that retrieves the current position of the mouse and compares it to the selected text. IsOverSelectedText exploits the IsOnSelectedText member of the CARETINFO structure, as illustrated in Listing 3.

Function IsOverSelectedText(ByVal hWnd As IntPtr)_ As Boolean Dim ci As NativeMethods.CARETINFO ci = NativeMethods.GetCaretInfo(hWnd) Return ci.IsOnSelectedText End Function

In Listing 4, you can see the source code of the WM_LBUTTONDOWN message handler. When the mouse button is depressed, notice whether the mouse is over the selected text. If not, do as usual and yield to the base WndProc method. Otherwise, you can detect whether or not a drag operation has been started. How do you do that? A drag operation is defined as any movement of the mouse from the click point that exceeds a system measure. The .NET Framework uses the SystemInformation.DragSize constant to indicate this value. Although undocumented in the .NET Framework, there's a better way to detect a drag operation called a Win32 API function.

The function is named DragDetect and lives in the user32 DLL. The DragDetect function takes the handle of the window within which the drag takes place and the current mouse position. It returns a Boolean value. To simplify things and minimize data marshaling, I've defined a Win32 wrapper for this function: IsDragging. Here's the C++ source code:

BOOL APIENTRY IsDragging(HWND hwndTextBox) { // Get the current mouse position POINT pt; GetCursorPos(&pt); return DragDetect(hwndTextBox, pt); }

At this point, you're pretty much done. What remains to do is just calling DoDragDrop and packing the information for the target. You pass the text currently selected in the TextBox.

' Capture the outcome of the drag operation dropEffect = DoDragDrop(SelectedText, _ DragDropEffects.Copy)

Figure 4. Customized Drag/Drop: This is what drag-and-drop between custom TextBox controls looks like.
DoDragDrop is the method that governs the whole drag operation. The method fires the proper events to the target and to the source as needed. Pass in the data (plain text, in this case) and the allowed effects (only copy, in this case). When the method returns, the operation has completed and you must update the source accordingly. You need to do nothing special if the drag ended in a copy. If it ends in a move, cut the selected text from the source TextBox.

To top off the code, consider that when you drop onto another control, you should make sure that the original selection is restored on the source. At the same time, this should be avoided if the drag-and-drop takes place within the context of the same control. Figure 4 shows a sample application with two TextBox controls that support drag-and-drop.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date