Enabling Drag and Drop


Drag and Drop operations involve two elements: the drag source and the drop target.

The Drag Source

You initiate a drag-and-drop operation from a drag source, the control that contains the source data. This requires using the DoDragDrop method of the Control class. DoDragDrop is almost always placed in the handler for a MouseDown event:[1]

[1] As you'll see later, ListView exposes a special ItemDrag event specifically for drag and drop.

// MainForm.cs partial class MainForm : Form {   void dragSourceLabel_MouseDown(object sender, MouseEventArgs e) {     // Start a drag and drop operation     this.DoDragDrop(this.dragSourceLabel.Text, DragDropEffects.Copy);   } }


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.

The Drop Target

With the drag source in place, you now establish the drop target, a control that supports having things dragged and dropped onto it. A drop target can be any control on a form, or the form itself. You designate your drop target by setting its AllowDrop property to true:

// MainForm.cs partial class MainForm : Form {   void MainForm_Load(object sender, EventArgs e) {     // Enable drop target     this.dropTargetTextBox.AllowDrop = true;   }   ... }


Next, you subscribe the drop target 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 handle the DragEnter event, or else they can't accept any dropped data. The DragEnter event comes with an instance of the DragEventArgs class, which gives the source information about the data:

namespace System.Windows.Forms {   class DragEventArgs : EventArgs {     // Properties     public DragDropEffects AllowedEffect { get; }     public IDataObject Data { get; }     public DragDropEffects Effect { get; set; }     public int KeyState { get; }     public int X { get; }     public int Y { get; }   } }


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:

namespace System.Windows.Forms {   interface IDataObject {     // Methods     public virtual object GetData(string format, bool autoConvert);     public virtual object GetData(string format);     public virtual object GetData(Type format);     public virtual void       SetData(string format, bool autoConvert, object data);     public virtual void SetData(string format, object data);     public virtual void SetData(Type format, object data);     public virtual void SetData(object data);     public virtual bool GetDataPresent(string format, bool autoConvert);     public virtual bool GetDataPresent(string format);     public virtual bool GetDataPresent(Type format);     public virtual string[] GetFormats(bool autoConvert);     public virtual string[] GetFormats();   } }


The IDataObject interface is actually defined from its Component Object Model (COM) cousin, where drag and drop was born. Windows Forms continues to work with the COMbased protocol so that managed and unmanaged applications can participate in drag-anddrop 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:

namespace System.Windows.Forms {   class DataFormats {     // Fields     public static readonly string Bitmap;     public static readonly string CommaSeparatedValue;     public static readonly string Dib;     public static readonly string Dif;     public static readonly string EnhancedMetafile;     public static readonly string FileDrop;     public static readonly string Html;     public static readonly string Locale;     public static readonly string MetafilePict;     public static readonly string OemText;     public static readonly string Palette;     public static readonly string PenData;     public static readonly string Riff;     public static readonly string Rtf;     public static readonly string Serializable;     public static readonly string StringFormat;     public static readonly string SymbolicLink;     public static readonly string Text;     public static readonly string Tiff;     public static readonly string UnicodeText;     public static readonly string WaveAudio;     // Methods     public static DataFormats.Format GetFormat(string format);     public static DataFormats.Format GetFormat(int id);   } }


In addition to supporting well-known data formats, .NET provides a conversion from some .NET types, such as String, to the compatible formats, such as DataFormats.Text and DataFormats.UnicodeText. Using the GetDataPresent method of the IDataObject, the target determines whether the type of data being dragged is acceptable for a drop:

// MainForm.cs partial class MainForm : Form {   ...   void dropTargetTextBox_DragEnter(object sender, DragEventArgs e) {     // Could check against DataFormats.Text as well     if (e.Data.GetDataPresent(typeof(string))) {       e.Effect = DragDropEffects.Copy;     }     else {       e.Effect = DragDropEffects.None;     }   } }


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 call the GetFormats method, which returns an array of formats. Calling any of the IDataObject methods with the autoConvert parameter set to false disables anything except a direct match of data types.

If the data is acceptable, the DragEnter event handler must set the Effect property of the DragEventArgs 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 DragDropEffects:

namespace System.Windows.Forms {   enum DragDropEffects {     Scroll = -2147483648, // Scrolling is happening in the target     All = -2147483645, // Data is copied and removed from the drag                        // source, and scrolls in the drop target     None = 0, // Reject the data     Copy = 1, // Take a copy of the data     Move = 2, // Take ownership of the data     Link = 4, // Link to the data   }   ... }


If a drop is allowed and it happens while the mouse is over the target, the target control receives the DragDrop event. You handle this event to retrieve the dragged data and to process the drop onto the target according to the chosen DragDropEffects:

// MainForm.cs partial class MainForm : Form {   ...   void dropTargetTextBox_DragDrop(object sender, DragEventArgs e) {     // Handles both DragDropEffect.Move and DragDropEffect.Copy     // Retrieve drag data and drop it onto target     string dragData = (string)e.Data.GetData(typeof(string));     this.dropTargetTextBox.Text = dragData;   } }


When you implement the DragDrop handler, the Effect property of DragEventArgs is one of the effects that the source and target agreed on. Retrieving the data is a matter of calling GetDatausing either a DataFormat format string or a .NET Type objectand 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 E.1.

Figure E.1. 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 to work, the UI thread must join a Single-Threaded Apartment (STA). To ensure that that's the case, always double-check that the Main entry-point method on all your Windows Forms applications is marked with the STAThread attribute:

// Program.cs static class Program {   [STAThread]   static void Main() {     Application.EnableVisualStyles();     Application.SetCompatibleTextRenderingDefault(false);     Application.Run(new MainForm());   } }


By default, all VS05-generated code contains this attribute on the Main function (even Console applications), but just in case it somehow goes missing or you aren't using VS05, this is the first thing to check when you see the exception dialog shown in Figure E.1.

Drag and Drop in Operation

With a drag source and drop target in place, let's look at how a drag-and-drop operation takes place. Drag and drop is initiated when a user presses and holds the left mouse button over the drag source, at which point DoDragDrop is called. Figure E.2 illustrates a drag-and-drop operation initiated from the source Label.

Figure E.2. Initiating a Drag-and-Drop Operation


Because the drag source Label is not a drop target, the drag-and-drop effect is None, which explains the special mouse cursor shown in Figure E.2. 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, thereby communicating to users what would happen if they were to drop at any point. Notice in Figure E.2 that the label 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 E.3.

Figure E.3. 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 E.4.

Figure E.4. Completed Drag-and-Drop Copy Operation


When the drag and drop is completed, the DoDragDrop method returns with the effect that was performed, something that can be useful when you need to support multiple drag effects, such as the standard Copy and Move.




Windows Forms 2.0 Programming
Windows Forms 2.0 Programming (Microsoft .NET Development Series)
ISBN: 0321267966
EAN: 2147483647
Year: 2006
Pages: 216

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