Chapter 24. Under the Hood of Revit

If you are an experienced AutoCAD user, it is likely you have had some experience with creating or using customized scripts or routines to increase your productivity based on unique needs. You may have also seen such add-on programs attempt to offer similar object-like behavior as seen in Revit today (remember Auto-Architect by Softdesk?). Unlike the more generic platform of AutoCAD, Revit is a specific application designed for architecture and engineering. As such, Autodesk has slowly and deliberately opened up the application programming interface for customization by others. This chapter will help you understand how to create your own commands and applications to extend the power of Revit to meet your needs.

In this chapter, you'll learn to:

  • Understand the basics of the Revit API

  • Understand the new .addin manifest file method used to load custom command and applications into Revit

  • Set up and build your own custom external applications and commands

Introducing the Revit API

All great applications provide a means to extend their functionality through some sort of API, and Autodesk Revit is no different. The software community's desire to customize and extend software applications to suit their specific needs has been around as long as software itself. The integration of an efficient means to accomplish application customization will usually have some effect on an application's popularity in the marketplace.

One of the most important reasons that a software company would provide a powerful application programming interface (API) in their product is to allow customers to purchase their product in a state that may not be exactly what is needed but is easily molded to suit their needs, resulting in a larger market share than if a useful API was not provided. The Revit API supports in-process dynamic link libraries (DLLs) only and runs on Microsoft's .NET Framework (www.microsoft.com/net). Only single-threaded access is supported, but future releases of the API will likely support multithreading. Since the API runs on the .NET Framework, it supports all native .NET programming languages. Although most of the examples found online and in the Revit 2011 SDK are written in the C# language, Visual Basic .NET (VB.NET) is equally capable of providing the same functionality. The choice between .NET programming languages is purely up to the programmer.

There are two types of DLLs you can develop with the Revit API: external commands and external applications. The differences between the two types are distinct in both lifetime as well as scope.

External Commands

External commands are accessible from the Add-ins tab under the External Tools drop-down button. External commands have access to the current document as well as any elements selected when the command is executed. Commands can be registered into Revit through the Revit.ini file or by using an .addin manifest file new to the Revit 2011 API. This new feature will be explained in greater detail later in this chapter in the section "External Utility Registration Options."

The memory lifetime for external commands lasts only from the time the command is clicked, to when the command returns success or failure. Any objects created or held in memory by the command are destroyed when the command is completed. Data will not persist in memory alone from one external command to another. If data persistence is needed from one external command to another, you will need to employ a means to store this data either by parameters or by an external database.

The IExternalCommand Interface

The IExternalCommand interface is a required implementation for all Revit API commands and must accompany a public function named Execute to access it at runtime. Empty boilerplate samples of this interface are shown here in both VB.NET (Listing 24.1) and C# (Listing 24.2).

Example 24.1. VB.NET IExternalCommand Example

Imports Autodesk.Revit
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI

<Regeneration(RegenerationOption.Manual)> _
<Transaction(TransactionMode.Automatic)> _
Public Class Command
    Implements IExternalCommand
    Public Function Execute(ByVal commandData As UI.ExternalCommandData, _
                ByRef message As String, _
                ByVal elements As DB.ElementSet) _
                As UI.Result Implements IExternalCommand.Execute
        Try
            ' Command implementation
            Return Autodesk.Revit.UI.Result.Succeeded
Catch ex As Exception
         Return Autodesk.Revit.UI.Result.Failed
      End Try
   End Function
End Class

Example 24.2. C# IExternalCommand Example

using Autodesk.Revit;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

[Regeneration(RegenerationOption.Manual)]
[Transaction(TransactionMode.Automatic)]
public class Command : IExternalCommand
{    public Autodesk.Revit.IExternalCommand.Result
Execute(Autodesk.Revit.ExternalCommandData commandData,
        ref string message, Autodesk.Revit.ElementSet elements)
    {
        // Command implementation
    }
}

Required Execute Function Parameters

It is good practice to always trap the main functionality attempt within a try statement. This provides a stable means of trapping any complications that may arise during the command startup:

Public Function Execute(ByVal commandData As ExternalCommandData, _
                        ByRef message As String, _
                        ByVal elements As DB.ElementSet) _
                        As Result Implements IExternalCommand.Execute
    Try
        ' Functionality extends from here
        Return Result.Succeeded
    Catch ex As Exception
        ' If something goes wrong, exit and return failed
        Return Result.Failed
    End Try
End Function

Three return states exist for the IExternalCommand interface. The only result that will allow changes to be committed to the model is IExternalCommand.Result.Succeeded. All others will roll back any changes made by the command. The three return states are shown here:

Return IExternalCommand.Result.Succeeded
Return IExternalCommand.Result.Failed
Return IExternalCommand.Result.Canceled

CommandData

The CommandData parameter provides access to both the application information and the model database information. Information for the current view is also accessible through this parameter.

Message

The Message parameter serves as a means of reporting error information to the user upon returning a result of Failed. The Message parameter will only be displayed if the command status returns Failed. There is a limit of 1,023 characters for this message; longer strings will be truncated.

Elements

The Elements parameter provides access to objects currently selected while the command is executed.

External Applications

External applications are loaded when Revit starts and remain loaded until Revit shuts down. One of the biggest advantages that applications have over commands is that applications provide access to Revit document events. Applications are typically accessible from the Add-Ins tab of the ribbon, but the 2011 API now also provides a means to add application ribbon items to the Analyze tab in Revit MEP 2011.

The IExternalApplication Interface

The IExternalApplication interface is a required implementation for all Revit API external applications. External applications must accompany OnStartup and OnShutdown functions that fire off when Revit launches (OnStartup) and when it closes (OnShutdown). Empty boilerplate samples of this interface are shown here in both VB.NET (Listing 24.3) and C# (Listing 24.4).

Example 24.3. VB.NET IExternalApplication Example

Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Events
Imports Autodesk.Revit.Attributes

<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class App
     ' Required Interface...
Implements IExternalApplication
       Public Function OnStartup(ByVal a As UIControlledApplication) _
       As Result Implements IExternalApplication.OnStartup
               ' OnStartup implementation
       End Function

       Public Function OnShutdown(ByVal a As UIControlledApplication) _
           As Result Implements IExternalApplication.OnShutdown
               ' OnShutdown implementation
       End Function

End Class

Example 24.4. C# IExternalApplication Example

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.Attributes;

namespace ClassLibrary1
{
     [Regeneration(RegenerationOption.Automatic)]
     public class Application : IExternalApplication
     {
         Public Result OnStartup(ControlledApplication application)
         {
             // OnStartup implementation
     }
     Public Result OnShutdown(ControlledApplication application)
     {
         // OnShutdown implementation
     }
  }
}

External Utility Registration Options

There are now two options for registering external commands and applications into the Autodesk Revit user interface.

.addin Manifest File Method (Preferred)

The Revit API now offers the ability to register API applications into Revit using an .addin manifest file. Manifest files will be read automatically by Revit when they are placed in one of two locations on a user's system:

Windows XP

  • C:Documents and SettingsAll UsersApplication DataAutodeskRevitAddins2011

  • C:Documents and Settings<user>Application DataAutodeskRevitAddins2011

Windows 7

  • C:ProgramDataAutodeskRevitAddins2011

  • C:Users<user>AppDataRoamingAutodeskRevitAddins2011

All files named .addin in these locations will be read and processed by Revit during startup. It is possible to load multiple elements within a single manifest by nesting multiple AddIn elements into one .addin file. An example .addin file adding our example ExternalCommand looks like this:

<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<RevitAddIns>
 <AddIn Type="Command">
  <Assembly>C:Revit ProjectsExampleCommand.dll</Assembly>
  <AddInId>72d6cd76-5462-4c97-b56c-5468b08ba742</AddInId>
  <FullClassName>ExampleCommand.Command</FullClassName>
  <Text>Batch Family Export Utility</Text>
  <VisibilityMode>NotVisibleInFamily</VisibilityMode>
  <LongDescription>Batch export all family files as external RFA files. i
  Each file will be saved into a subdirectory named by its category i
  beneath the parent directory selected by the user.</LongDescription>
  <TooltipImage> C:Revit ProjectsPreview_BatchFamilyExport.PNG</TooltipImage>
 </AddIn>
</RevitAddIns>

The example ExternalApplication.addin file would look like this:

<?xml version="1.0" encoding="utf-8" standalone="no" ?>
<RevitAddIns>
 <AddIn Type="Application">
  <Name>ExampleApplication</Name>
  <Assembly>c:Revit Projects ExampleApplication.dll</Assembly>
  <AddInId>fb36c4b0-d4c2-4ead-96db-81f48407e57a</AddInId>
  <FullClassName>ExampleApplication.Application</FullClassName>
 </AddIn>
</RevitAddIns>

The .addin XML schema utilizes the XML tags described in Table 24.1.

Table 24.1. .addin XML Schema

Tag

Description

Assembly

The full path to the add-in assembly file. Required for all ExternalCommand and ExternalApplication objects.

FullClassName

The full name of the class that implements IExternalCommand or IExternalApplication. Required for all ExternalCommand and ExternalApplication objects.

AddInId

A GUID that represents the ID of this particular application. AddInId must be unique for a given session of Revit. Autodesk recommends you generate a unique GUID for each registered application or command. Required for all ExternalCommand and ExternalApplication objects. The property UIApplication.ActiveAddInId provides programmatic access to this value, if required.

Name

The name of application. Required; for ExternalApplications only.

Text

The name of the button. Optional; use this tag for ExternalCommand objects only. The default is External Tool.

Description

Short description of the command, will be used as the button tooltip. Optional; use this tag for ExternalCommand objects only. The default is a tooltip with just the command text.

VisibilityMode

Provides the ability to specify if the command is visible in project documents, family documents, or no document at all. Also provides the ability to specify the discipline(s) where the command should be visible. Multiple values may be set for this option. Optional; use this tag for ExternalCommand objects only. The default is to display the command in all modes and disciplines, including when there is no active document. Previously written external commands that need to run against the active document should either be modified to ensure that the code deals with invocation of the command when there is no active document, or apply the NotVisibleWhenNoActiveDocument mode.

AvailabilityClassName

The full name of the class in the assembly file that implemented IExternalCommandAvailability. This class allows the command button to be selectively grayed out depending on context. Optional; use this tag for ExternalCommand objects only. The default is a command that is available whenever it is visible.

LargeImage

The path to the icon to use for the button in the External Tools drop-down menu. The icon should be 32 x 32 pixels for best results. Optional; use this tag for ExternalCommand objects only. The default is to show a button without an icon.

LongDescription

Long description of the command; will be used as part of the button's extended tooltip. This tooltip is shown when the mouse hovers over the command for a long amount of time. You can split the text of this option into multiple paragraphs by placing <p> tags around each paragraph. Optional; use this tag for ExternalCommand objects only. If neither this property nor TooltipImage is supplied, the button will not have an extended tooltip.

TooltipImage

The path to an image file to show as a part of the button extended tooltip, shown when the mouse hovers over the command for a longer amount of time. Optional; use this tag for ExternalCommand objects only. If neither this property nor TooltipImage is supplied, the button will not have an extended tooltip.

LanguageType

Localization setting for Text, Description, LargeImage, LongDescription, and TooltipImage of external tools buttons. Revit will load the resource values from the specified language resource DLL. The value can be one of the 11 languages supported by Revit. If no LanguageType is specified, the language resource that the current session of Revit is using will be automatically loaded.

Revit.ini Method

In Revit 2011, the registration method utilizing the Revit.ini file is still supported; however, it will not be supported in future releases of the API. This is because it does not offer any of the new capabilities of the .addin manifest method. Be aware that updaters may not be registered from external applications registered using the Revit.ini method because Dynamic Model Update registration requires a valid AddInId.

Create a backup copy of your Revit.ini found in the C:Program FilesAutodeskRevit Architecture 2011Program directory. Only after you have backed up your Revit.ini file, launch Notepad and open Revit.ini for editing. Press Ctrl+F, and search within Notepad for the text string [ExternalCommands]. If your Revit.ini does not contain this entry, we will need to add it to the end of the file.

The [ExternalCommands] portion of the Revit.ini requires a line directly beneath it entered as ECCount=#, where # would be replaced by the total number of external commands being loaded into the Revit.ini file. If you have two external commands, the entry would read ECCount=2.

[ExternalCommands]
ECCount=1

Each externally defined command requires four lines to describe the name, the class, the assembly filename, and a description. Only the description line is optional.

In the following example external command entry, the number preceding the = represents the command's loading order into Revit. Commands must be numbered in sequence without any skipped numbers:

ECName1=Batch Family Export
ECClassName1=BatchFamilyExporter.Command
ECAssembly1=C:BatchFamilyExtractorinDebugBatchFamilyExporter.dll
ECDescription1=Batch Family Export Utility...

In the previous code, the entry for ECName will display in the Add-Ins tab of the ribbon beneath External Commands for your command.

ECClassName requires the namespace of the project to be loaded followed by the class name containing the IExternalCommand reference. The required format is NameSpace.Classname. In this example, our namespace is BatchFamilyExporter, and our class containing the IExternalCommand reference is Command.

ECAssembly requires the full path and filename of the DLL file to be loaded containing the external command.

ECDescription is optional and will display in the status bar of Revit.

Selecting a Development Environment

Several development environment options are available for building and debugging .NET Revit API utilities. Although Visual Studio 2008 Professional is preferred, it is not free. Microsoft provides free express versions of Visual Studio 2008 in both VB.NET and C# on its website at www.microsoft.com/express. These express versions provide more than enough functionality to generate a fully featured .NET Revit API utility.

A new .NET programming language known as F# is quickly emerging as the next generation of .NET technology. F# has been so successful that Microsoft has promoted it to a "first-class" language in Visual Studio 2010. See www.fsharp.net for more information.

Visual Studio Debug Settings

Debugging a Revit command or application in Visual Studio 2008 requires that you first enter the full path and filename to your Revit.exe in the Start External Program field in the Debug tab of the Project Properties dialog box, as shown in Figure 24.1. The Revit API does not yet provide a means to interact with the Revit application in an externally defined stand-alone executable file. Entering the full path and filename to a Revit file in the Command Line Arguments field will launch that file when the project enters debug mode.

Setting debug options in Visual Studio

Figure 24.1. Setting debug options in Visual Studio

Revit 2011 Software Development Kit (SDK)

The Revit 2011 SDK is available on the Revit 2011 installation disk and in the downloadable installation package; however, you can download the latest version of the SDK from Autodesk's website at www.autodesk.com/revit. The SDK contains several working samples of code written in both the C# language as well as VB.NET. Examples range from simple element modifications to family creation. The "Getting Started with the Revit API" document included with the SDK will help you with the examples provided.

The Revit 2011 API requires the .NET Framework Version 3.5 as a prerequisite, but since this same framework version is required to install and run Revit in general, it is not necessary to build your applications with this as a runtime prerequisite. If you choose to build your application using a .NET Framework version newer than 3.5, you should build your application to verify the installation of your required framework version at runtime.

What's New in the Revit 2011 API

If you are planning on updating any existing Revit API utilities to run in Autodesk Revit 2011, be prepared to make some rather major changes to your code. If you have any familiarity with previous versions of the Revit API, you will immediately notice some differences as you explore the new version.

The changes introduced in this version are going to make Autodesk Revit much more powerful in the long run because it will become possible to accomplish things that you just couldn't do in previous versions of the API. Not only are there new features, but other features have been redesigned to improve performance, such as the way elements are accessed and iterated within loops.

DLL Split

There was just one lonely DLL in previous releases of the API representing the entire thing. There are now two DLLs, each with its own scope, in the Autodesk Revit API:

RevitAPI.dll

This DLL now contains only methods used to access the Autodesk Revit application, documents, elements, and parameters at the database level.

RevitAPIUI.dll

This is a new DLL and contains all API interfaces related to manipulation and customization of the Revit user interface. This DLL provides access to the Revit command and application interfaces required by all external command and applications to run inside Revit.

  • IExternalCommand and External Command–related interfaces

  • IExternalApplication and related interfaces

  • Selection

  • RibbonPanel, RibbonItem, and all Ribbon subclasses

  • TaskDialogs (new)

Access to the document- and application-level interfaces through both the RevitAPIUI.dll and the RevitAPI.dll is now provided through a split of the Document and Application classes.

Autodesk.Revit.UI.UIApplication

This class provides access to the UI-level interfaces for the Revit application, including the ability to add RibbonPanels and the ability to obtain the active document in the Revit user interface.

Autodesk.Revit.UI.UIDocument

This provides access to UI-level interfaces for the document, such as the contents of the selection and the ability to prompt the user to make selections and pick points.

Autodesk.Revit.ApplicationServices.Application

This provides access to all other application-level properties.

Autodesk.Revit.DB.Document

This new DB.Document class provides access to the document-level properties. The ExternalCommandData interface provides access to the UIApplication object as well as the active document view. From the active UIApplication object, you can obtain the active UI document as either a family or a project model. You can also construct the UIApplication from the Application object, and the UIDocument from the database-level document at any time.

New Transaction Interfaces

Transactions are a means to prevent corruption by only allowing changes to be made when all required methods and conditions succeed. "All or nothing" is a good way to describe a transaction. Transactions roll back any changes contained within the transaction when a requirement or method does not return successfully. Transactions are common in complex database programs and are critical for ensuring that a complex operation succeeds as intended.

The 2011 API now provides a much more comprehensive system for handling transactions for changes being made to the model. There are three main transaction classes included in the Revit 2011 API. The most notable is the ability to manage subtransactions. Each of the three transaction types listed here share these four common methods:

Start

Starts the context

Commit

Commits all changes to the document

RollBack

Discards all changes to the document

GetStatus

Returns the current status of a transaction object

The Commit and RollBack methods also return a status indicating whether or not the method was successful. Available status values for Commit and RollBack include the following:

  • Uninitialized

  • Started

  • Committed

  • RolledBack

  • Pending

In the methods mentioned here for Commit and RollBack, the Pending method is set after an attempt to either submit or roll back a transaction object but because of failures, that process could not be completed yet and is waiting for the end user's response (in a modeless dialog). Once the failure processing is finished, the status will be automatically updated (to either Committed or RolledBack status).

In the Revit 2011 API, events no longer automatically open transactions. Because of this change, the document won't be modified during an event unless one of the event's handlers modifies it by making changes within a transaction. If your application requires changes to anything in the document, these transactions are no longer optional.

Transaction

All transactions must be named, and only one can be active at any given time. Transactions cannot be nested within other transactions. Each transaction will be represented as one "undo" in Revit upon successful completion. A series of three successful transactions would be represented as three individual undos in Revit, and so on.

Subtransaction

Subtransactions are optional and can be used to enclose a set of Revit model modification commands. Subtransactions are a convenience feature that allows a developer to break up more complex tasks into logical, smaller, and more manageable tasks.

Subtransactions can only be created within an already opened transaction and must be closed (either committed or rolled back) before the transaction is closed (committed or rolled back).

Unlike transactions, a subtransaction may be nested, but any nested subtransaction must be closed before the enclosing subtransaction is closed. Subtransactions do not have names, nor do they appear as actions in the Undo command in Revit.

Transaction Group

Transaction groups are optional and allow several independent transactions to be treated as a single transaction all at once. A transaction group can only be started when no transactions have been opened yet, and can be closed only after all enclosed transactions are closed (rolled back or committed). Transaction groups can be nested, but any nested group must be closed before the enclosing group is closed.

When a transaction group is to be closed, it can be rolled back, which means that all already submitted transactions will be rolled back at once. If not rolled back, a group can be either submitted or assimilated. In the former case, all submitted transactions (within the group) will be left as they were. In the later case, transactions within the group will be merged together into one single transaction that will bear the group's name.

Regeneration Options

To help improve the speed of a custom application or command as it interacts with the elements in a project, new regeneration options are available within the Revit API. In previous versions, all actions on model elements would force the model to update or regenerate automatically. Within the 2011 API, you can now specify automatic or manual regeneration. There is no default for this option and you must apply it to legacy application classes to allow your application to function in Revit 2011.

The new Autodesk.Revit.Attributes.RegenerationAttribute custom attribute should be applied to your implementation class of the IExternalCommand interface and IExternalApplication interface to control the regeneration behavior for the external command and external application. There are two supported values:

RegenerationOption.Automatic

This option is the equivalent behavior with Revit 2010 and earlier versions of the Revit API. This option will regenerate after every model-level change. The performance of multiple modifications within the same file will be slower than RegenerationOption.Manual. This mode is provided for behavioral equivalence with Revit 2010 and earlier; it is obsolete and will be removed in a future release.

RegenerationOption.Manual

This is the preferred option and will not regenerate after every model-level change. Instead, you may use the regeneration APIs to force an update of the document after a group of changes. Because this mode suspends all updates to the document, your application should not read data from the document after it has been modified until the document has been regenerated, or it runs the risk of accessing outdated information. This will eventually be the only regeneration option mode in future releases of the Revit API.

In the empty boilerplate samples shown here, you will see the definition of the manual option in VB.NET (Listing 24.5) and the automatic option in C# (Listing 24.6).

Example 24.5. VB.NET External Application Example

Imports Autodesk.Revit
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.ApplicationServices

<Regeneration(RegenerationOption.Manual)> _
Public Class Application
    Implements IExternalApplication
Public Function OnStartup(ByVal a As UIControlledApplication) _
          As Result Implements IExternalApplication.OnStartup
      Try
          ' Application starts here
          Return Result.Succeeded
      Catch ex As Exception
          Return Result.Failed
      End Try
  End Function

  Public Function OnShutdown(ByVal a As UIControlledApplication) _
      As Result Implements IExternalApplication.OnShutdown
      Return Result.Succeeded
  End Function

End Class

Example 24.6. C# External Application Example

using Autodesk.Revit;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;

[Regeneration(RegenerationOption.Automatic)]
public class Application : IExternalApplication
{
     public Autodesk.Revit.UI.Result OnStartup(ControlledApplication application)
     {
       // OnStartup implementation
     }
     public Autodesk.Revit.UI.Result OnShutdown(ControlledApplication application)
    {
       // OnShutdown implementation
    }
}

Namespace Changes

One of the more obvious changes made to the API in this release is the renaming of namespaces. These new names help improve consistency and make the API more suitable for future expansion. Most notable are the changes to the MEP namespaces to Mechanical, Electrical, and Plumbing. Figure 24.2 shows a small excerpt from the Revit 2011 API Namespace Remapping.xlsx file included with the Revit 2011 SDK available for download from the Autodesk website.

This figure shows from left to right the class name, the previous 2010 namespace, the new 2011 namespace, and finally any notes explaining the change in the last column of the document. This spreadsheet will help get you on your way to understanding the namespace changes made to the Revit 2011 API.

Namespace remapping spreadsheet available with the Revit SDK

Figure 24.2. Namespace remapping spreadsheet available with the Revit SDK

Additional Ribbon Customization Options

Perhaps one of the most powerful improvements made in the Revit 2011 API has to do with the ribbon control additions. The ability to dock a custom text box into the Quick Access toolbar (QAT) for uses ranging from command entry with full argument syntax support to family search is now right at your fingertips. This opens doors for generating simpler customizations without having to create a clunky dialog interface.

New Ribbon Controls

The ability to create ribbon items with all the same features as the default ribbon items is a major improvement in this version of the API. You can now add long descriptions that display when you hover over a custom ribbon control as well as tooltip images. The list of ribbon improvements is quite extensive. For example, new controls that can be added to the ribbon in Revit 2011 include the following:

SplitButton

Creates a pull-down button with a default push button attached.

RadioGroup

Creates a set of ToggleButtons, where only one of the set can be active at a time.

ComboBox

Creates a pull-down containing a set of selectable items, which can be optionally grouped.

TextBox

Creates an input field for users to enter text; an event has also been added to pass the contents when Enter is pressed.

SlideOut Panel

Similar to a flyout panel that slides down from a ribbon panel when clicked. These panels can contain any of the standard ribbon components.

Custom Panels to the Analyze Tab

Custom panels can also be added to the Analyze tab in Revit MEP 2011 as well as the Add-Ins tab via a new overload of Application.CreateRibbonPanel().

New Ribbon Control Methods

As a result of the ribbon control enhancements, some preexisting ribbon methods have been modified, as shown in Table 24.2.

Table 24.2. Changes in Ribbon Control Methods

Method/Property

Description

RibbonPanel.AddButton()

Replaced with RibbonPanel.AddItem()

RibbonPanel.AddStackedButtons()

Replaced with RibbonPanel.AddStackedItems() overloads

Property RibbonPanel.Items

Replaced with method RibbbonPanel.GetItems()

Property PulldownButton.Items

Replaced with method PulldownButton.GetItems()

Method RibbonPanel.AddPushButton()

Removed; use RibbonPanel.AddItem()

Method RibbonPanel.AddPulldownButton()

Removed; use RibbonPanel.AddItem()

Method RibbonPanel.AddToPulldown()

Removed; use PulldownButton.AddItem()

Method PulldownButton.AddPushButton()

Removed; use PulldownButton.AddItem()

Building a Batch Family Extractor

Now that you have a basic understanding of the Revit API, let's build a custom command and a custom application to call it. The code samples for the external command and external application are written in VB.NET using Visual Studio 2008.

The sample command will iterate through all family symbols in the model and export them to individual RFA files to a user-selected directory location. Each family file will be saved into a subdirectory named by its Revit family category name. The sample application will generate a custom ribbon button you will use to launch the custom external command. Harvesting content from existing projects will never be easier.

Creating the Visual Studio Projects

All external commands and applications are now required to have unique globally unique identifiers (GUIDs) in order to load into the Revit user interface. This means that each command and application is better managed as individual Visual Studio projects.

Visual Studio allows multiple stand-alone projects to be loaded into a master project, allowing you to easily manage multiple projects written in any supported .NET language in one convenient master environment.

This example demonstrates how to configure the ExternalCommand as one Visual Studio project and the ExternalApplication as another. You will then create a third master project into which you will load the two subprojects.

  1. Create a new Visual Basic Class Library project in Visual Studio 2008. Make sure that the .NET Framework is set to 3.5. Name it ExampleCommand (Figure 24.3) and click OK.

  2. Save the ExampleCommand project to disk and create another project using the same settings, but this time name it ExampleApplication and save it alongside the ExampleCommand project.

  3. Close both of the command and application projects and create a third project. This time select the Empty Project template and name the solution ExampleProject, as shown in Figure 24.4.

    Create a new class library in Visual Studio.

    Figure 24.3. Create a new class library in Visual Studio.

    Create a new example project in Visual Studio.

    Figure 24.4. Create a new example project in Visual Studio.

  4. Add the ExampleCommand and ExampleApplication projects to this new ExampleProject project by selecting File

    Create a new example project in Visual Studio.

Adding Namespace References

All Autodesk Revit 2011 IExternalCommands and IExternalApplications now require two namespace references: RevitAPI.dll and RevitAPIUI.dll.

Right-click the title of the Visual Studio 2008 project name in the Solution Explorer and select Add

Adding Namespace References
Adding API references to your Visual Studio project

Figure 24.5. Adding API references to your Visual Studio project

The ExternalApplication project requires two additional references to Presentation Core and Windows Base for the ribbon image functionality. Both of these references are available from the .NET tab of the Add References dialog box.

Class and Form Configurations

With the namespace references out of the way, the next step in our project configuration is to name the default IExternalApplication and IExternalCommand interface classes to something logical. The names of these classes can be anything you want but we recommend that a consistent naming strategy be used to make it easier to manage. We typically use the name of Command for commands and App for applications.

Command Class

Select Class1.vb in the ExternalCommand project, and change its name to Command.vb in the Visual Studio properties window. This class will serve as the main external command class containing the IExternalCommand interface necessary to gain access to the Revit user interface.

Import Namespaces

The command class requires three namespace imports. The Revit and Revit.UI namespaces are used to get at the main program and user interface functionalities. The Revit.Attributes namespace is used to access the regeneration and transaction features that are now required by the Revit 2011 API.

Imports Autodesk.Revit
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.Attributes

Transaction and Regeneration Mode Attributes

The Transaction and Regeneration attributes are now required for all Revit 2011 API commands and applications. These two attributes must be called immediately before the class declaration and after any namespace imports.

The transaction serves as a container of changes that can be rolled back if a failure occurs. The regeneration options can now be set to manual, preventing Revit from regenerating on each and every modification made by your utility. This basically opens the door for speed and efficiency in your functions.

<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class Command .......

Listing 24.7 shows the completed Command class code.

Example 24.7. The Completed Command Class

Imports Autodesk.Revit
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.Attributes

''' <summary>
''' Demonstrates how an ExternalCommand is added to the Revit user interface.
''' </summary>
<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class Command
    Implements IExternalCommand ' All ExternalCommands i
    must implement IExternalCommand
    ' Handy way to post build version info to a form

    Public Const appDate As String = "(YYYY-MM-DD) - "
    Public Const appVer As String = "V#.#"

    Public Function Execute(ByVal commandData As ExternalCommandData, _
                             ByRef message As String, _
                             ByVal elements As DB.ElementSet) _
                             As Result Implements IExternalCommand.Execute
        Try
            Dim rvtApp As UI.UIApplication = commandData.Application
            Dim rvtDoc As DB.Document = rvtApp.ActiveUIDocument.Document
            Dim oForm As New formMain
            Dim sAppVersion As String = rvtDoc.Application.VersionNumber
            ' Pass the Revit document along with any other variables to the form
            oForm.Initialise(rvtApp, rvtDoc, appDate, appVer & i
            " for RAC" & sAppVersion)
            oForm.ShowDialog()
            ' ExecuteCommands must return either Succeeded, Failed, or Cancelled
            Return Result.Succeeded
        Catch ex As Exception
            ' If something goes wrong, exit and return failed
            message = "Command load failed"
            Return Result.Failed
        End Try
    End Function
End Class

Application (App) Class

Select Class1.vb in the ExternalApplication project and change its name to App.vb in the Visual Studio properties window. This class will serve as the main external application class containing the IExternalApplication interface for adding our ribbon panel to the Revit user interface.

The sample application class will add a pushbutton to the Add-Ins ribbon as well as a sample text box demonstrating how to capture the EnterPressed event for this control. The text box control introduction to the Revit 2011 API is in our opinion the most useful addition to the UI namespace of the Revit 2011 API.

Import Namespaces

The sample external application needs six namespace imports to achieve the functionality needed as shown here. Revit.UI.Events is required for the textbox EnterPressed event example. Each of the System imports are required to add images to our ribbon controls.

Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Events
Imports Autodesk.Revit.Attributes

Imports System.Windows.Forms
Imports System.Windows.Media.Imaging
Imports System.Reflection

Immediately following the namespace imports, we set the transaction mode to automatic. In an effort to make the program run faster, we then set the regeneration option to manual, as shown here, in an effort to make the program run faster.

<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class App ......

Listing 24.8 shows the completed App class code.

Example 24.8. The Completed App Class

Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Events
Imports Autodesk.Revit.Attributes
Imports System.Windows.Forms
Imports System.Windows.Media.Imaging
Imports System.Reflection
''' <summary>
''' Demonstrates how an ExternalApplication is added to the Revit user interface.
''' </summary>
<Transaction(TransactionMode.Automatic)> _
<Regeneration(RegenerationOption.Manual)> _
Public Class App
    ' Required Interface...
    Implements IExternalApplication
    ' Fired off by OnStartup
    Public Sub AddRibbonPanel(ByVal a As UIControlledApplication)
        ' Share the execution path with supplemental command paths
        Dim myPath As String = Assembly.GetExecutingAssembly.Location.Substring( _
            0, (Assembly.GetExecutingAssembly.Location.LastIndexOf("") + 1))
        Dim rvtPanel As RibbonPanel = a.CreateRibbonPanel("Samples")
        ' Use the 'New' method to create pushbuttons
        Dim rvtData As New PushButtonData("ExampleCommand", _
            "Batch Family Export Utility", _
            myPath & "ExampleCommand.dll", _
            "ExampleCommand.Command")
        rvtData.ToolTip = "Example Batch Family Export Command"
        ' Add the pushbutton to the ribbon panel
        Dim myButton As PushButton = rvtPanel.AddItem(rvtData)
        ' Add a textbox that will return its entered value when enter is pressed
        Dim txtData As New TextBoxData("MyTextBox")
        ' Add the textbox to the ribbon panel
        Dim txtBox As Autodesk.Revit.UI.TextBox = rvtPanel.AddItem(txtData)
        txtBox.Value = "Textbox Event Example"
        txtBox.Image = New BitmapImage(New Uri(myPath & "enter.png"))
        txtBox.ToolTip = "A sample message dialog displaying the entered data..."
        txtBox.ShowImageAsButton = True
        txtBox.PromptText = "Enter Text and Press Enter"
        ' This passes the event data to our event handler function
        AddHandler txtBox.EnterPressed, _
            New EventHandler(Of TextBoxEnterPressedEventArgs)i
           (AddressOf Me.TextBoxPopup)
    End Sub

    Public Sub TextBoxPopup(ByVal sender As Object, _
                            ByVal args As TextBoxEnterPressedEventArgs)
        ' This reacts with the string entered into the textbox...
        MessageBox.Show(sender.value, _
                        "Textbox Value!", _
                        MessageBoxButtons.OK, _
                        MessageBoxIcon.Exclamation)
    End Sub

    Public Function OnStartup(ByVal a As UIControlledApplication) _
    As Result Implements IExternalApplication.OnStartup
        Try
            ' Add the ribbon part to the Add-Ins tab
            AddRibbonPanel(a)
Return Result.Succeeded
     Catch ex As Exception
         Return Result.Failed
        End Try
    End Function

    Public Function OnShutdown(ByVal a As UIControlledApplication) _
        As Result Implements IExternalApplication.OnShutdown
        Return Result.Succeeded
    End Function

End Class

The User Form

The sample command will need a user form as a means to interact with the user. Right-click ExternalCommand in the Solution Explorer and select Add

The User Form

Adding the Controls

The next step is the design of the user form along with all required controls necessary to provide the functionality we need. Save the project and open the design view of the formMain class. Figure 24.6 illustrates what the completed form design should look like.

Example of a completed form in Visual Studio

Figure 24.6. Example of a completed form in Visual Studio

You can be creative as to your placement of these controls so long as they are of the correct control types and match the names listed here. The controls are listed in order from top to bottom, going left to right, as illustrated in the previous example.

FolderBrowserDialog Control: FolderBrowserDialog1

Add a FolderBrowserDialog from the toolbox to the form named FolderBrowserDialog1. This will serve as a helper dialog box to navigate and select an alternate directory for the RFA file family exports. This control only displays in the form design tray and will not necessarily display on the form itself.

Progressbar Control: ProgressBar1

Provides progress information to user. Place this control along the top of the form as illustrated earlier.

Label Control: LabelExport

The example form design shows the LabelExport control placed underneath the left corner of the progress bar along the top of the form. This label displays as the main prompt to the user on load.

Button Control: ButtonBrowse

Place this control beneath the progress bar and set its text to .... This button will be used to launch the folder browse dialog box. Double-click the ButtonBrowse control and enter the following code:

Private Sub ButtonBrowse_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs) _
                               Handles ButtonBrowse.Click
    ' Browse to select the parent export path
    Me.FolderBrowserDialog1.ShowDialog()
    If Me.FolderBrowserDialog1.SelectedPath.ToString() <> "" Then
        Me.LabelExportPath.Text = 
Example of a completed form in Visual Studio
Me.FolderBrowserDialog1.SelectedPath.ToString() End If End Sub
Label Control: LabelExportPath

Place a label control named LabelExportPath to the right of the ButtonBrowse control. This label will display the path to the selected export directory.

Label Control: LabelFileName

Place a label control beneath the ButtonBrowse button named LabelExport. This label will display the current family file as it is being processed by the main export functions.

Button Control: ButtonExport

The bottom-left button, named ButtonExport, is used to launch the export command and commit the start of the export process. Set the text for this button to Export all Families. Double-click this button control to create the basic code structure for the click event. The code behind this button is shown in "The Main Export Function" section later in this chapter.

Button Control: ButtonCancel

The bottom-right button, named ButtonCancel, is used to close the command without performing any actions at all. Set the text for this control to Cancel. Double-click the Cancel button and enter the following code:

Private Sub ButtonCancel_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs) _
                               Handles ButtonCancel.Click
    Me.Close()
End Sub

Adding the Private Form Class Variables

The following lines are used to make the Revit application, current Revit project model, and default export path available to all functions within the form class. These declarations will go just beneath the form class declaration:

Private rvtDir As String = ""
Private rvtApp As UI.UIApplication = Nothing
Private rvtDoc As DB.Document

Adding the Supplemental Form Functions

The command example is quite simple in function but still requires a few supplemental helper functions:

Initialization

It is common to utilize an initialization subroutine within a form as a means of introducing any required parameters to the form as it is being launched. This provides an easy way to pass object variables to the form that can then be used within the form class.

Among the objects that we are interested in passing over to the user form are the Revit application object, current Revit project model, a string representing the build date, and another string representing an arbitrary build version, such as V1.0.

This function will also set our default export path to the directory of our model using the central model file path if the project is workshared.

Public Sub Initialise(ByVal app As UI.UIApplication, _
                      ByVal document As DB.Document, _
                      ByVal myAppDate As String, _
                      ByVal strAppVer As String)
    ' Hide the progressbar until we need it
    Me.ProgressBar1.Visible = False
    ' Form title with version
    Me.Text = "Batch Family Exporter - " & myAppDate & strAppVer
    ' Expose Revit doc and app
    rvtDoc = document
    rvtApp = app
    ' Set default export path adjacent to model location
    ' If workshared, use the central model path
    If rvtDoc.IsWorkshared = True Then
        Try
            rvtDir = Path.GetDirectoryName i
           (rvtDoc.WorksharingCentralFilename) _
           & "/Exported Families/"
       Catch ex As Exception
           ' Detached model will not have a file path
       End Try
    Else
       rvtDir = Path.GetDirectoryName(rvtDoc.PathName) & _
       "/Exported Families/"
End If
    ' The proper formatted file path...
    Me.LabelExportPath.Text = Replace(rvtDir, "/", "")
End Sub
CheckValidFileName

While Revit provides its own means of preventing invalid file-naming characters from making their way into family names, you can use this function as a precautionary means. This filename validity snippet is useful anytime you are exporting files and need to verify that the characters used in the filename are valid.

Public Shared Function CheckValidFileName
Adding the Supplemental Form Functions
(ByVal fileName As String) As String CheckValidFileName = fileName For Each c In Path.GetInvalidFileNameChars() If fileName.Contains(c) Then ' Invalid filename characters detected... ' Could either replace characters or return empty CheckValidFileName = "" End If Next Return CheckValidFileName End Function

The Main Export Function

The main export function in the sample project is all nested inside the ButtonExport_Click subroutine. There will be a lot going on inside this routine. The meat of the function is where the family symbols are filtered out of the Revit document and then iterated one by one and exported out as external RFA family files into a subdirectory named using the family's Revit category name.

The Element Collection Filter

The Revit 2011 API provides a much more efficient and robust means of filtering elements for selection as well as iterating elements within these collections.

' Filter to get a set of elements that are elementType
Dim filter As New DB.ElementIsElementTypeFilter
Dim collector As New DB.FilteredElementCollector(rvtDoc)
collector.WherePasses(filter)
Element Iteration

Once the elements have been gathered from the model using an element filter, we can iterate through them quite easily. This snippet illustrates the element iteration code:

Dim iter As IEnumerator = collector.GetElementIterator
Dim element As DB.Element
Dim FamInst As DB.Family = Nothing
Dim famSymb As DB.FamilySymbol = Nothing
Dim category As DB.Category
Do While (iter.MoveNext())
   element = iter.Current
If (Type Of element Is DB.FamilySymbol) Then
       category = element.Category
       If Not (category Is Nothing) Then
           Try ' Create the category subdirectory
               exportPath = rvtDir & category.Name & "/"
               Directory.CreateDirectory(Replace _
               (exportPath, "/", "", , , CompareMethod.Text))
          Catch ex As Exception
              ' Category subdirectory exists
          End Try
          Try ' family variable to Element
              famSymb = element
              FamInst = famSymb.Family
              sFamName = FamInst.Name
              ' Verify famname is valid filename and exists
              If Dir$(exportPath + sFamName & ".rfa") = "" And _
                      CheckValidFileName(sFamName) <> "" Then
                  Me.LabelFileName.Text = "..." & _
                      category.Name & "" & _
                      FamInst.Name
                  Dim famDoc As DB.Document = rvtDoc.EditFamily(FamInst)
                  famDoc.SaveAs(exportPath + sFamName & ".rfa")
                  famDoc.Close(False)
              End If
          Catch ex As Exception
              ' Prevent hault on system families
           End Try
       End If
   End If
Loop

Listing 24.9 shows the completed formMain code.

Example 24.9. The Completed formMain Code

Imports Autodesk.Revit
Imports System.IO

Public Class formMain

    Private rvtDir As String = ""
    Private rvtApp As UI.UIApplication = Nothing
    Private rvtDoc As DB.Document

    Public Sub Initialise(ByVal app As UI.UIApplication, _
                          ByVal document As DB.Document, _
                          ByVal myAppDate As String, _
                          ByVal strAppVer As String)
' Hide the progressbar until we need it
        Me.ProgressBar1.Visible = False
        ' Form title with version
        Me.Text = "Batch Family Exporter - " & myAppDate & strAppVer
        ' Expose Revit doc and app
        rvtDoc = document
        rvtApp = app
        ' Set default export path adjacent to model location
        ' If workshared, use the central model path
        If rvtDoc.IsWorkshared = True Then
            Try
                rvtDir = Path.GetDirectoryName
The Completed formMain Code
(rvtDoc.WorksharingCentralFilename) _ & "/Exported Families/" Catch ex As Exception ' Detached model will not have a file path End Try Else rvtDir = Path.GetDirectoryName(rvtDoc.PathName) & _ "/Exported Families/" End If ' The proper formatted file path... Me.LabelExportPath.Text = Replace(rvtDir, "/", "") End Sub Public Shared Function CheckValidFileName(ByVal fileName As String) As String CheckValidFileName = fileName For Each c In Path.GetInvalidFileNameChars() If fileName.Contains(c) Then ' Invalid filename characters detected... ' Could either replace characters or return empty CheckValidFileName = "" End If Next Return CheckValidFileName End Function Private Sub ButtonExport_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles ButtonExport.Click ' Return a qualified export path rvtDir = Replace(rvtDir, "", "/", , , CompareMethod.Text) Dim sFamName As String = "" Dim exportPath As String = "" Try ' If the parent export directory is missing, create it Directory.CreateDirectory _ (Replace(rvtDir, "/", "", , , CompareMethod.Text)) Catch ex As Exception
' Message to show any errors
            MsgBox(Err.Description, MsgBoxStyle.Information, Err.Source)
        End Try
        ' Filter to get a set of elements that are elementType
        Dim filter As New DB.ElementIsElementTypeFilter
        Dim collector As New DB.FilteredElementCollector(rvtDoc)
        collector.WherePasses(filter)
        ' Iterate the elements
        Dim iter As IEnumerator = collector.GetElementIterator
        ' Variables for element handling
        Dim element As DB.Element
        Dim FamInst As DB.Family = Nothing
        Dim famSymb As DB.FamilySymbol = Nothing
        Dim category As DB.Category
        ' Quickly count for progress bar
        Dim iCntFam As Integer = 0
        Dim iCnt As Integer = 0
        Do While (iter.MoveNext())
            element = iter.Current
            If (TypeOf element Is DB.FamilySymbol) Then
                iCntFam += 1
            End If
        Loop
        ' Reset for export process
        iter.Reset()
        ' Start the progressbar
        Me.ProgressBar1.Visible = True
        Me.ProgressBar1.Minimum = 0
        Me.ProgressBar1.Maximum = iCntFam
        Me.ProgressBar1.Value = iCnt
        ' The export process
        Do While (iter.MoveNext())
            element = iter.Current
            If (TypeOf element Is DB.FamilySymbol) Then
                Me.ProgressBar1.Value = Me.ProgressBar1.Value + 1
                category = element.Category
                If Not (category Is Nothing) Then
                    Try ' Create the category subdirectory
                        exportPath = rvtDir & category.Name & "/"
                        Directory.CreateDirectory(Replace _
                         (exportPath, "/", "", , , CompareMethod.Text))
                    Catch ex As Exception
                        ' Category subdirectory exists
                    End Try
                    Try ' family variable to Element
                        famSymb = element
                        FamInst = famSymb.Family
                        sFamName = FamInst.Name
' Verify famname is valid filename and exists
                        If Dir$(exportPath + sFamName & ".rfa") = "" And _
                                CheckValidFileName(sFamName) <> "" Then
                           Me.LabelFileName.Text = "..." & _
                               category.Name & "" & _
                               FamInst.Name
                           Dim famDoc As DB.Document = 
The Completed formMain Code
rvtDoc.EditFamily(FamInst) famDoc.SaveAs(exportPath + sFamName & ".rfa") famDoc.Close(False) End If Catch ex As Exception ' Prevent hault on system families End Try End If End If Loop Me.Close() End Sub Private Sub ButtonCancel_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles ButtonCancel.Click Me.Close() End Sub Private Sub ButtonBrowse_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles ButtonBrowse.Click ' Browse to select the parent export path Me.FolderBrowserDialog1.ShowDialog() If Me.FolderBrowserDialog1.SelectedPath.ToString() <> "" Then Me.LabelExportPath.Text =
The Completed formMain Code
Me.FolderBrowserDialog1.SelectedPath.ToString() End If End Sub End Class

Additional API Resources

If you are interested in learning more about the Revit API, here are some online resources for your reference:

  • Autodesk Developer Center and Developer Network (ADN): www.autodesk.com/adn

  • The Building Coder (a blog by Jeremy Tammik): thebuildingcoder.typepad.com

  • Autodesk Revit API Discussion Group: discussion.autodesk.com/forum.jspa?forumID=160

  • DevTV: Introduction to Revit API Programming: download.autodesk.com/media/adn/DevTV_Introduction_to_Revit_Programming_new

Using Revit Journals

As you work in Revit, all of your actions are recorded in journal files. These files, which can be found in C:Program FilesAutodeskRevit Architecture 2011Journals, are basic text files that can be opened and reviewed in any text editor such as Notepad or Notepad ++ (notepad-plus.sourceforge.net). If you have ever had any kind of complicated support issue, you may have been asked to send your journal files to Autodesk for review. With the journal files, the Autodesk support team can replay the actions captured in an active Revit session. Journal files can also be used to perform some simple automated tasks.

Family Upgrade Method

One simple example of a journal script is the family upgrade utility, which is a simple journal-based script that will launch Revit and open a series of families you specify, thus upgrading them to the latest version. This utility is included with your Revit installation package and can be found after you extract the installation package in the subfolder: UtilitiesCommonContentBatchUtility.

Before running the script, you must first copy the Upgrade_RFA.txt and Upgrade_RFA.bat files into the root directory of the library you want to upgrade and then run the Upgrade_RFA.bat file to generate a list of files to upgrade. This BAT routine will create another text file named famlist_rfa.txt; don't change the name of this file or move it. The Upgrade_RFA.txt file is a Revit journal file. If you open the journal file, you will find the main function listed as a subroutine between the code Sub and End Sub, as shown in Listing 24.10.

Example 24.10. The Main Function in the Family Upgrade Journal Script

Sub upgrade(namepath, file)

Jrn.Command "Menu", "Open an existing project , 57601 , ID_FILE_OPEN"
  Jrn.Data "File Name" _
          , "IDOK", namepath

  Jrn.Command "Internal" , " , ID_REVIT_SAVE_AS_FAMILY"
Jrn.Data "File Name" , "IDOK", namepath

  Jrn.Command "Menu" , "Close the active project , ID_REVIT_FILE_CLOSE"
End Sub

To execute the journal script, simply drag the Upgrade_RFA.txt file onto the Revit desktop icon. Revit will launch and open each RFA file in the list. As you can see in Listing 24.10, the actual journal commands being passed to Revit are quite simple. ID FILE OPEN opens the project, whereas ID REVIT SAVE AS FAMILY saves it in the new version.

Local File Script and AutoHotkey

You can find another example of journal scripting in a local file utility developed in collaboration between David Baldacchino, David Kingham, and James Vandezande. This utility was created in an open source scripting language called AutoHotkey (www.autohotkey.com). This language is easy to learn and can be an excellent learning platform for aspiring programmers. You can download the local file utility from the AUGI Forums at forums.augi.com/showthread.php?t=65897.

The local file utility has several iterations for each of the developer's respective companies, but at its core, it performs basic file functions to create local copies of Revit central files and then automatically opens them with journal-based scripts. Let's take a look at that portion of the utility.

Other parts of the script will identify the central file and locations for a user's local copies. Once a local copy of the central file has been created, journal commands are generated and written to an actual journal file:

Set Jrn = CrsJournalScript
Jrn.Command "Menu" , "Open an existing project , 57601 , ID_REVIT_FILE_OPEN"
Jrn.Data "File Name" _
, "IDOK", "%DESTINATION%\%LOCALFILE%.rvt"
Jrn.Data "WorksetConfig" _
, "Editable", 0
  Jrn.Data "MessageBox" _
         , "IDOK", "This Central File has been copied or moved from 
Local File Script and AutoHotkey
""%DRV%\%DSCPLN%\%Folder%\%C1%"" to ""%DESTINATION%\%LOCALFILE%.rvt""."
Local File Script and AutoHotkey
& vbCrLf & "" & vbCrLf & "If you wish this file to remain a Central File
Local File Script and AutoHotkey
then you must re-save the file as a Central File. To do this select
Local File Script and AutoHotkey
""Save As"" from the ""File"" menu and check the ""Make this a Central
Local File Script and AutoHotkey
File after save"" check box (under the options button) before you save."
Local File Script and AutoHotkey
& vbCrLf & "" & vbCrLf & "If you do not save the file as a Central File
Local File Script and AutoHotkey
then it will be considered a local user copy of the file belonging to
Local File Script and AutoHotkey
user ""%USERNAME%""." Jrn.Command "Menu" , "Workset control , 33460 , ID_SETTINGS_PARTITIONS"

The journal file is then executed by passing it to the Revit program executable, which is similar to actually dropping a journal file on the Revit desktop icon in the family upgrade method.

Notice the syntax of the journal launching code is quite similar to running an application with optional parameters at the Windows command prompt. The following line runs Revit.exe with the journal file (%JournalFile%) in a maximized application window:

Run %AppPath%ProgramRevit.exe "%JournalFile%", Max

The Bottom Line

Understand the basics of the Revit API.

The need for some users and developers to extend the functionality of Revit is supported by the Revit application programming interface (API). Revit's software development kit (SDK) provides sample code and instructions for building add-ons to the application.

Master It

What are the two types of dynamic link libraries you can develop for Revit?

Understand the new .addin manifest file method used to load custom command and applications into Revit.

The Revit API for 2011 now offers the ability to register API applications into Revit using an .addin manifest file.

Master It

How is the .addin manifest method different from previous versions?

Set up and build your own custom external applications and commands.

You can start to create your own custom applications and commands for Revit using either Microsoft Visual Studio or a free tool such as Visual Studio Express or SharpDevelop.

Master It

How do you make the Revit API functions available in your developing environment?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.89.173