Drag and Drop API

We already looked at what can be done using drag and drop in Revit 2012.

At that point, an external Windows application could be used to initiate a standard Revit drag and drop sequence, but we had no control over Revit's behaviour on receiving the dropped files.

This has changed in the Revit 2013 API, which provides a drag and drop API as part of the add-in integration features and includes the UIAPI sample in the Revit SDK to demonstrate its use, among several other new API features such as discipline dependent application availability, preview control, and a custom WPF options dialogue.

By the way, the UIAPI sample differs from many other SDK samples in a couple of points:

To make up for that, here is a pretty detailed description of its drag and drop functionality, at least.

UIAPI SDK Sample Drag and Drop

The UIAPI drag and drop command displays the following modeless dialogue:

UIAPI DragAndDrop form

It presents a list of loaded furniture family symbols on the left and all furniture family files found by recursively searching the content library on the right.

UIApplication DoDragDrop Method Overloads

The UIApplication now provides two overloads of the new static method DoDragDrop for interacting with drag and drop events:

This method and its same two overloads are also provided on the macro-specific ApplicationEntryPoint class, for Revit macro use only.

Drag and Drop a List of Files

In the first overload, the method argument holds a list of paths and names of files to be dropped on the Revit user interface, which causes the following default behaviour:

Here again, this behaviour is built into Revit and cannot be modified.

The code initiating the drag and drop from the modeless dialogue is as simple as this:

  // Drag action from list box
  private void listBox1_MouseMove( 
    object sender, 
    MouseEventArgs e )
  {
    if( System.Windows.Forms.Control.MouseButtons 
      == MouseButtons.Left )
    {
      FamilyListBoxMember member 
        = (FamilyListBoxMember) listBox1.SelectedItem;
 
      // Use standard Revit drag and drop behavior
 
      List<String> data = new List<String>();
      data.Add( member.FullPath );
      UIApplication.DoDragDrop( data );
    }
  }

In other words, dragging a file name from the list on the right onto Revit will trigger the same built-in default behaviour as dragging the same file from the Windows explorer.

Yet another Caveat against Invoking Revit API Methods Modelessly

Note that both this method and the one discussed below call the static UIApplication DoDragDrop method from a modeless dialogue, i.e. outside a valid Revit API context, which is unusual for the Revit API. This is most likely going to be changed in the sample, as it will almost certainly cause serious problems such as unexpected results, corrupted documents, crashes, etc. if utilised in a commercial application. A better approach would be to use the Idling or an external event to trigger the DragDrop activity at a later time.

The only safe option is to call DoDragDrop from an Idling or external event call-back, just like all other Revit API calls.

Actually, on further discussion, we realised that the situation is even worse: invoking the drag-and-drop method during both Idling and external events is never recommendable, because it is likely to cause deadlock.

The problem is that the user interface is not expecting to be invoked during idling; the application is idling for the exact reason that there is no UI activity. If UI operations are invoked in this situation, two things may happen:

Unfortunately, this means that there really is no totally safe way to utilise this wonderful new API from a modeless dialogue. We will obviously be taking a new look at this in future versions, but for now it is important to realise the risks of invoking the Revit UI during the Idling event.

Here are some ideas which may help relativate these worries:

So once again: make sure that you test the scenario in which you plan to use this very carefully, and avoid all risks in a commercial implementation.

Drag and Drop with a Custom Handler

The second overload is much more exciting, because it allows us to define our own drop behaviour inside Revit.

This can only be used to define behaviour within the context of the add-in's own UI, though. For example, it does not allow you to define new drag and drop behaviour for files which are not supported by the method above when dropped onto Revit from explorer, or from an unconnected application like Excel, AutoCAD, etc. The drag operation must be initiated from a point where it can call DoDragDrop to Revit to allow it to complete.

It requires us to set up a handler for the drop event, which is derived from the IDropHandler interface, requiring then implementation of one single method, Execute.

In this sample, the drop handler expects the element id of a family symbol to be passed in and simply calls the PromptForFamilyInstancePlacement method on the symbol.

You are obviously completely free to pass in any data you like when invoking the handler, and can choose to do something completely different with it inside Revit on receipt:

  /// <summary>
  /// Custom handler for placement of loaded family types
  /// </summary>
  public class LoadedFamilyDropHandler : IDropHandler
  {
    public void Execute( 
      UIDocument doc, 
      object data )
    {
      ElementId familySymbolId = (ElementId) data;
 
      FamilySymbol symbol = doc.Document.GetElement( 
        familySymbolId ) as FamilySymbol;
 
      if( symbol != null )
      {
        doc.PromptForFamilyInstancePlacement( 
          symbol );
      }
    }
  }

With the drop handler in place, we can initiate a custom drag and drop like this:

  // Drag action from list view
  private void listView_MouseMove( 
    object sender, 
    MouseEventArgs e )
  {
    if( System.Windows.Forms.Control.MouseButtons 
      == MouseButtons.Left )
    {
      ListViewItem selectedItem = this.listView1
        .SelectedItems.Cast<ListViewItem>()
        .FirstOrDefault<ListViewItem>();
 
      if( selectedItem != null )
      {
        // Use custom Revit drag and drop behavior
 
        LoadedFamilyDropHandler myhandler 
          = new LoadedFamilyDropHandler();
 
        UIApplication.DoDragDrop( 
          selectedItem.Tag, myhandler );
      }
    }
  }

Be sure to take a look at the other commands defined by the UIAPI SDK sample as well, because they are all pretty exciting and address a number of long-standing wish list items.