User Interface / Data Exchange / Drag and Drop
In This Topic
    Drag and Drop
    In This Topic
     Drag and Drop Overview

    Drag-and-Drop (abbreviated hereafter as DnD) is a one publisher to one consumer data exchange mechanism. Neither the publisher nor the consumer care about each other - the only additional information that they exchange, except the data object being dragged and dropped, is the effect of the DnD operation. In NOV all operations related to DnD are managed by the static NDragDrop class. The User Input topic discusses the role that the NDragDrop static class plays as DnD target events dispatcher, so this topic focuses on other DnD details.

    DnD consists of the following sequence of actions:

    1. Request - the request of a DnD operation associates a data object with a drag drop source.
    2. Start - the DnD system gains control over the desktop and starts the requested DnD operation.
    3. Drag - the data object is dragged to a different visual object called drag drop target
    4. Drop – the data object is dropped on the current drag drop target (this step may not be performed if DnD is aborted)
    5. End - the DnD system lifts its control from the desktop and allows for another DnD request to be made.

    From the DnD definition we can clearly see that in DnD there are two distinct types of objects, apart from the data object being dragged and dropped and the DnD system – the drag drop source and the drag drop target.

     Drag Drop Source

    The drag drop source is the object that is associated with the data object being published by DnD. Its purpose is to expose some control over the DnD operation to the DnD initiator. In NOV the drag drop source is represented by an instance of the NDragDropSource class. In NOV you request a DnD operation with the NDragDrop.RequestDragDrop method, that receives two arguments – the drag drop source and the data object that needs to be dragged and dropped. The following code requests a DnD operation with a text string:

    Requesting Drag and Drop
    Copy Code
    // before doing anything, check whether the RequestDragDrop can potentially succeed
    if (NDragDrop.CanRequestDragDrop())
    {
        // create a data object containing an Unicode text string
        NDataObject dataObject = new NDataObject();
        dataObject.SetText("Dragged and dropped text");
        // create the drag drop source object - allows only to copy the string
        NDragDropSource dragDropSource = new NDragDropSource(ENDragDropEffects.Copy);
        // request a drag and drop operation at the earliest NApplication availability.
        NDragDrop.RequestDragDrop(dragDropSource, dataObject);
    }
    

    DnD in terms of traditional Win32 programming (WinForm and WPF too) is performed by directly calling the Ole32.dll DoDragDrop method, that is synchronous (i.e. returns only after the drag and drop operation is performed). As with traditional modal windows, this method follows the wrongful “blocking the caller” pattern, which is something that NOV never does.

    NOV aims to be event driven from start to end, and that is why the request for a DnD operation is always asynchronously fulfilled. The NDragDrop.RequestDragDrop always returns immediately. Subsequent NDragDrop.RequestDragDrop calls fail until the DnD system has fulfilled the first requested DnD operation, which occurs at the earliest NApplication possibility. This allows NOV to implement/integrate with DnD systems in Windowing Systems without a message queue (or where it is not accessible).

    The “blocking the caller” pattern also does not work well in UI event driven systems that follow the chain of responsibility pattern. Suppose the following scenario: Element B inside Element A, both starting DnD in their MouseDown handlers – first B starts synchronous drag and drop. The mouse moves around the desktop and the mouse buttons state also changes during drag and drop. By the time the synchronous drag and drop returns control to the code path in the original MouseDown in element B – everything has changed, yet this code path still assumes a MouseDown action with wrong screen coordinates. To tap the mess element A then performs a subsequent DnD that is totally out of place.

    A drag drop source is initiated with the effect that you want to allow on the drag drop source side (the effect is discussed later in this topic).
     
    A DnD operation is performed by a DnD system, that typically gains full control over the desktop and that is one of the reasons for having a drag drop source – it is the source side entry point with which the DnD system communicates. As part of this communication the NDragDropSource will raises the following events:

    DragStarting – raised just before the DnD system gains control over the desktop.

    QueryDragAction – raised when the DnD system detected a certain user event, and it wants to consult with the drag drop source whether to Continue, Abort or Drop (commit) the DnD operation. If you do not handle this event, NOV automatically takes the following decisions for you:

    - Esc is pressed – Abort
    - ModifiersChanged – Continue
    - If a mouse button is down and it is one of the buttons specified by the DropOnMouseButtonsDown (by default set to All) - Drop
    - If a mouse button is up and it is one of the buttons specified by the DropOnMouseButtonsUp (by default set to Left) – Drop
    - In all other cases - Continue.

    DragEnded - raised after the DnD system has lifted its control from the desktop and contains information about the final effect of the drag and drop operation.

     Drag Drop Target

    The drag drop target is automatically determined during a DnD operation. Because the DnD system is a subsystem of the current native Windows Manager, natively this is the top-most native window below the mouse pointer. The DnD system sends the native DnD notifications to the native window. Because NOV windows are peered by native windows (directly or indirectly), in NOV these notifications arrive as window peer callbacks of the NDragDrop dispatcher (see User Input for more info).

    In NOV the drag drop target is a collective name for the top-most input target, which is currently below the mouse pointer and its chain of ancestors. NDragDrop will raise the NDragDrop.DragEnterEvent, NDragDrop.DragInEvent, NDragDrop.DragOutEvent and NDragDrop.DragLeaveEvent events whenever it changes the current drag drop target.

    The DnD Action Events – NDragDrop.DragOverEvent and NDragDrop.DragDropEvent allow you to modify the drag drop effect. All drag drop target events carry information about the mouse position, the data object being dragged and the allowed effect (the effect that the drag drop source allows). Only DnD Action Events are however allowed to change the effect of a drag drop operation.

     Drag Drop Effects

    The DnD effects are represented by the ENDragDropEffects masked enumeration, that can be a combination of the Move, Copy and Link primary flags.

    The allowed drag and drop effects is specified by the source of data (when you construct a new NDragDropSource object). The allowed effects can be any combination of primary ENDragDropEffect flags – Move, Copy and Link (e.g. Move or Copy, Copy or Link, All etc), but cannot be None - meaning that the allowed effect is a mask of the effects that are allowed and cannot be none. There is no sense to perform a DnD operation in which the source of data allows nothing to the target. The NDragDropSource will throw an exception, if you attempt to initialize it with an effect mask equal to None.

    When NOV dispatches the NDragDrop.DragOverEvent to the current drag drop target, it allows the drag drop target to update the effect by setting the Effect property of the NDragOverEventArgs associated with the event. The value set to the Effect property needs to be a primary flag of the ENDragDropEffects masked enum (Move, Copy or Link) and can also be None. The validity of the set effect is automatically enforced by the NDragActionEventArgs.Effect property setter, which will not allow you to set an effect that is not a primary flag, or a non allowed effect. The following code shows the pattern that needs to be used when handling the NDragDrop.OverEvent:

    Handling a NDragDrop.OverEvent pattern
    Copy Code
    // fictional OnDragOver override of NInputElement
    protected override void OnDragOver(NDragActionEventArgs args)
    {
        // if operation is handled -> do nothing
        if (!args.Cancel)
        {
            // always start by setting effect to None. It will remain set to None if the following condition is true;
            // the source did not publish a data object with a format we are interested in,
            // or we cannot perform the action that is typically performed for the current keyboard modifiers state,
            // or the source did not allow the action that we can perfrom for the current keyboard modifiers state.
            args.Effect = ENDragDropEffects.None;
            // first you need to check whether the data object contains data in the desired format (this sample checks for Text)
            if (!args.DataObject.ContainsData(NDataFormat.TextFormat))
            {
                // if the Ctrl is pressed, you must typically copy the data if you can (this sample assumes you can)
                if (NKeyboard.CtrlPressed & (args.AllowedEffect & ENDragDropEffects.Copy) == ENDragDropEffects.Copy)
                {
                    args.Effect = ENDragDropEffects.Copy;
                }
                // if the Alt is pressed, you must typically link the data if you can (this sample assumes you can)
                else if (NKeyboard.AltPressed & (args.AllowedEffect & ENDragDropEffects.Link) == ENDragDropEffects.Link)
                {
                    args.Effect = ENDragDropEffects.Link;
                }
                // by default you need to move data if you can (this sample assumes you can)
                else if ((args.AllowedEffect & ENDragDropEffects.Move) == ENDragDropEffects.Move)
                {
                    args.Effect = ENDragDropEffects.Move;
                }
            }
            // mark event as handled if effect changed
            if (args.Effect != ENDragDropEffects.None)
            {
                args.Cancel = true;
            }
        }
        // call base
        base.OnDragOver(args);
    }
    
    It is important to handle the DragOver event not only because it affects the cursor in a DnD operation, but also because DragDrop is not raised when the current DnD effect is None. The effect in a DnD operation is persistent, meaning that if you have set an effect in DragOver, this effect will be subsequently passed to DragDrop. A DnD operation in NOV performs automatic reset of the effect to None, when the DnD enters or leaves the NOV Window and when NOV changes the drag over target (which usually occurs prior to raising DragOver for the current target).

    The pattern for handling the DragDrop event is similar to the DragOver handling pattern, except that you need to set the effect only after you have performed the respective operation – Link, Copy or Move. The last set effect in a DnD operation is passed back to the drag drop source, via its DragEnded event. A drag drop source that allows the Move operation, must typically handle this event and perform the associated data removal, if the final drag drop effect is Move.

    See Also