Muokkaa

Jaa


Drag and Drop Overview

This topic provides an overview of drag-and-drop support in Windows Presentation Foundation (WPF) applications. Drag-and-drop commonly refers to a method of data transfer that involves using a mouse (or some other pointing device) to select one or more objects, dragging these objects over some desired drop target in the user interface (UI), and dropping them.

Drag-and-Drop Support in WPF

Drag-and-drop operations typically involve two parties: a drag source from which the dragged object originates and a drop target which receives the dropped object. The drag source and drop target may be UI elements in the same application or a different application.

The type and number of objects that can be manipulated with drag-and-drop is completely arbitrary. For example, files, folders, and selections of content are some of the more common objects manipulated through drag-and-drop operations.

The particular actions performed during a drag-and-drop operation are application specific, and often determined by context. For example, dragging a selection of files from one folder to another on the same storage device moves the files by default, whereas dragging files from a Universal Naming Convention (UNC) share to a local folder copies the files by default.

The drag-and-drop facilities provided by WPF are designed to be highly flexible and customizable to support a wide variety of drag-and-drop scenarios. Drag-and-drop supports manipulating objects within a single application, or between different applications. Dragging-and-dropping between WPF applications and other Windows applications is also fully supported.

In WPF, any UIElement or ContentElement can participate in drag-and-drop. The events and methods required for drag-and-drop operations are defined in the DragDrop class. The UIElement and ContentElement classes contain aliases for the DragDrop attached events so that the events appear in the class members list when a UIElement or ContentElement is inherited as a base element. Event handlers that are attached to these events are attached to the underlying DragDrop attached event and receive the same event data instance. For more information, see the UIElement.Drop event.

Important

OLE drag-and-drop does not work while in the Internet zone.

Data Transfer

Drag-and-drop is part of the more general area of data transfer. Data transfer includes drag-and-drop and copy-and-paste operations. A drag-and-drop operation is analogous to a copy-and-paste or cut-and-paste operation that is used to transfer data from one object or application to another by using the system clipboard. Both types of operations require:

  • A source object that provides the data.

  • A way to temporarily store the transferred data.

  • A target object that receives the data.

In a copy-and-paste operation, the system clipboard is used to temporarily store the transferred data; in a drag-and-drop operation, a DataObject is used to store the data. Conceptually, a data object consists of one or more pairs of an Object that contains the actual data, and a corresponding data format identifier.

The drag source initiates a drag-and-drop operation by calling the static DragDrop.DoDragDrop method and passing the transferred data to it. The DoDragDrop method will automatically wrap the data in a DataObject if necessary. For greater control over the data format, you can wrap the data in a DataObject before passing it to the DoDragDrop method. The drop target is responsible for extracting the data from the DataObject. For more information about working with data objects, see Data and Data Objects.

The source and target of a drag-and-drop operation are UI elements; however, the data that is actually being transferred typically does not have a visual representation. You can write code to provide a visual representation of the data that is dragged, such as occurs when dragging files in Windows Explorer. By default, feedback is provided to the user by changing the cursor to represent the effect that the drag-and-drop operation will have on the data, such as whether the data will be moved or copied.

Drag-and-Drop Effects

Drag-and-drop operations can have different effects on the transferred data. For example, you can copy the data or you can move the data. WPF defines a DragDropEffects enumeration that you can use to specify the effect of a drag-and-drop operation. In the drag source, you can specify the effects that the source will allow in the DoDragDrop method. In the drop target, you can specify the effect that the target intends in the Effects property of the DragEventArgs class. When the drop target specifies its intended effect in the DragOver event, that information is passed back to the drag source in the GiveFeedback event. The drag source uses this information to inform the user what effect the drop target intends to have on the data. When the data is dropped, the drop target specifies its actual effect in the Drop event. That information is passed back to the drag source as the return value of the DoDragDrop method. If the drop target returns an effect that is not in the drag sources list of allowedEffects, the drag-and-drop operation is cancelled without any data transfer occurring.

It is important to remember that in WPF, the DragDropEffects values are only used to provide communication between the drag source and the drop target regarding the effects of the drag-and-drop operation. The actual effect of the drag-and-drop operation depends on you to write the appropriate code in your application.

For example, the drop target might specify that the effect of dropping data on it is to move the data. However, to move the data, it must be both added to the target element and removed from the source element. The source element might indicate that it allows moving the data, but if you do not provide the code to remove the data from the source element, the end result will be that the data is copied, and not moved.

Drag-and-Drop Events

Drag-and-drop operations support an event driven model. Both the drag source and the drop target use a standard set of events to handle drag-and-drop operations. The following tables summarize the standard drag-and-drop events. These are attached events on the DragDrop class. For more information about attached events, see Attached Events Overview.

Drag Source Events

Event Summary
GiveFeedback This event occurs continuously during a drag-and-drop operation, and enables the drop source to give feedback information to the user. This feedback is commonly given by changing the appearance of the mouse pointer to indicate the effects allowed by the drop target. This is a bubbling event.
QueryContinueDrag This event occurs when there is a change in the keyboard or mouse button states during a drag-and-drop operation, and enables the drop source to cancel the drag-and-drop operation depending on the key/button states. This is a bubbling event.
PreviewGiveFeedback Tunneling version of GiveFeedback.
PreviewQueryContinueDrag Tunneling version of QueryContinueDrag.

Drop Target Events

Event Summary
DragEnter This event occurs when an object is dragged into the drop target's boundary. This is a bubbling event.
DragLeave This event occurs when an object is dragged out of the drop target's boundary. This is a bubbling event.
DragOver This event occurs continuously while an object is dragged (moved) within the drop target's boundary. This is a bubbling event.
Drop This event occurs when an object is dropped on the drop target. This is a bubbling event.
PreviewDragEnter Tunneling version of DragEnter.
PreviewDragLeave Tunneling version of DragLeave.
PreviewDragOver Tunneling version of DragOver.
PreviewDrop Tunneling version of Drop.

To handle drag-and-drop events for instances of an object, add handlers for the events listed in the preceding tables. To handle drag-and-drop events at the class level, override the corresponding virtual On*Event and On*PreviewEvent methods. For more information, see Class Handling of Routed Events by Control Base Classes.

Implementing Drag-and-Drop

A UI element can be a drag source, a drop target, or both. To implement basic drag-and-drop, you write code to initiate the drag-and-drop operation and to process the dropped data. You can enhance the drag-and-drop experience by handling optional drag-and-drop events.

To implement basic drag-and-drop, you will complete the following tasks:

  • Identify the element that will be a drag source. A drag source can be a UIElement or a ContentElement.

  • Create an event handler on the drag source that will initiate the drag-and-drop operation. The event is typically the MouseMove event.

  • In the drag source event handler, call the DoDragDrop method to initiate the drag-and-drop operation. In the DoDragDrop call, specify the drag source, the data to be transferred, and the allowed effects.

  • Identify the element that will be a drop target. A drop target can be UIElement or a ContentElement.

  • On the drop target, set the AllowDrop property to true.

  • In the drop target, create a Drop event handler to process the dropped data.

  • In the Drop event handler, extract the data from the DragEventArgs by using the GetDataPresent and GetData methods.

  • In the Drop event handler, use the data to perform the desired drag-and-drop operation.

You can enhance your drag-and-drop implementation by creating a custom DataObject and by handling optional drag source and drop target events, as shown in the following tasks:

  • To transfer custom data or multiple data items, create a DataObject to pass to the DoDragDrop method.

  • To perform additional actions during a drag, handle the DragEnter, DragOver, and DragLeave events on the drop target.

  • To change the appearance of the mouse pointer, handle the GiveFeedback event on the drag source.

  • To change how the drag-and-drop operation is canceled, handle the QueryContinueDrag event on the drag source.

Drag-and-Drop Example

This section describes how to implement drag-and-drop for an Ellipse element. The Ellipse is both a drag source and a drop target. The transferred data is the string representation of the ellipse’s Fill property. The following XAML shows the Ellipse element and the drag-and-drop related events that it handles. For complete steps on how to implement drag-and-drop, see Walkthrough: Enabling Drag and Drop on a User Control.

<Ellipse Height="50" Width="50" Fill="Green"
     MouseMove="ellipse_MouseMove"
     GiveFeedback="ellipse_GiveFeedback"
     AllowDrop="True"
     DragEnter="ellipse_DragEnter" DragLeave="ellipse_DragLeave"
     DragOver="ellipse_DragOver" Drop="ellipse_Drop" />

Enabling an Element to be a Drag Source

An object that is a drag source is responsible for:

  • Identifying when a drag occurs.

  • Initiating the drag-and-drop operation.

  • Identifying the data to be transferred.

  • Specifying the effects that the drag-and-drop operation is allowed to have on the transferred data.

The drag source may also give feedback to the user regarding the allowed actions (move, copy, none), and can cancel the drag-and-drop operation based on additional user input, such as pressing the ESC key during the drag.

It is the responsibility of your application to determine when a drag occurs, and then initiate the drag-and-drop operation by calling the DoDragDrop method. Typically, this is when a MouseMove event occurs over the element to be dragged while a mouse button is pressed. The following example shows how to initiate a drag-and-drop operation from the MouseMove event handler of an Ellipse element to make it a drag source. The transferred data is the string representation of the ellipse’s Fill property.

private void ellipse_MouseMove(object sender, MouseEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null && e.LeftButton == MouseButtonState.Pressed)
    {
        DragDrop.DoDragDrop( ellipse,
                             ellipse.Fill.ToString(),
                             DragDropEffects.Copy);
    }
}
Private Sub Ellipse_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing AndAlso e.LeftButton = MouseButtonState.Pressed Then
        DragDrop.DoDragDrop(ellipse, ellipse.Fill.ToString(), DragDropEffects.Copy)
    End If
End Sub

Inside of the MouseMove event handler, call the DoDragDrop method to initiate the drag-and-drop operation. The DoDragDrop method takes three parameters:

  • dragSource – A reference to the dependency object that is the source of the transferred data; this is typically the source of the MouseMove event.

  • data - An object that contains the transferred data, wrapped in a DataObject.

  • allowedEffects - One of the DragDropEffects enumeration values that specifies the permitted effects of the drag-and-drop operation.

Any serializable object can be passed in the data parameter. If the data is not already wrapped in a DataObject, it will automatically be wrapped in a new DataObject. To pass multiple data items, you must create the DataObject yourself, and pass it to the DoDragDrop method. For more information, see Data and Data Objects.

The allowedEffects parameter is used to specify what the drag source will allow the drop target to do with the transferred data. The common values for a drag source are Copy, Move, and All.

Note

The drop target is also able to specify what effects it intends in response to the dropped data. For example, if the drop target does not recognize the data type to be dropped, it can refuse the data by setting its allowed effects to None. It typically does this in its DragOver event handler.

A drag source can optionally handle the GiveFeedback and QueryContinueDrag events. These events have default handlers that are used unless you mark the events as handled. You will typically ignore these events unless you have a specific need to change their default behavior.

The GiveFeedback event is raised continuously while the drag source is being dragged. The default handler for this event checks whether the drag source is over a valid drop target. If it is, it checks the allowed effects of the drop target. It then gives feedback to the end user regarding the allowed drop effects. This is typically done by changing the mouse cursor to a no-drop, copy, or move cursor. You should only handle this event if you need to use custom cursors to provide feedback to the user. If you handle this event, be sure to mark it as handled so that the default handler does not override your handler.

The QueryContinueDrag event is raised continuously while the drag source is being dragged. You can handle this event to determine what action ends the drag-and-drop operation based on the state of the ESC, SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons. The default handler for this event cancels the drag-and-drop operation if the ESC key is pressed, and drops the data if the mouse button is released.

Caution

These events are raised continuously during the drag-and-drop operation. Therefore, you should avoid resource-intensive tasks in the event handlers. For example, use a cached cursor instead of creating a new cursor each time the GiveFeedback event is raised.

Enabling an Element to be a Drop Target

An object that is a drop target is responsible for:

  • Specifying that it is a valid drop target.

  • Responding to the drag source when it drags over the target.

  • Checking that the transferred data is in a format that it can receive.

  • Processing the dropped data.

To specify that an element is a drop target, you set its AllowDrop property to true. The drop target events will then be raised on the element so that you can handle them. During a drag-and-drop operation, the following sequence of events occurs on the drop target:

  1. DragEnter

  2. DragOver

  3. DragLeave or Drop

The DragEnter event occurs when the data is dragged into the drop target's boundary. You typically handle this event to provide a preview of the effects of the drag-and-drop operation, if appropriate for your application. Do not set the DragEventArgs.Effects property in the DragEnter event, as it will be overwritten in the DragOver event.

The following example shows the DragEnter event handler for an Ellipse element. This code previews the effects of the drag-and-drop operation by saving the current Fill brush. It then uses the GetDataPresent method to check whether the DataObject being dragged over the ellipse contains string data that can be converted to a Brush. If so, the data is extracted using the GetData method. It is then converted to a Brush and applied to the ellipse. The change is reverted in the DragLeave event handler. If the data cannot be converted to a Brush, no action is performed.

private Brush _previousFill = null;
private void ellipse_DragEnter(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        // Save the current Fill brush so that you can revert back to this value in DragLeave.
        _previousFill = ellipse.Fill;

        // If the DataObject contains string data, extract it.
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

            // If the string can be converted into a Brush, convert it.
            BrushConverter converter = new BrushConverter();
            if (converter.IsValid(dataString))
            {
                Brush newFill = (Brush)converter.ConvertFromString(dataString);
                ellipse.Fill = newFill;
            }
        }
    }
}
Private _previousFill As Brush = Nothing
Private Sub Ellipse_DragEnter(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then
        ' Save the current Fill brush so that you can revert back to this value in DragLeave.
        _previousFill = ellipse.Fill

        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString = e.Data.GetData(DataFormats.StringFormat)

            ' If the string can be converted into a Brush, convert it.
            Dim converter As New BrushConverter()
            If converter.IsValid(dataString) Then
                Dim newFill As Brush = CType(converter.ConvertFromString(dataString), Brush)
                ellipse.Fill = newFill
            End If
        End If
    End If
End Sub

The DragOver event occurs continuously while the data is dragged over the drop target. This event is paired with the GiveFeedback event on the drag source. In the DragOver event handler, you typically use the GetDataPresent and GetData methods to check whether the transferred data is in a format that the drop target can process. You can also check whether any modifier keys are pressed, which will typically indicate whether the user intends a move or copy action. After these checks are performed, you set the DragEventArgs.Effects property to notify the drag source what effect dropping the data will have. The drag source receives this information in the GiveFeedback event args, and can set an appropriate cursor to give feedback to the user.

The following example shows the DragOver event handler for an Ellipse element. This code checks to see if the DataObject being dragged over the ellipse contains string data that can be converted to a Brush. If so, it sets the DragEventArgs.Effects property to Copy. This indicates to the drag source that the data can be copied to the ellipse. If the data cannot be converted to a Brush, the DragEventArgs.Effects property is set to None. This indicates to the drag source that the ellipse is not a valid drop target for the data.

private void ellipse_DragOver(object sender, DragEventArgs e)
{
    e.Effects = DragDropEffects.None;

    // If the DataObject contains string data, extract it.
    if (e.Data.GetDataPresent(DataFormats.StringFormat))
    {
        string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

        // If the string can be converted into a Brush, allow copying.
        BrushConverter converter = new BrushConverter();
        if (converter.IsValid(dataString))
        {
            e.Effects = DragDropEffects.Copy | DragDropEffects.Move;
        }
    }
}
Private Sub Ellipse_DragOver(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    e.Effects = DragDropEffects.None

    ' If the DataObject contains string data, extract it.
    If e.Data.GetDataPresent(DataFormats.StringFormat) Then
        Dim dataString = e.Data.GetData(DataFormats.StringFormat)

        ' If the string can be converted into a Brush, convert it.
        Dim converter As New BrushConverter()
        If converter.IsValid(dataString) Then
            e.Effects = DragDropEffects.Copy Or DragDropEffects.Move
        End If
    End If
End Sub

The DragLeave event occurs when the data is dragged out of the target's boundary without being dropped. You handle this event to undo anything that you did in the DragEnter event handler.

The following example shows the DragLeave event handler for an Ellipse element. This code undoes the preview performed in the DragEnter event handler by applying the saved Brush to the ellipse.

private void ellipse_DragLeave(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        ellipse.Fill = _previousFill;
    }
}
Private Sub Ellipse_DragLeave(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then
        ellipse.Fill = _previousFill
    End If
End Sub

The Drop event occurs when the data is dropped over the drop target; by default, this happens when the mouse button is released. In the Drop event handler, you use the GetData method to extract the transferred data from the DataObject and perform any data processing that your application requires. The Drop event ends the drag-and-drop operation.

The following example shows the Drop event handler for an Ellipse element. This code applies the effects of the drag-and-drop operation, and is similar to the code in the DragEnter event handler. It checks to see if the DataObject being dragged over the ellipse contains string data that can be converted to a Brush. If so, the Brush is applied to the ellipse. If the data cannot be converted to a Brush, no action is performed.

private void ellipse_Drop(object sender, DragEventArgs e)
{
    Ellipse ellipse = sender as Ellipse;
    if (ellipse != null)
    {
        // If the DataObject contains string data, extract it.
        if (e.Data.GetDataPresent(DataFormats.StringFormat))
        {
            string dataString = (string)e.Data.GetData(DataFormats.StringFormat);

            // If the string can be converted into a Brush,
            // convert it and apply it to the ellipse.
            BrushConverter converter = new BrushConverter();
            if (converter.IsValid(dataString))
            {
                Brush newFill = (Brush)converter.ConvertFromString(dataString);
                ellipse.Fill = newFill;
            }
        }
    }
}
Private Sub Ellipse_Drop(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs)
    Dim ellipse = TryCast(sender, Ellipse)
    If ellipse IsNot Nothing Then

        ' If the DataObject contains string data, extract it.
        If e.Data.GetDataPresent(DataFormats.StringFormat) Then
            Dim dataString = e.Data.GetData(DataFormats.StringFormat)

            ' If the string can be converted into a Brush, convert it.
            Dim converter As New BrushConverter()
            If converter.IsValid(dataString) Then
                Dim newFill As Brush = CType(converter.ConvertFromString(dataString), Brush)
                ellipse.Fill = newFill
            End If
        End If
    End If
End Sub

See also