next previous home

1-2 Command Arguments

In this lab, we explore the contents and usage of the Execute() arguments. To do so, we add another command to the existing application by implementing another class derived from IExternalCommand in the same source file.

namespace LabsCode
{
  public class Lab1_2_CommandArguments : IExternalCommand
  {
    public IExternalCommand.Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
    {
      return IExternalCommand.Result.Succeeded;
    }
  }
}
Public Class Lab1_2_CommandArguments
    Implements IExternalCommand

    Public Function Execute( _
        ByVal commandData As ExternalCommandData, _
        ByRef message As String, _
        ByVal elements As ElementSet) _
    As Autodesk.Revit.IExternalCommand.Result _
    Implements Autodesk.Revit.IExternalCommand.Execute

        Return IExternalCommand.Result.Succeeded

    End Function

End Class

The first argument is the command data object which is used to access the application object, from it the current view and document, and from it in turn all or the currently selected elements.

Let's first report some data about the current application, document and view. Add something like the following to the Execute() function body:

  //
  // List the app, doc and view data:
  //
  Application revitApp = commandData.Application;
  Document doc = revitApp.ActiveDocument;
  View view = commandData.View;
  string sMsg = "Application = " + revitApp.VersionName
    + " " + revitApp.VersionNumber + "\r\n";
  sMsg += "Document path = " + doc.PathName + "\r\n"; // Empty if not saved yet
  sMsg += "Document title = " + doc.Title + "\r\n";
  sMsg += "View name = " + view.Name;
  LabUtils.InfoMsg( sMsg );
  ' List the app, doc and view data
  Dim revitApp As Revit.Application = commandData.Application
  Dim doc As Revit.Document = revitApp.ActiveDocument
  Dim view As Revit.Elements.View = commandData.View

  Dim sMsg As String = "Application = " & revitApp.VersionName & " " & revitApp.VersionNumber & vbCrLf
  sMsg += "Document path = " & doc.PathName & vbCrLf ' Empty if not saved yet
  sMsg += "Document title = " & doc.Title & vbCrLf
  sMsg += "View name = " & view.Name
  MsgBox(sMsg)

In VB, we can use the built-in MsgBox() function to display messages. In C#, however, this function is not available. We can use MessageBox.Show() instead, like in the Hello world demo we just looked at. Since it is cumbersome to add an additional argument for the caption each time we call it, we implement some message box helper functions in a separate class LabUtils. This is required only for C#:

using System;
using WinForms = System.Windows.Forms;

namespace Labs
{
  class LabUtils
  {
    /// <summary>
    /// MessageBox wrapper for informational message.
    /// </summary>
    public static void InfoMsg( string msg )
    {
      WinForms.MessageBox.Show( msg, "Revit API Labs", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Information );
    }

    /// <summary>
    /// MessageBox wrapper for error message.
    /// </summary>
    public static void ErrorMsg( string msg )
    {
      WinForms.MessageBox.Show( msg, "Revit API Labs", WinForms.MessageBoxButtons.OK, WinForms.MessageBoxIcon.Error );
    }

    /// <summary>
    /// MessageBox wrapper for question message.
    /// </summary>
    public static bool QuestionMsg( string msg )
    {
      return WinForms.DialogResult.Yes
        == WinForms.MessageBox.Show( msg, "Revit API Labs", WinForms.MessageBoxButtons.YesNo, WinForms.MessageBoxIcon.Question );
    }

    /// <summary>
    /// MessageBox wrapper for question and cancel message.
    /// </summary>
    public static WinForms.DialogResult QuestionCancelMsg( string msg )
    {
      return WinForms.MessageBox.Show( msg, "Revit API Labs", WinForms.MessageBoxButtons.YesNoCancel, WinForms.MessageBoxIcon.Question );
    }
  }
}

To list all the currently selected elements, we can use the Selection property on Document object. This property further exposes Elements property which returns an elementset containing the selected elements. This is often used whenever we need to work with element selection set:

  //
  // List the current selection set:
  //
  Selection sel = doc.Selection;
  sMsg = "There are " + sel.Elements.Size + " elements in the selection set:";
  foreach( Element elem in sel.Elements )
  {
    sMsg += "\r\n  " + elem.Category.Name + " Id=" + elem.Id.Value.ToString();
  }
  LabUtils.InfoMsg( sMsg );
  ' List the current selection set
  Dim sel As Selection = doc.Selection

  sMsg = "There are " & sel.Elements.Size & " elements in the selection set:"
  Dim elem As Revit.Element
  For Each elem In sel.Elements
      sMsg += vbCrLf & "  " & elem.Category.Name & " Id=" & elem.Id.Value.ToString
  Next
  MsgBox(sMsg)

The other two arguments are used only when returning a Cancelled or Failed status to Revit, so that a meaningful message can be displayed in the standard user interface dialogue box and the relevant elements can be highlighted. To simulate an error condition, add the following code:

  //
  // Let's pretend that something is wrong with the first element in the selection.
  // We pass a message back to the Revit user and indicate the error result:
  //
  if( !sel.Elements.IsEmpty )
  {
    ElementSetIterator iter = sel.Elements.ForwardIterator();
    iter.MoveNext();
    Element errElem = iter.Current as Element;
    elements.Clear();
    elements.Insert( errElem );
    message = "We pretend something is wrong with this element and pass back this message to user";
    return IExternalCommand.Result.Failed;
  }
  else {
    return IExternalCommand.Result.Succeeded;
  }
  ' Let's pretend that something is wrong with the first element in the selection
  ' We pass a message back to the Revit user and indicate the error result
  If Not sel.Elements.IsEmpty Then
      Dim iter As ElementSetIterator = sel.Elements.ForwardIterator
      iter.MoveNext()
      Dim errElem As Revit.Element = iter.Current
      elements.Clear()
      elements.Insert(errElem)
      message = "We pretend something is wrong with this element and pass back this message to user"
      Return IExternalCommand.Result.Failed
  Else
      Return IExternalCommand.Result.Succeeded
  End If

Build the project and adjust the Revit.ini file to include the new command, i.e. increment the counter and add the details for the second command:

[ExternalCommands]
ECCount=2

ECName1=Lab 1-1 Hello World
ECDescription1=Basic sample that displays a message box from Revit!
ECClassName1=LabsCode.Lab1_1_HelloWorld
ECAssembly1=LabsCode.dll

ECName2=Lab 1-2 Command Arguments
ECDescription2=Lists the app, doc and view data; then the current selection set and finally simulates an error condition on return
ECClassName2=LabsCode.Lab1_2_CommandArguments
ECAssembly2=LabsCode.dll

If you now start Revit, pre-select some elements and then run the new Lab1-2 command, it will display the app, doc and view data, then the currently selected elements, and finally an error reported on the first one of the elements in this set.

next previous home copyright © 2007-2008 jeremy tammik, autodesk inc. all rights reserved.