Nice3point.Revit.Toolkit
2027.0.0
Prefix Reserved
dotnet add package Nice3point.Revit.Toolkit --version 2027.0.0
NuGet\Install-Package Nice3point.Revit.Toolkit -Version 2027.0.0
<PackageReference Include="Nice3point.Revit.Toolkit" Version="2027.0.0" />
<PackageVersion Include="Nice3point.Revit.Toolkit" Version="2027.0.0" />
<PackageReference Include="Nice3point.Revit.Toolkit" />
paket add Nice3point.Revit.Toolkit --version 2027.0.0
#r "nuget: Nice3point.Revit.Toolkit, 2027.0.0"
#:package Nice3point.Revit.Toolkit@2027.0.0
#addin nuget:?package=Nice3point.Revit.Toolkit&version=2027.0.0
#tool nuget:?package=Nice3point.Revit.Toolkit&version=2027.0.0
Make Revit API more flexible
This library provides a modern interface for working with the Revit API. Package contains interfaces implementation frequently encountered in revit, aiming to provide as much flexibility as possible, so developers are free to choose which components to use.
Installation
You can install the Toolkit as a NuGet package.
The packages are compiled for specific versions of Revit. To support different versions of libraries in one project, use the RevitVersion property:
<PackageReference Include="Nice3point.Revit.Toolkit" Version="$(RevitVersion).*"/>
Package included by default in Revit Templates.
Table of contents
Features
External Commands
The Toolkit provides base classes for Revit external commands with implemented:
- Automatic dependency resolution to avoid
FileNotFoundExceptionexceptions (dependencies are searched in the plugin folder) - Simplified method signatures — override
Execute()instead of implementing full interface
ExternalCommand
Implementation for IExternalCommand. Override Execute() to implement a command:
[Transaction(TransactionMode.Manual)]
public class Command : ExternalCommand
{
public override void Execute()
{
var document = Application.ActiveUIDocument.Document;
}
}
AsyncExternalCommand
Implementation for asynchronous IExternalCommand. Override ExecuteAsync() for async/await support.
The Revit UI remains responsive during async operations through dispatcher message pumping. Ideal for I/O-bound operations such as HTTP requests, file operations, or database queries.
[Transaction(TransactionMode.Manual)]
public class Command : AsyncExternalCommand
{
public override async Task ExecuteAsync()
{
using var httpClient = new HttpClient();
var response = await httpClient.GetStringAsync("https://example.com");
using var transaction = new Transaction(Application.ActiveUIDocument.Document, "Update Parameter");
transaction.Start();
var element = Application.ActiveUIDocument.Document.GetElement(id);
element?.FindParameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)?.Set(response);
transaction.Commit();
}
}
External Applications
The Toolkit provides base classes for Revit external applications with implemented:
- Automatic dependency resolution to avoid
FileNotFoundExceptionexceptions (dependencies are searched in the plugin folder) - Simplified method signatures — override
OnStartup()/OnShutdown()instead of implementing full interface
ExternalApplication
Implementation for IExternalApplication. Override OnStartup() and optionally OnShutdown():
public class Application : ExternalApplication
{
public override void OnStartup()
{
var panel = Application.CreatePanel("Commands", "RevitAddin");
panel.AddPushButton<Command>("Execute");
.SetImage("/RevitAddin;component/Resources/Icons/RibbonIcon16.png");
.SetLargeImage("/RevitAddin;component/Resources/Icons/RibbonIcon32.png");
}
public override void OnShutdown()
{
}
}
AsyncExternalApplication
Implementation for asynchronous IExternalApplication. Override OnStartupAsync() and optionally OnShutdownAsync() for async/await support.
The Revit UI remains responsive during async operations through dispatcher message pumping. Ideal for I/O-bound operations such as HTTP requests, file operations, or database queries during application startup and shutdown.
public class Application : AsyncExternalApplication
{
public override async Task OnStartupAsync()
{
using var httpClient = new HttpClient();
var configuration = await httpClient.GetStringAsync("https://api.example.com/config");
var panel = Application.CreatePanel(configuration.PanelTitle);
panel.AddPushButton<Command>(configuration.ButtonTitle);
}
public override async Task OnShutdownAsync()
{
await SaveSettingsAsync();
}
}
ExternalDBApplication
Implementation for IExternalDBApplication. Same as ExternalApplication but without UI access:
public class Application : ExternalDBApplication
{
public override void OnStartup()
{
}
public override void OnShutdown()
{
}
}
External events
The Toolkit provides ExternalEvent implementations for working with the Revit API from outside the execution context,
which is particularly useful when working with modeless windows.
Events do not need to be created inside the Revit API context before use — the Toolkit handles all the initialization for you, so you can create them anywhere in your code and on any thread.
ExternalEvent
A synchronous external event that queues the handler via the Revit external event mechanism.
When Raise() is called, the delegate is placed into the Revit event queue and will be executed
in the next event-processing cycle, once Revit is ready and no other commands or edit modes are active.
private readonly ExternalEvent _deleteWindowsEvent = new(application =>
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete windows");
transaction.Start();
document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
transaction.Commit();
//2. The delegate body executes when Revit processes the event
});
private void DeleteWindows()
{
_deleteWindowsEvent.Raise();
//1. Raise returns immediately, the delegate is queued
}
ExternalEvent<T>
A generic synchronous external event that accepts an argument of type T.
Works like ExternalEvent, but allows passing data to the handler at the time of raising.
private readonly ExternalEvent<ElementId> _deleteWindowEvent = new((application, elementId) =>
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete window");
transaction.Start();
document.Delete(elementId);
transaction.Commit();
//2. The delegate body executes with the provided argument
});
private void DeleteWindow(ElementId elementId)
{
_deleteWindowEvent.Raise(elementId);
//1. Raise returns immediately, the delegate is queued
}
AsyncExternalEvent
An asynchronous external event that queues the handler and asynchronously awaits its completion.
The RaiseAsync() method returns a Task that completes when Revit has finished executing the delegate.
Exceptions thrown inside the delegate are rethrown in the original synchronization context.
Synchronously blocking the result of RaiseAsync() on the Revit main thread
(e.g. .Wait(), .Result, .GetAwaiter().GetResult()) will cause a deadlock,
because Revit cannot process the external event while its main thread is blocked by the waiting call.
Use await instead, which releases the thread and allows Revit to process the event.
private readonly AsyncExternalEvent _deleteWindowsAsyncEvent = new(application =>
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete windows");
transaction.Start();
document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
transaction.Commit();
//1. The delegate body executes when Revit processes the event
});
private async Task DeleteWindowsAsync()
{
await _deleteWindowsAsyncEvent.RaiseAsync();
//2. Continues after the delegate has completed
}
AsyncExternalEvent<T>
A generic asynchronous external event that accepts an argument of type T and asynchronously awaits completion.
private readonly AsyncExternalEvent<ElementId> _deleteWindowAsyncEvent = new((application, elementId) =>
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete window");
transaction.Start();
document.Delete(elementId);
transaction.Commit();
//1. The delegate body executes with the provided argument
});
private async Task DeleteWindowAsync(ElementId elementId)
{
await _deleteWindowAsyncEvent.RaiseAsync(elementId);
//2. Continues after the delegate has completed
}
AsyncRequestExternalEvent<TResult>
An asynchronous external event that returns a result of type TResult via RaiseAsync().
The handler is queued to Revit and the returned Task<TResult> completes with the result once Revit has finished executing the delegate.
private readonly AsyncRequestExternalEvent<int> _countWindowsAsyncEvent = new(application =>
{
var document = application.ActiveUIDocument.Document;
var elementIds = document.GetInstanceIds(BuiltInCategory.OST_Windows);
//1. The delegate body executes and returns a value
return elementIds.Count;
});
private async Task CountWindowsAsync()
{
var count = await _countWindowsAsyncEvent.RaiseAsync();
//2. Continues after the delegate has completed, with the result
}
AsyncRequestExternalEvent<T, TResult>
A generic asynchronous external event that accepts an argument of type T
and returns a result of type TResult.
private readonly AsyncRequestExternalEvent<ElementId, bool> _deleteWindowRequestEvent = new((application, elementId) =>
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete window");
transaction.Start();
document.Delete(elementId);
transaction.Commit();
//1. The delegate body executes with the provided argument and returns a value
return true;
});
private async Task DeleteWindowAsync(ElementId elementId)
{
var result = await _deleteWindowRequestEvent.RaiseAsync(elementId);
//2. Continues after the delegate has completed, with the result
}
ExternalEventOptions
You can configure the behavior of external events using ExternalEventOptions.
The AllowDirectInvocation option enables the handler to be invoked directly on the calling thread when Revit is in API mode, instead of being queued:
private readonly ExternalEvent _deleteWindowsEvent = new(application =>
{
//Execution logic
}, ExternalEventOptions.AllowDirectInvocation);
This option useful if you want support Modal and Modeless windows from a single codebase without wasting time on queue management and Revit event-processing cycle.
ExternalEvent attribute
The ExternalEventAttribute is an attribute that allows generating external event properties for annotated methods.
Its purpose is to completely eliminate the boilerplate that is needed to define external events wrapping private methods in a class.
How it works
The ExternalEvent attribute can be used to annotate a method in a partial type, like so:
public partial class MyViewModel
{
[ExternalEvent]
private void DeleteWindows(UIApplication application)
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete windows");
transaction.Start();
document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
transaction.Commit();
}
}
And it will generate properties like this:
public partial class MyViewModel
{
public IExternalEvent DeleteWindowsEvent => field ??= new ExternalEvent(DeleteWindows);
public IAsyncExternalEvent DeleteWindowsAsyncEvent => field ??= new AsyncExternalEvent(DeleteWindows);
}
After you can call a Raise method:
public partial class MyViewModel
{
private void DeleteCommand()
{
DeleteWindowsEvent.Raise();
}
}
The name of the generated properties is created based on the method name.
The generator appends Event for the synchronous property and AsyncEvent for the asynchronous property.
Methods without parameters
For void methods, the generator creates both sync and async properties:
[ExternalEvent]
private void DeleteWindows()
{
_document.Delete(_windowIds);
}
// Generates:
// IExternalEvent DeleteWindowsEvent
// IAsyncExternalEvent DeleteWindowsAsyncEvent
For methods that return a value, only an async property is generated:
[ExternalEvent]
private int CountWindows()
{
return _document.GetInstanceIds(BuiltInCategory.OST_Windows).Count;
}
// Generates:
// IAsyncRequestExternalEvent<int> CountWindowsAsyncEvent
Methods with UIApplication parameter
When a method accepts a UIApplication parameter, the generated events pass the application instance to the handler:
[ExternalEvent]
private void DeleteWindows(UIApplication application)
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete windows");
transaction.Start();
document.Delete(document.GetInstanceIds(BuiltInCategory.OST_Windows));
transaction.Commit();
}
// Generates:
// IExternalEvent DeleteWindowsEvent
// IAsyncExternalEvent DeleteWindowsAsyncEvent
Methods with one extra parameter
When a method has one additional parameter beyond the optional UIApplication,
the generator creates typed event properties:
[ExternalEvent]
private void DeleteWindow(UIApplication application, ElementId elementId)
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Delete window");
transaction.Start();
document.Delete(elementId);
transaction.Commit();
}
// Generates:
// IExternalEvent<ElementId> DeleteWindowEvent
// IAsyncExternalEvent<ElementId> DeleteWindowAsyncEvent
For methods with a return value:
[ExternalEvent]
private int CountWindows(UIApplication application, BuiltInCategory category)
{
return application.ActiveUIDocument.Document.GetInstanceIds(category).Count;
}
// Generates:
// IAsyncRequestExternalEvent<BuiltInCategory, int> CountWindowsAsyncEvent
Methods with multiple extra parameters
When a method has two or more extra parameters, the generator creates a sealed record to bundle them into a single argument type, along with extension methods to call Raise with individual parameters:
public partial class MyViewModel
{
private Room CreateRoom(UIApplication application, Level level, UV coordinate)
{
var document = application.ActiveUIDocument.Document;
using var transaction = new Transaction(document, "Create room");
transaction.Start();
var createdRoom = document.Create.NewRoom(level, coordinate);
transaction.Commit();
return createdRoom;
}
}
Will generate:
public partial class MyViewModel
{
public IAsyncRequestExternalEvent<CreateRoomArgs, Room> CreateRoomAsyncEvent => field ??= new IAsyncRequestExternalEvent<CreateRoomArgs, Room>(...);
public sealed record CreateRoomArgs(Level Level, UV Coordinate);
}
public static partial class MyViewModelExtensions
{
public static Task<Room> RaiseAsync(this IAsyncRequestExternalEvent<CreateRoomArgs, Room> externalEvent, Level level, UV coordinate)
{
return externalEvent.RaiseAsync(new CreateRoomArgs(level, coordinate));
}
}
These extensions allow you to call the event with individual arguments instead of creating a new Args:
var room = await CreateRoomAsyncEvent.RaiseAsync(level, coordinate);
Enabling direct invocation
Use the AllowDirectInvocation property on the attribute to configure the generated events to execute directly when Revit is in API mode:
[ExternalEvent(AllowDirectInvocation = true)]
private void DeleteWindows(UIApplication application)
{
//Execution logic
}
Context
Interfaces to global information about an application environment.
It allows access to application-specific data, as well as up-calls for application-level operations such as dialog and failure handling.
RevitApiContext
Provides members for accessing the Revit application context at the database level.
List of available environment properties:
- RevitApiContext.Application
RevitApiContext data can be accessed from any application execution location.
RevitApiContext provides access to failure suppression using disposable scopes.
By default, Revit uses manual error resolution control with user interaction. RevitApiContext provides automatic resolution of all failures without notifying the user or interrupting the program:
using (RevitApiContext.BeginFailureSuppressionScope())
{
using var transaction = new Transaction(document, "Operation");
transaction.Start();
// Operations that may cause failures
transaction.Commit();
}
// Failure handling is restored automatically
By default, all errors are handled for successful completion of the transaction. However, if you want to cancel the transaction and undo all failed changes, pass false as the parameter:
using (RevitApiContext.BeginFailureSuppressionScope(resolveErrors: false))
{
//User transactions
ModifyDocument();
}
RevitContext
Provides members for accessing the Revit application context at the UI level.
List of available environment properties:
- RevitContext.UiApplication
- RevitContext.ActiveDocument
- RevitContext.ActiveUiDocument
- RevitContext.ActiveView
- RevitContext.ActiveGraphicalView
- RevitContext.IsRevitInApiMode
RevitContext data can be accessed from any application execution location.
If your application can run in a separate thread or use API requests in an asynchronous context, use IsRevitInApiMode property to verify Revit API context:
public void Execute()
{
if (RevitContext.IsRevitInApiMode)
{
ModifyDocument();
}
}
RevitContext provides access to dialog suppression using disposable scopes:
using (RevitContext.BeginDialogSuppressionScope())
{
//User operations
LoadFamilies();
}
// Dialogs are restored automatically
You can specify a result code for the suppressed dialogs:
using (RevitContext.BeginDialogSuppressionScope(resultCode: 2))
{
LoadFamilies();
}
using (RevitContext.BeginDialogSuppressionScope(TaskDialogResult.Ok))
{
LoadFamilies();
}
using (RevitContext.BeginDialogSuppressionScope(MessageBoxResult.Yes))
{
LoadFamilies();
}
Or use a custom handler for more control:
using (RevitContext.BeginDialogSuppressionScope(args =>
{
var result = args.DialogId == "TaskDialog_ModelUpdater" ? TaskDialogResult.Ok : TaskDialogResult.Close;
args.OverrideResult((int)result);
}))
{
LoadFamilies();
}
Options
The Toolkit provides implementation of various Revit interfaces, with the possibility of customization.
FamilyLoadOptions
Contains an implementation for IFamilyLoadOptions. Provides a handler for loading families
document.LoadFamily(fileName, new FamilyLoadOptions(), out var family);
document.LoadFamily(fileName, new FamilyLoadOptions(false, FamilySource.Project), out var family);
document.LoadFamily(fileName, UIDocument.GetRevitUIFamilyLoadOptions(), out var family);
DuplicateTypeNamesHandler
Contains an implementation for IDuplicateTypeNamesHandler. Provides a handler of duplicate type names encountered during a paste operation.
var options = new CopyPasteOptions();
options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler());
options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(args => DuplicateTypeAction.Abort));
options.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(DuplicateTypeAction.UseDestinationTypes));
ElementTransformUtils.CopyElements(source, elementIds, destination, null, options);
SaveSharedCoordinatesCallback
Contains an implementation for ISaveSharedCoordinatesCallback. Provides a handler for control Revit when trying to unload or reload a Revit link with changes in shared coordinates.
var linkType = elementId.ToElement<RevitLinkType>(RevitContext.ActiveDocument);
linkType.Unload(new SaveSharedCoordinatesCallback());
linkType.Unload(new SaveSharedCoordinatesCallback(SaveModifiedLinksOptions.DoNotSaveLinks));
linkType.Unload(new SaveSharedCoordinatesCallback(type =>
{
if (type.AttachmentType == AttachmentType.Overlay) return SaveModifiedLinksOptions.SaveLinks;
return SaveModifiedLinksOptions.DoNotSaveLinks;
}));
FrameworkElementCreator
Contains an implementation for IFrameworkElementCreator.
Creator of FrameworkElements for the dockable pane.
DockablePaneProvider.Register(application, guid, title)
.SetConfiguration(data =>
{
data.FrameworkElementCreator = new FrameworkElementCreator<DockPaneView>();
data.FrameworkElementCreator = new FrameworkElementCreator<DockPaneView>(serviceProvider);
});
SelectionConfiguration
Contains an implementation for ISelectionFilter. Creates a configuration for creating Selection Filters.
By default, all elements are allowed for selection:
var selectionConfiguration = new SelectionConfiguration();
uiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);
You can also customize the selection of Element or Reference separately:
var selectionConfiguration = new SelectionConfiguration()
.Allow.Element(element => element.Category.Id.AreEquals(BuiltInCategory.OST_Walls));
uiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);
Or set rules for everything:
var selectionConfiguration = new SelectionConfiguration()
.Allow.Element(element => element.Category.Id.AreEquals(BuiltInCategory.OST_Walls))
.Allow.Reference((reference, xyz) => false);
uiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);
Decorators
Simplified implementation of raw Revit classes
DockablePaneProvider
Provides access to create a new dockable pane to the Revit user interface.
DockablePaneProvider
.Register(application, new Guid(), "Dockable pane")
.SetConfiguration(data =>
{
data.FrameworkElement = new RevitAddInView();
data.InitialState = new DockablePaneState
{
MinimumWidth = 300,
MinimumHeight = 400,
DockPosition = DockPosition.Right
};
});
Helpers
Provides auxiliary components
ResolveHelper
Provides handlers to resolve dependencies.
using (ResolveHelper.BeginAssemblyResolveScope<Application>())
{
window.Show();
}
// Assembly resolution is restored automatically
You can also pass a type directly:
using (ResolveHelper.BeginAssemblyResolveScope(typeof(ViewModel)))
{
return new Window();
}
Or specify a directory path directly:
using (ResolveHelper.BeginAssemblyResolveScope(@"C:\Libraries"))
{
return LoadExternalLibrary();
}
Scopes can be nested. Dependencies are searched from innermost to outermost scope:
using (ResolveHelper.BeginAssemblyResolveScope(@"C:\Shared\Common"))
using (ResolveHelper.BeginAssemblyResolveScope(@"C:\Plugin"))
{
// First searches in Plugin, then in Common
return new Window();
}
Enabled by default for ExternalCommand, AsyncExternalCommand, ExternalApplication and ExternalDBApplication.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows7.0 is compatible. |
-
net10.0-windows7.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Nice3point.Revit.Toolkit:
| Package | Downloads |
|---|---|
|
HcBimUtils
Package Description |
|
|
HC.Tech.So.Revit.Templates
Templates for Revit plugin development |
|
|
DTDucas.RevitBase
Base framework for Revit API development with comprehensive command handling, exception suppression, and external event management |
GitHub repositories (2)
Showing the top 2 popular GitHub repositories that depend on Nice3point.Revit.Toolkit:
| Repository | Stars |
|---|---|
|
lookup-foundation/RevitLookup
Interactive Revit RFA and RVT project database exploration tool to view and navigate BIM element parameters, properties and relationships.
|
|
|
Nice3point/RevitTemplates
Templates for creating Revit add-ins
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2027.0.0 | 99 | 4/7/2026 |
| 2027.0.0-preview.5.20260329 | 74 | 3/29/2026 |
| 2027.0.0-preview.4.20260310 | 87 | 3/10/2026 |
| 2026.1.0 | 211 | 4/7/2026 |
| 2026.1.0-preview.5.20260329 | 71 | 3/29/2026 |
| 2026.1.0-preview.4.20260310 | 98 | 3/10/2026 |
| 2025.2.0 | 248 | 4/7/2026 |
| 2025.2.0-preview.5.20260329 | 69 | 3/29/2026 |
| 2025.2.0-preview.4.20260310 | 95 | 3/10/2026 |
| 2024.3.0 | 256 | 4/7/2026 |
| 2024.3.0-preview.5.20260329 | 66 | 3/29/2026 |
| 2024.3.0-preview.4.20260310 | 79 | 3/10/2026 |
| 2023.4.0 | 225 | 4/7/2026 |
| 2023.4.0-preview.5.20260329 | 96 | 3/29/2026 |
| 2022.4.0 | 190 | 4/7/2026 |
| 2022.4.0-preview.5.20260329 | 59 | 3/29/2026 |
| 2021.4.0 | 133 | 4/7/2026 |
| 2021.4.0-preview.5.20260329 | 61 | 3/29/2026 |
| 2020.4.0 | 83 | 4/7/2026 |
| 2020.4.0-preview.5.20260329 | 46 | 3/29/2026 |