{"id":17335141,"url":"https://github.com/nice3point/revittoolkit","last_synced_at":"2025-05-16T11:02:27.016Z","repository":{"id":47955028,"uuid":"516402997","full_name":"Nice3point/RevitToolkit","owner":"Nice3point","description":"Toolkit for Revit plugin development ","archived":false,"fork":false,"pushed_at":"2025-04-02T13:02:25.000Z","size":234,"stargazers_count":103,"open_issues_count":4,"forks_count":18,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-14T23:42:17.412Z","etag":null,"topics":["revit","toolkit"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nice3point.png","metadata":{"files":{"readme":"Readme.md","changelog":"Changelog.md","contributing":"Contributing.md","funding":null,"license":"License.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-07-21T14:24:14.000Z","updated_at":"2025-05-12T17:41:29.000Z","dependencies_parsed_at":"2024-02-22T14:54:13.372Z","dependency_job_id":"64c66f43-86e8-483e-8be8-ff65e6c30f24","html_url":"https://github.com/Nice3point/RevitToolkit","commit_stats":{"total_commits":180,"total_committers":2,"mean_commits":90.0,"dds":0.02777777777777779,"last_synced_commit":"06be21872e7b4b0f0ed0c246f57df79608a90476"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nice3point%2FRevitToolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nice3point%2FRevitToolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nice3point%2FRevitToolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nice3point%2FRevitToolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nice3point","download_url":"https://codeload.github.com/Nice3point/RevitToolkit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254518383,"owners_count":22084374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["revit","toolkit"],"created_at":"2024-10-15T15:08:21.108Z","updated_at":"2025-05-16T11:02:26.923Z","avatar_url":"https://github.com/Nice3point.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" width=\"610\" srcset=\"https://github.com/Nice3point/RevitToolkit/assets/20504884/852aba24-118f-4908-949d-2e0c019c83da\"\u003e\n        \u003cimg alt=\"RevitLookup\" width=\"610\" src=\"https://github.com/Nice3point/RevitToolkit/assets/20504884/c59042df-b9b5-4829-9417-006912781cf2\"\u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n## Make Revit API more flexible\n\n[![Nuget](https://img.shields.io/nuget/vpre/Nice3point.Revit.Toolkit?style=for-the-badge)](https://www.nuget.org/packages/Nice3point.Revit.Toolkit)\n[![Downloads](https://img.shields.io/nuget/dt/Nice3point.Revit.Toolkit?style=for-the-badge)](https://www.nuget.org/packages/Nice3point.Revit.Toolkit)\n[![Last Commit](https://img.shields.io/github/last-commit/Nice3point/RevitToolkit/develop?style=for-the-badge)](https://github.com/Nice3point/RevitToolkit/commits/develop)\n\nThis library provides a modern interface for working with the Revit API.\nPackage contains interfaces implementation frequently encountered in revit, aiming to provide as much flexibility as possible, so developers are free to choose which components to\nuse.\n\n## Installation\n\nYou can install the Toolkit as a [NuGet package](https://www.nuget.org/packages/Nice3point.Revit.Toolkit).\n\nThe packages are compiled for specific versions of Revit. To support different versions of libraries in one project, use the `RevitVersion` property:\n\n```xml\n\u003cPackageReference Include=\"Nice3point.Revit.Toolkit\" Version=\"$(RevitVersion).*\"/\u003e\n```\n\nPackage included by default in [Revit Templates](https://github.com/Nice3point/RevitTemplates).\n\n## Table of contents\n\n* [ExternalCommand](#externalcommand)\n* [ExternalApplication](#externalapplication)\n* [ExternalDBApplication](#externaldbapplication)\n* [External events](#external-events)\n  * [ActionEventHandler](#actioneventhandler)\n  * [IdlingEventHandler](#idlingeventhandler)\n  * [AsyncEventHandler](#asynceventhandler)\n  * [AsyncEventHandler\\\u003cT\u003e](#asynceventhandlert)\n* [Context](#context)\n* [Options](#options)\n  * [FamilyLoadOptions](#familyloadoptions)\n  * [DuplicateTypeNamesHandler](#duplicatetypenameshandler)\n  * [SaveSharedCoordinatesCallback](#savesharedcoordinatescallback)\n  * [FrameworkElementCreator](#frameworkelementcreator)\n  * [SelectionConfiguration](#selectionconfiguration)\n* [Decorators](#decorators)\n  * [DockablePaneProvider](#dockablepaneprovider)\n* [Helpers](#helpers)\n  * [ResolveHelper](#resolvehelper)\n* [Samples](#samples)\n  * [External application flow control](#external-application-flow-control)\n  * [External command flow control](#external-command-flow-control)\n\n## Features\n\n### ExternalCommand\n\nContains an implementation for **IExternalCommand**.\n\nOverride method **Execute()** to implement and external command within Revit.\n\nThe following properties provide access to the external command execution context:\n\n```c#\n[Transaction(TransactionMode.Manual)]\npublic class Command : ExternalCommand\n{\n    public override void Execute()\n    {\n        var title = Document.Title;\n        var viewName = ActiveView.Name;\n        var username = Application.Username;\n        var selection = UiDocument.Selection;\n        var windowHandle = UiApplication.MainWindowHandle;\n    }\n}\n```\n\n**ExternalCommand** includes dependency resolution for external dependencies, to avoid `FileNotFoundException` exceptions. \nDependencies are searched for in the plugin folder.\n\n### ExternalApplication\n\nContains an implementation for **IExternalApplication**.\n\nOverride method **OnStartup()** to execute some tasks when Revit starts.\n\nOverride method **OnShutdown()** to execute some tasks when Revit shuts down.\n\nThe following properties provide access to the external application execution context:\n\n```c#\npublic class Application : ExternalApplication\n{\n    public override void OnStartup()\n    {\n        var panel = Application.CreatePanel(\"Commands\", \"RevitAddin\");\n        panel.AddPushButton\u003cCommand\u003e(\"Execute\");\n            .SetImage(\"/RevitAddin;component/Resources/Icons/RibbonIcon16.png\");\n            .SetLargeImage(\"/RevitAddin;component/Resources/Icons/RibbonIcon32.png\");\n    }\n\n    public override void OnShutdown()\n    {\n    }\n}\n```\n\n**ExternalApplication** includes dependency resolution for external dependencies, to avoid `FileNotFoundException` exceptions.\nDependencies are searched for in the plugin folder.\n\n### ExternalDBApplication\n\nContains an implementation for **IExternalDBApplication**.\n\n```c#\npublic class Application : ExternalDBApplication\n{\n    public override void OnStartup()\n    {\n    }\n\n    public override void OnShutdown()\n    {\n    }\n}\n```\n\nOverride method **OnStartup()** to execute some tasks when Revit starts.\n\nOverride 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.\n\n**ExternalDBApplication** includes dependency resolution for external dependencies, to avoid `FileNotFoundException` exceptions.\nDependencies are searched for in the plugin folder.\n\n### External events\n\nThe Toolkit provides implementations of **IExternalEventHandler** for various scenarios. These handlers are used to modify the Revit document from another thread, which is particularly useful when working with modeless windows.\n\n#### ActionEventHandler\n\nA handler that provides access to modify a Revit document outside the execution context with queue support for Raise method calls.\n\nCalling a handler in a Revit context will call it immediately, without adding it to the queue.\n\n```c#\nprivate readonly ElementId _elementId = new(12869);\nprivate readonly ActionEventHandler _actionEventHandler = new();\n\nprivate void DeteleElement()\n{\n    _actionEventHandler.Raise(application =\u003e\n    {\n        var document = application.ActiveUIDocument.Document;\n        using var transaction = new Transaction(document, \"Delete element\");\n        transaction.Start();\n        document.Delete(_elementId);\n        transaction.Commit();\n        \n        Debug.WriteLine(\"Deleted\");\n    });\n\n    Debug.WriteLine(\"Command completed\");\n}\n```\n\nDebug output in a Revit context:\n\n```text\nCommand completed\nDeleted\n```\n\nDebug output outside the Revit context:\n\n```text\nDeleted\nCommand completed\n```\n\n#### IdlingEventHandler\n\nWith this handler, you can queue delegates for method calls when Revit becomes available again.\nUnsubscribing from the Idling event occurs immediately.\nSuitable for cases where you need to call code when Revit receives focus.\nFor example, to display a window after loading a family into a project.\n\n```c#\nprivate readonly IdlingEventHandler _idlingEventHandler = new();\n\nprivate void NotifyOnIdling()\n{\n    _idlingEventHandler.Raise(application =\u003e\n    {\n        var view = new FamilyBrowser();\n        view.Show();\n        \n        Debug.WriteLine(\"Idling\");\n    });\n\n    Debug.WriteLine(\"Command completed\");\n}\n```\n\nDebug output:\n\n```text\nCommand completed\nIdling\n```\n\n#### AsyncEventHandler\n\nWith this handler, you can wait for the external event to complete.\nThe **RaiseAsync** method will return to its previous context after executing the method encapsulated in the delegate.\nSuitable for cases where you need to maintain the sequence of code execution.\n\nExceptions in the delegate will not be ignored and will be rethrown in the original synchronization context.\n\nCalling the handler in a Revit context will call it immediately without adding it to the queue and awaiting with `await` keyword will not cause a context switch, \nand you can still call API requests in the main Revit thread.\n\n```c#\nprivate readonly AsyncEventHandler _asyncEventHandler = new();\n\nprivate async Task DeleteDoorsAsync()\n{\n    await _asyncEventHandler.RaiseAsync(application =\u003e\n    {\n        var doorIds = document.GetInstanceIds(BuiltInCategory.OST_Doors);\n        document.Delete(doorIds);\n\n        Debug.WriteLine(\"Doors deleted\");\n    });\n\n    Debug.WriteLine(\"Command completed\");\n}\n```\n\nDebug output:\n\n```text\nDoors deleted\nCommand completed\n```\n\n#### AsyncEventHandler\\\u003cT\u003e\n\nWith this handler, you can wait for the external event to complete with the return value from the method encapsulated in the delegate.\nThe **RaiseAsync** method will return to its previous context after executing.\nSuitable for cases where you need to maintain the sequence of code execution.\n\nExceptions in the delegate will not be ignored and will be rethrown in the original synchronization context\n\nCalling the handler in a Revit context will call it immediately without adding it to the queue and awaiting with `await` keyword will not cause a context switch,\nand you can still call API requests in the main Revit thread.\n\n```c#\nprivate readonly AsyncEventHandler\u003cint\u003e _asyncEventHandler = new();\n\nprivate async Task GetWindowsCountAsync()\n{\n    var windowsCount = await _asyncEventHandler.RaiseAsync(application =\u003e\n    {\n        var uiDocument = application.ActiveUIDocument;\n        var elementIds = uiDocument.Document.GetInstanceIds(BuiltInCategory.OST_Windows);\n        uiDocument.Selection.SetElementIds(elementIds);\n\n        Debug.WriteLine(\"Windows selected\");\n        return elementIds.Count;\n    });\n\n    Debug.WriteLine($\"Windows count {windowsCount}\");\n    Debug.WriteLine(\"Command completed\");\n}\n```\n\nDebug output:\n\n```text\nWindows selected\nWindows count 17\nCommand completed\n```\n\n### Context\n\nInterface to global information about an application environment.\n\nIt allows access to application-specific data, as well as up-calls for application-level operations such as dialog and failure handling.\n\nList of available environment properties:\n\n- Context.Application\n- Context.UiApplication\n- Context.UiControlledApplication\n- Context.ActiveDocument\n- Context.ActiveUiDocument\n- Context.ActiveView\n- Context.ActiveGraphicalView\n- Context.IsRevitInApiMode\n\n**Context** data can be accessed from any application execution location:\n\n```C#\npublic void Execute()\n{\n    Context.ActiveDocument.Delete(elementId);\n    Context.ActiveView = view;\n}\n```\n\nIf your application can run in a separate thread or use API requests in an asynchronous context, perform an **IsRevitInApiMode** check.\n\nA direct API call should be used if Revit is currently within an API context, otherwise API calls should be handled by `IExternalEventHandler`:\n\n```C#\npublic void Execute()\n{\n    if (Context.IsRevitInApiMode)\n    {\n        ModifyDocument();\n    }\n    else\n    {\n        externalEventHandler.Raise(application =\u003e ModifyDocument());\n    }\n}\n```\n\n**Context** provides access to global application handlers for dialog and failure management:\n\n```C#\ntry\n{\n    Context.SuppressDialogs();\n    Context.SuppressDialogs(resultCode: 2);\n    Context.SuppressDialogs(args =\u003e\n    {\n        var result = args.DialogId == \"TaskDialog_ModelUpdater\" ? TaskDialogResult.Ok : TaskDialogResult.Close;\n        args.OverrideResult((int)result);\n    });\n    \n    //User operations\n    LoadFamilies();\n}\nfinally\n{\n    Context.RestoreDialogs();\n}\n```\n\nBy default, Revit uses manual error resolution control with user interaction.\nContext provides automatic resolution of all failures without notifying the user or interrupting the program.\n\nBy default, all errors are handled for successful completion of the transaction.\nHowever, if you want to cancel the transaction and undo all failed changes, pass false as the parameter:\n\n```C#\ntry\n{\n    Context.SuppressFailures();\n    Context.SuppressFailures(resolveErrors: false);\n    \n    //User transactions\n    ModifyDocument();\n}\nfinally\n{\n    Context.RestoreFailures();\n}\n```\n\n### Options\n\nThe Toolkit provides implementation of various Revit interfaces, with the possibility of customization.\n\n#### FamilyLoadOptions\n\nContains an implementation for **IFamilyLoadOptions**.\nProvides a handler for loading families\n\n```c#\ndocument.LoadFamily(fileName, new FamilyLoadOptions(), out var family);\ndocument.LoadFamily(fileName, new FamilyLoadOptions(false, FamilySource.Project), out var family);\ndocument.LoadFamily(fileName, UIDocument.GetRevitUIFamilyLoadOptions(), out var family);\n```\n\n#### DuplicateTypeNamesHandler\n\nContains an implementation for **IDuplicateTypeNamesHandler**.\nProvides a handler of duplicate type names encountered during a paste operation.\n\n```c#\nvar options = new CopyPasteOptions();\noptions.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler());\noptions.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(args =\u003e DuplicateTypeAction.Abort));\noptions.SetDuplicateTypeNamesHandler(new DuplicateTypeNamesHandler(DuplicateTypeAction.UseDestinationTypes));\nElementTransformUtils.CopyElements(source, elementIds, destination, null, options);\n```\n\n#### SaveSharedCoordinatesCallback\n\nContains an implementation for **ISaveSharedCoordinatesCallback**.\nProvides a handler for control Revit when trying to unload or reload a Revit link with changes in shared coordinates.\n\n```c#\nvar linkType = elementId.ToElement\u003cRevitLinkType\u003e(Context.ActiveDocument);\nlinkType.Unload(new SaveSharedCoordinatesCallback());\nlinkType.Unload(new SaveSharedCoordinatesCallback(SaveModifiedLinksOptions.DoNotSaveLinks));\nlinkType.Unload(new SaveSharedCoordinatesCallback(type =\u003e\n{\n    if (type.AttachmentType == AttachmentType.Overlay) return SaveModifiedLinksOptions.SaveLinks;\n    return SaveModifiedLinksOptions.DoNotSaveLinks;\n}));\n```\n\n#### FrameworkElementCreator\n\nContains an implementation for **IFrameworkElementCreator**.\nCreator of `FrameworkElements` for the dockable pane.\n\n```c#\nDockablePaneProvider.Register(application, guid, title)\n    .SetConfiguration(data =\u003e\n    {\n        data.FrameworkElementCreator = new FrameworkElementCreator\u003cDockPaneView\u003e();\n        data.FrameworkElementCreator = new FrameworkElementCreator\u003cDockPaneView\u003e(serviceProvider);\n    });\n```\n\n#### SelectionConfiguration\n\nContains an implementation for **ISelectionFilter**.\nCreates a configuration for creating Selection Filters.\n\nBy default, all elements are allowed for selection:\n\n```c#\nvar selectionConfiguration = new SelectionConfiguration();\nuiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);\n```\n\nYou can also customize the selection of Element or Reference separately:\n\n```c#\nvar selectionConfiguration = new SelectionConfiguration()\n        .Allow.Element(element =\u003e element.Category.Id.AreEquals(BuiltInCategory.OST_Walls));\n\nuiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);\n```\n\nOr set rules for everything:\n\n```c#\nvar selectionConfiguration = new SelectionConfiguration()\n    .Allow.Element(element =\u003e element.Category.Id.AreEquals(BuiltInCategory.OST_Walls))\n    .Allow.Reference((reference, xyz) =\u003e false);\n\nuiDocument.Selection.PickObject(ObjectType.Element, selectionConfiguration.Filter);\n```\n\n### Decorators\n\nSimplified implementation of raw Revit classes\n\n#### DockablePaneProvider\n\nProvides access to create a new dockable pane to the Revit user interface.\n\n```c#\nDockablePaneProvider\n    .Register(application, new Guid(), \"Dockable pane\")\n    .SetConfiguration(data =\u003e\n    {\n        data.FrameworkElement = new RevitAddInView();\n        data.InitialState = new DockablePaneState\n        {\n            MinimumWidth = 300,\n            MinimumHeight = 400,\n            DockPosition = DockPosition.Right\n        };\n    });\n```\n\n### Helpers\n\nProvides auxiliary components\n\n#### ResolveHelper\n\nProvides handlers to resolve dependencies for Revit 2025 and older.\n\n```c#\ntry\n{\n    ResolveHelper.BeginAssemblyResolve\u003cApplication\u003e();\n    window.Show();\n}\nfinally\n{\n    ResolveHelper.EndAssemblyResolve();\n}\n```\n\nEnabled by default for `ExternalCommand`, `ExternalApplication` and `ExternalDBApplication`.\n\n### Samples\n\n#### External application flow control\n\nAdding a button to the Revit ribbon based on the username. \n`IExternalApplication` does not provide access to `Application`, but you can use the **Context** class to access the environment data to get the username:\n\n```c#                                                                                \npublic class Application : ExternalApplication                                       \n{                                                                                    \n    public override void OnStartup()                                                 \n    {                                                                                \n        var panel = Application.CreatePanel(\"Commands\", \"RevitAddin\");\n        panel.AddPushButton\u003cCommand\u003e(\"Execute\");\n        \n        var userName = Context.Application.Username;                                 \n        if (userName == \"Administrator\")                                                \n        {                                                                            \n            var panel = Application.CreatePanel(\"Secret Panel\", \"RevitAddin\");\n            panel.AddPushButton\u003cCommand\u003e(\"Execute\");\n        }                                                                            \n    }       \n}                                                                                    \n```   \n\nSuppression of OnShutdown call in case of unsuccessful plugin startup:\n\n```c#                                                                                \npublic class Application : ExternalApplication                                       \n{         \n    private ApplicationHosting _applicationHosting;\n    \n    public override void OnStartup()                                                 \n    {        \n        var isValid = LicenseManager.Validate();\n        if (!isValid)                                                \n        {     \n            //If Result is overridden as Result.Failed, the OnShutdown() method will not be called\n            Result = Result.Failed;                                                  \n            return;                                                                  \n        }\n        \n        //Running the plugin environment in case of successful license verification\n        _applicationHosting = ApplicationHosting.Run();\n    }       \n    \n    public override void OnShutdown()\n    {\n        //These methods will not be called if the license check fails on startup\n        _applicationHosting.SaveData();\n        _applicationHosting.Shutdown();\n    }\n}                                                                                    \n```          \n\n#### External command flow control\n\nAutomatic transaction management without displaying additional dialogs to the user in case of an error.\nCan be used to use Modal windows when errors and dialogs change modal mode to modeless:\n\n```c#\n[Transaction(TransactionMode.Manual)]\npublic class Command : ExternalCommand\n{\n    public override void Execute()\n    {\n        //Suppresses all possible warnings and errors during command execution\n        Context.SuppressDialogs();\n        Context.SuppressFailures();\n        \n        try\n        {\n            //Action\n            var selectedIds = UiDocument.Selection.GetElementIds();\n            \n            using var transaction = new Transaction(Document);\n            transaction.Start(\"Delete elements\");\n            Document.Delete(selectedIds);\n            transaction.Commit();\n        }\n        finally\n        {\n            //Restore normal application error and dialogs handling when exiting an external command            \n            Context.RestoreDialogs();\n            Context.RestoreFailures();\n        }\n    }\n}\n```\n\nRedirecting errors to the revit dialog box, highlighting unsuccessfully deleted elements in the model:\n\n```c#\n[Transaction(TransactionMode.Manual)]\npublic class Command : ExternalCommand\n{\n    public override void Execute()\n    {\n        var selectedIds = UiDocument.Selection.GetElementIds();\n        \n        try\n        {\n            //Action\n            using var transaction = new Transaction(Document);\n            transaction.Start(\"Delete elements\");\n            Document.Delete(selectedIds);\n            transaction.Commit();\n        }\n        catch\n        {\n            //Redirecting errors to the Revit dialog with elements highlighting\n            Result = Result.Failed;\n            ErrorMessage = \"Unable to delete selected elements\";\n            foreach (var selectedId in selectedIds)\n            {\n                ElementSet.Insert(selectedId.ToElement(Document));\n            }\n        }\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnice3point%2Frevittoolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnice3point%2Frevittoolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnice3point%2Frevittoolkit/lists"}