Nice3point.Revit.Toolkit 2022.2.1

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Nice3point.Revit.Toolkit --version 2022.2.1                
NuGet\Install-Package Nice3point.Revit.Toolkit -Version 2022.2.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nice3point.Revit.Toolkit" Version="2022.2.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Nice3point.Revit.Toolkit --version 2022.2.1                
#r "nuget: Nice3point.Revit.Toolkit, 2022.2.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Nice3point.Revit.Toolkit as a Cake Addin
#addin nuget:?package=Nice3point.Revit.Toolkit&version=2022.2.1

// Install Nice3point.Revit.Toolkit as a Cake Tool
#tool nuget:?package=Nice3point.Revit.Toolkit&version=2022.2.1                

Make Revit API more flexible

Nuget Downloads Last Commit

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 Toolkit as a nuget package.

Packages are compiled for a specific version of Revit, to support different versions of libraries in one project, use RevitVersion property.

<PackageReference Include="Nice3point.Revit.Toolkit" Version="$(RevitVersion).*"/>

Package included by default in Revit Templates.

Table of contents

Features

External command

The ExternalCommand class contains an implementation for IExternalCommand.

[Transaction(TransactionMode.Manual)]
public class Command : ExternalCommand
{
    public override void Execute()
    {
    }
}

Override method Execute() to implement and external command within Revit.

You can suppress the display of exceptions, dialog boxes, without subscribing to events.

Data available when executing an external command is accessible by properties.

[Transaction(TransactionMode.Manual)]
public class Command : ExternalCommand
{
    public override void Execute()
    {
        var title = Document.Title;
        var viewName = ActiveView.Name;
        var username = Application.Username;
        var selection = UiDocument.Selection;
        var windowHandle = UiApplication.MainWindowHandle;

        if (title.Equals("Untitled"))
        {
            Result = Result.Cancelled;
            return;
        }

        SuppressDialogs();
        SuppressDialogs(args => args.OverrideResult(2));
        RestoreDialogs();

        SuppressFailures();
        RestoreFailures();
    }
}

ExternalCommand contains the logic for resolving dependencies. Now you may not encounter a FileNotFoundException. Dependencies are searched in the plugin folder.

Starting with Revit 2025, ExternalCommand uses AssemblyLoadContext to isolate dependencies. This feature allows plugins to run in a separate, isolated context, ensuring independent operation and preventing conflicts from incompatible library versions

External application

The ExternalApplication class contains an implementation for IExternalApplication.

public class Application : ExternalApplication
{
    public override void OnStartup()
    {
    }

    public override void OnShutdown()
    {
    }
}

Override method OnStartup() to execute some tasks when Revit starts.

Override method OnShutdown() to execute some tasks when Revit shuts down. You don't have to override this method if you don't plan to use it.

Data available when executing an external command is accessible by properties.

public class Application : ExternalApplication
{
    public override void OnStartup()
    {
        var userName = Context.Application.Username;
        if (userName != "Nice3point")
        {
            //If Result is overridden, the OnShutdown() method will not be called
            Result = Result.Failed;
            return;
        }
        
        var panel = Application.CreatePanel("Secret Panel", "RevitAddin");
        var showButton = panel.AddPushButton<Command>("Execute");
        showButton.SetImage("/RevitAddin;component/Resources/Icons/RibbonIcon16.png");
        showButton.SetLargeImage("/RevitAddin;component/Resources/Icons/RibbonIcon32.png");
    }
}

ExternalApplication contains the logic for resolving dependencies. Now you may not encounter a FileNotFoundException. Dependencies are searched in the plugin folder.

Starting with Revit 2025, ExternalApplication uses AssemblyLoadContext to isolate dependencies. This feature allows plugins to run in a separate, isolated context, ensuring independent operation and preventing conflicts from incompatible library versions

External DB application

The ExternalDBApplication class contains an implementation for IExternalDBApplication.

public class Application : ExternalDBApplication
{
    public override void OnStartup()
    {
    }

    public override void OnShutdown()
    {
    }
}

Override method OnStartup() to execute some tasks when Revit starts.

Override method OnShutdown() to execute some tasks when Revit shuts down. You don't have to override this method if you don't plan to use it.

ExternalDBApplication contains the logic for resolving dependencies. Now you may not encounter a FileNotFoundException. Dependencies are searched in the plugin folder.

Starting with Revit 2025, ExternalApplication uses ExternalDBApplication to isolate dependencies. This feature allows plugins to run in a separate, isolated context, ensuring independent operation and preventing conflicts from incompatible library versions

External events

EventHandler classes contains an implementation for IExternalEventHandler.

It is used to modify the document when using modeless windows. You can create your own handlers by deriving from this class.

ActionEventHandler

With this handler, you can queue delegates for method calls:

public ViewModel
{
    ActionEventHandler = new ActionEventHandler();
}

public ActionEventHandler ActionEventHandler { get; }
public ElementId ElementId { get; set; }

private void DeteleElement()
{
    ActionEventHandler.Raise(application =>
    {
        var document = application.ActiveUIDocument.Document;
        using var transaction = new Transaction(document, $"Delete element");
        transaction.Start();
        document.Delete(ElementId)
        transaction.Commit();
        
        Debug.WriteLine("Deleted");
    });

    Debug.WriteLine("Command completed");
}

Debug output:

Command completed
Deleted
IdlingEventHandler

With this handler, you can queue delegates for method calls when Revit becomes available again. Unsubscribing from the Idling event occurs immediately. Suitable for cases where you need to call code when Revit receives focus. For example, to display a window after loading a family into a project.

public ViewModel
{
    IdlingEventHandler = new IdlingEventHandler();
}

public IdlingEventHandler IdlingEventHandler { get; }

private void NotifyOnIdling()
{
    IdlingEventHandler.Raise(application =>
    {
        var view = new FamilyBrowser();
        view.Show();
        
        Debug.WriteLine("Idling");
    });

    Debug.WriteLine("Command completed");
}

Debug output:

Command completed
Idling
AsyncEventHandler

With this handler, you can wait for the external event to complete. The RaiseAsync method will return to its previous context after executing the method encapsulated in the delegate. Suitable for cases where you need to maintain the sequence of code execution.

Exceptions in the delegate will not be ignored and will be rethrown in the original synchronization context.

public ViewModel
{
    AsyncEventHandler = new AsyncEventHandler();
}

public AsyncEventHandler AsyncEventHandler { get; }

private async Task DeleteDoorsAsync()
{
    await AsyncEventHandler.RaiseAsync(application =>
    {
        var doorIds = document.GetInstanceIds(BuiltInCategory.OST_Doors);
        document.Delete(doorIds);

        Debug.WriteLine("Doors deleted");
    });

    Debug.WriteLine("Command completed");
}

Debug output:

Doors deleted
Command completed
AsyncEventHandler<T>

With this handler, you can wait for the external event to complete with the return value from the method encapsulated in the delegate. The RaiseAsync method will return to its previous context after executing. Suitable for cases where you need to maintain the sequence of code execution.

Exceptions in the delegate will not be ignored and will be rethrown in the original synchronization context

public ViewModel
{
    AsyncEventHandler = new AsyncEventHandler<int>();
}

public AsyncEventHandler<int> AsyncEventHandler { get; }

private async Task GetWindowsCountAsync()
{
    var windowsCount = await AsyncEventHandler.RaiseAsync(application =>
    {
        var uiDocument = application.ActiveUIDocument;
        var elementIds = uiDocument.Document.GetInstanceIds(BuiltInCategory.OST_Windows);
        uiDocument.Selection.SetElementIds(elementIds);

        Debug.WriteLine("Windows selected");
        return elementIds.Count;
    });

    Debug.WriteLine($"Windows count {windowsCount}");
    Debug.WriteLine("Command completed");
}

Debug output:

Windows selected
Windows count 17
Command completed

External Command Availability

ExternalCommandAvailability classes contains an implementation for IExternalCommandAvailability.

It provides the implementation for an accessibility check for a Revit add-in External Command.

Starting with Revit 2025, ExternalCommandAvailability uses AssemblyLoadContext to isolate dependencies. If your implementation does not include dependencies, use the IExternalCommandAvailability interface to reduce memory allocation

AvailableCommandController

Controller providing permanent accessibility for External Command invocation. This means that it will always be available for execution, even when no Document is open

panel.AddPushButton<StartupCommand>("Execute")
    .SetAvailabilityController<AvailableCommandController>()

Context

Provides computed properties to retrieve Revit objects in the current session. Values are provided even outside the Revit context.

  • Context.UiApplication;
  • Context.Application;
  • Context.UiDocument;
  • Context.Document;
  • Context.ActiveView;
  • Context.ActiveGraphicalView;
  • Context.ActiveView;
Context.Document.Create.NewFamilyInstance();
Context.ActiveView = view;

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>(Context.Document);
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);

Helpers

Provides auxiliary components

ResolveHelper

Provides handlers to resolve dependencies.

try
{
    ResolveHelper.BeginAssemblyResolve<DockView>();
    window.Show();
}
finally
{
    ResolveHelper.EndAssemblyResolve();
}

Enabled by default for ExternalCommand, ExternalApplication and ExternalDBApplication.

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
        };
    });

Transaction utils

The TransactionManager allows you to create transactions. You can write custom code in a lambda, and the method will take care of safely closing transactions in case of exceptions and cleaning up unmanaged resources.

Modification of the document, without specifying settings, the implementation already contains the name, and may not be specified:

document.Modify().Commit((document, transaction) =>
{
    document.Delete(new ElementId(69));
});

//The same, with an explicit indication of the transaction:
document.Modify(settings => settings.Transaction).Commit((document, transaction) =>
{
    document.Delete(new ElementId(69));
});

You can also create sub-transactions or groups of transactions:

document.Modify(settings => settings.SubTransaction).Commit((document, transaction) =>
{
    document.Delete(new ElementId(69));
});

document.Modify(settings => settings.GroupTransaction).Commit((document, transaction) =>
{
    transaction.RollBack();
});

You can apply various settings to modify a document:


document.Modify(settings => settings.Transaction
        .SetName("Deleting element 69")
        .DisableModalHandling()
        .EnableClearAfterRollback()
        .EnableDelayedMiniWarnings())
    .Commit((document, transaction) =>
    {
        document.Delete(new ElementId(69));
    });
Product Compatible and additional computed target framework versions.
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Nice3point.Revit.Toolkit:

Package Downloads
HcBimUtils

Package Description

HC.Tech.So.Revit.Templates

Templates for Revit plugin development

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on Nice3point.Revit.Toolkit:

Repository Stars
jeremytammik/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
2025.0.3 3,564 9/22/2024
2025.0.2 1,055 9/1/2024
2025.0.1 2,037 6/22/2024
2024.1.3 3,645 9/22/2024
2024.1.2 1,189 9/1/2024
2024.1.1 2,131 6/22/2024
2023.2.3 3,722 9/22/2024
2023.2.2 1,142 9/1/2024
2023.2.1 2,160 6/22/2024
2022.2.3 1,742 9/22/2024
2022.2.2 630 9/1/2024
2022.2.1 961 6/22/2024
2021.2.3 1,920 9/22/2024
2021.2.2 648 9/1/2024
2021.2.1 1,169 6/22/2024
2020.2.3 1,461 9/22/2024
2020.2.2 489 9/1/2024
2020.2.1 872 6/22/2024
2019.0.12 2,045 12/28/2022 2019.0.12 is deprecated because it is no longer maintained.

## Add-in dependencies isolation

This release introduces an isolated plugin dependency container using .NET **AssemblyLoadContext**.
This feature allows plugins to run in a separate, isolated context, ensuring
independent operation and preventing conflicts from incompatible library versions.
This enhancement is available for Revit 2025 and higher, addressing the limitations of Revit's traditional plugin loading mechanism, which loads plugins by path without native support for isolation.

![изображение](https://github.com/jeremytammik/RevitLookup/assets/20504884/d1e160a2-36ef-43ad-a384-fdcc15b0106e)

**How It Works:**

The core functionality centers on **AssemblyLoadContext**, which creates an isolated container for each plugin.
When a plugin is loaded, it is assigned a unique **AssemblyLoadContext** instance, encapsulating the plugin and its dependencies to prevent interference with other plugins or the main application.

To use this isolation feature, developers must inherit their classes from:
- ExternalCommand
- ExternalApplication
- ExternalDbApplication
- ExternalCommandAvailability

These classes contain the built-in isolation mechanism under the hood.
Plugins using interfaces such as **IExternalCommand** will not benefit from this isolation and will run in the default context.

**Limitations:**

- The isolated plugin context feature is available starting with Revit 2025.
- For older Revit versions, this library uses a **ResolveHelper** to help load dependencies from the plugin's folder, but does not protect against conflicts arising from incompatible packages.
- Additionally, plugins that do not inherit from the specified classes will not be isolated and may experience compatibility issues if they rely on the default context.

## Improvements

- Added **ExternalCommandAvailability** class.

   It involves isolating dependencies.
   If your implementation does not include dependencies, use the IExternalCommandAvailability interface to reduce memory allocation
- Added **AvailableCommandController** class.

   ExternalCommandAvailability implementation.
   Controller providing permanent accessibility for ExternalCommand invocation. Usage:
   ```C#
   panel.AddPushButton<StartupCommand>(Execute)
       .SetAvailabilityController<AvailableCommandController>()
   ```