Guides

C# Scripting Guide

Learn how to write Roslyn-compatible C# script templates to interact dynamically with Autodesk Revit.

Revit script templates are executed on the fly inside the active Autodesk Revit application. They are compiled at runtime using the built-in Roslyn compiler engine.


Script Context & Variables

Every script executed by the Revit AI Suite has access to the following pre-defined context variables:

  • doc (Autodesk.Revit.DB.Document): The active Revit Document object.
  • app (Autodesk.Revit.UI.UIApplication): The active Revit Application host.
  • ch (ScriptChangeTracker): A utility class to track element creations, modifications, and deletions.
  • args (Dictionary<string, object>): A collection of inputs sent from the React UI panel.

Architectural Rules

To keep the application responsive and prevent Revit API errors, scripts should adhere to the following architecture rules:

1. Query vs Command

  • Queries (Read-only): Collect elements, perform calculations, and return serializable JSON lists. Do not open Transactions.
  • Commands (Write): Modify model elements. You must manage your own Transactions:
    using (Transaction tx = new Transaction(doc, "My Transaction Name")) {
        tx.Start();
        // Perform modifications
        tx.Commit();
    }
    

2. Parameter Extraction

Extract primitive values safely from the UI args dictionary:

double height = Convert.ToDouble(args["height"]);
string name = args["name"].ToString();

3. Revit Object Caching (ScriptCache)

Because Revit elements (like Level, Wall, FamilyInstance) cannot be serialized and sent to a web browser, we store them in a persistent cache (ScriptCache) in the Revit process.

  • Storing (in a Query): Cache elements and return their GUID references to the UI:
    var levels = new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>();
    var resultList = new List<object>();
    foreach (var lvl in levels) {
        string cacheId = ScriptCache.Store(lvl);
        resultList.Add(new { id = cacheId, name = lvl.Name });
    }
    return JsonConvert.SerializeObject(resultList);
    
  • Retrieving (in a Command): Retrieve element references in command scripts using the GUID sent by the UI:
    string cacheId = args["levelId"].ToString();
    Level lvl = ScriptCache.Get<Level>(cacheId);
    

Script Templates & Code Examples

Query Script Example: GetLevelsQuery

This script fetches all levels from the active document, stores them in the ScriptCache, and returns their names and cache IDs:

using System;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using Newtonsoft.Json;

var collector = new FilteredElementCollector(doc)
    .OfClass(typeof(Level))
    .WhereElementIsNotElementType();

var list = new List<object>();
foreach (Level lvl in collector)
{
    string cacheId = ScriptCache.Store(lvl);
    list.Add(new { id = cacheId, name = lvl.Name });
}

return JsonConvert.SerializeObject(list);

Command Script Example: BuildWallCommand

This script retrieves a level from the ScriptCache, draws a straight line, opens a transaction, and builds a wall:

using System;
using Autodesk.Revit.DB;

string levelCacheId = args["levelId"].ToString();
double heightFeet = Convert.ToDouble(args["height"]);

Level lvl = ScriptCache.Get<Level>(levelCacheId);
if (lvl == null)
{
    return "Error: Level not found in cache";
}

// Draw a simple 10ft straight wall
XYZ p1 = new XYZ(0, 0, lvl.Elevation);
XYZ p2 = new XYZ(10, 0, lvl.Elevation);
Line line = Line.CreateBound(p1, p2);

Wall wall = null;
using (Transaction tx = new Transaction(doc, "Create Wall via UI"))
{
    tx.Start();
    wall = Wall.Create(doc, line, lvl.Id, false);
    if (wall != null)
    {
        wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(heightFeet);
        ch.DirectCreated.Add(wall.Id);
        ch.AllCreated.Add(wall.Id);
    }
    tx.Commit();
}

return wall != null ? $"Wall Created with ID {wall.Id}" : "Failed to create wall";