kennanchan / revit.async Goto Github PK
View Code? Open in Web Editor NEWUse task-based asynchronous pattern (TAP) to run Revit API code from any execution context.
License: MIT License
Use task-based asynchronous pattern (TAP) to run Revit API code from any execution context.
License: MIT License
The await RevitTask.RunAsync() does not seem to wait for Revit to finish. In the below sample method, RevitTask.RunAsync() returns "Jimmy" before the Selection is set in the ActiveUiDocument. So my MessageBox shows "jimmy" before the selection is made. I'm not sure why this would be the case.
private async void SelectInRevit()
{
var myString = await RevitTask.RunAsync(
app =>
{
app.ActiveUIDocument.Selection.SetElementIds(SelectedFam.ElementIds);
return "jimmy";
});
MessageBox.Show($"{myString}");
}
I'm not getting Revit.Async to work, and I think it might be because I am trying to implement it within an IExternalApplication instead of an IExternalCommand. I tried to start a simple method within RevitTask.RunAsync(), and it never finishes running. So in this sample method, I never reach the second line of code:
private async void SelectInRevit()
{
await RevitTask.RunAsync(
UIApp =>
{
UIApp.ActiveUIDocument.Selection.SetElementIds(SelectedFam.ElementIds);
});
SelectionChangeable = !SelectionChangeable;
}
Great tool!
Have you considered releasing this as a Nuget package?
Hi,
I think this repo can be useful to execute a Console App from Revit, because the console app has async code, so I want to await the result of the console app before I trigger any ExternalEvent
from Revit.
I'm new to this, so perhaps there is a simple way to explain how to:
Start Revit Command
-> Start Console App
-> Await Result
-> use Result
in ExternalEvent
-> do Transaction()
any tips on how to achieve this?
Hi,
Thanks a lot for this awesome repo!
The title is my question :
Is there a way to trigger the SaveFamilyCommand() with a MouseEventArgs ?
In the current case, the trigger is the TestWindow Button to save a family.
I would like to change the trigger with a MouseEventArgs but because the SaveFamilyCommand is a ICommand class, I have some trouble achieving this...
We've been using Revit.Async for a while on our project and generally it works great!
However, recently we've run into an issue where running a task from a CefSharp callback never terminates.
CefSharp is a utility that allows us to trigger C# code from an embedded browser in Revit. The callback must be synchronous, like so:
public string GetSomeData()
{
var task = RevitTask.RunAsync(app => getSomethingFromRevit(app));
task.Wait();
return task.Result;
}
GetSomeData()
will be invoked by CefSharp via our Javascript code (I have not been able to find information about how or where exactly it is invoked.)
This has been working fine for a while, but recently it has started hanging on task.Wait()
. I suspect this is related to the well-known issue of deadlock when calling async code from a synchronous context.
However, I've tried pretty much every workaround I have found on the internet, including adding .ConfigureAwait(false)
to all of the await
calls in the Revit.Async code.
If anyone has any ideas of what might be going on, it would be greatly appreciated.
When I use RevitTask.RunAsync (() => SomeMethod()) the GUI get frozen. If I use a simple task like Task.Delay(10000) it works correctly. What could be the issue?
Said that, thanks for this library. It's awesome!
Hi and thanks for creating a great library!
I've used the nuget package for a while without any errors. But now I am on a new PC and trying to use it on a new project (with Revit 2020), and I get a FileLoadException stating that the Revit assemblies or dependencies could not be loaded:
I have tried all versions on nuget and get the same exception with them all. I don't know if this is a problem on my computer or something else but I can't figure out how to resolve it or investigate it further. I even tried placing Revit 2016 DLLs along side the addin and add an AssebmlyResolve but without any luck.
Any ideas are greatly appreciated. Thanks
Hello,
I'm having a problem, I have 2 addins that use RevitTask when I do
RevitTask.Initialize(application);
Addin 1 Application :
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Revit.Async;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RevitTaskAddin1
{
public class Application : IExternalApplication
{
public static AddInId ActiveAddInId { get; private set; }
public static string PluginName { get; set; }
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
ActiveAddInId = application.ActiveAddInId;
PluginName = application.ActiveAddInId.GetAddInName();
var tabName = "Custom Tab";
RevitUIFactory.AddRibbonTab(application, tabName);
RibbonPanel detailingPanel = RevitUIFactory.AddRibbonPanel
(application, tabName, "Custom Panel", true);
RevitUIFactory.AddRibbonButton
(PluginName, tabName, detailingPanel,
typeof(MainCommand),
Properties.Resources.Icon_32x32,
Properties.Resources.Icon_16x16,
"Custom command discription.");
RevitTask.Initialize(application);
return Result.Succeeded;
}
}
public class CommandAvailability : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
if (applicationData.ActiveUIDocument != null && applicationData.ActiveUIDocument.Document != null &&
!applicationData.ActiveUIDocument.Document.IsFamilyDocument)
return true;
return false;
}
}
}
Command in UI of Addin 1
using Revit.Async;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace RevitTaskAddin1
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
txtBlockGuidAddin.Text = Application.ActiveAddInId.GetGUID().ToString();
await RevitTask.RunAsync(
app =>
{
string guid = app.ActiveAddInId.GetGUID().ToString();
txtBlockRevitAsync.Text = guid;
});
}
}
}
Addin 2 Application :
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Revit.Async;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RevitTaskAddin2
{
public class Application : IExternalApplication
{
public static AddInId ActiveAddInId { get; private set; }
public static string PluginName { get; set; }
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}
public Result OnStartup(UIControlledApplication application)
{
ActiveAddInId = application.ActiveAddInId;
PluginName = application.ActiveAddInId.GetAddInName();
var tabName = "Custom Tab";
RevitUIFactory.AddRibbonTab(application, tabName);
RibbonPanel detailingPanel = RevitUIFactory.AddRibbonPanel
(application, tabName, "Custom Panel", true);
RevitUIFactory.AddRibbonButton
(PluginName, tabName, detailingPanel,
typeof(MainCommand),
Properties.Resources.Icon_32x32,
Properties.Resources.Icon_16x16,
"Custom command discription.");
RevitTask.Initialize(application);
return Result.Succeeded;
}
}
public class CommandAvailability : IExternalCommandAvailability
{
public bool IsCommandAvailable(UIApplication applicationData, CategorySet selectedCategories)
{
if (applicationData.ActiveUIDocument != null && applicationData.ActiveUIDocument.Document != null &&
!applicationData.ActiveUIDocument.Document.IsFamilyDocument)
return true;
return false;
}
}
}
Command in UI of Addin 2
using Revit.Async;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RevitTaskAddin2
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
txtBlockGuidAddin.Text = Application.ActiveAddInId.GetGUID().ToString();
await RevitTask.RunAsync(
app =>
{
string guid = app.ActiveAddInId.GetGUID().ToString();
txtBlockRevitAsync.Text = guid;
});
}
}
}
The Revit task Guid is identical between the two addins.
You can initialise the UIApplication in RevitTask class. It would be nice to get the uiApp from that static class because I would not have to pass for all windows and commands commandData. I pass for now UIControlledApplication in OnStartUp.
I have found that if I call RevitTask.RunAsync
more than once before releasing control of the current thread (through async/await or ending the method), only the first task is ever run. Subsequent calls to RunAsync
appear to work through the queue, calling the next method, but always behind. Most of the time this isn't an issue as I await calls to RunAsync. Occasionally, though, I have wanted to set several tasks to run but don't care about when they finish. It's not terribly hard to work around, but because it's not typically an issue with other asynchronous methods, it took me a while to find the issue. Is this intended, unavoidable, or fixable?
In summary, this works:
await RevitTask.RunAsync([something 1]);
await RevitTask.RunAsync([something 2]);
but this doesn't
// These are run from, say, a button press. Only [something 1] is executed.
RevitTask.RunAsync([something 1]);
RevitTask.RunAsync([something 2]);
// After the button press method is finished, this is run by another button.
await RevitTask.RunAsync([something 3]); // This causes [something 2] to run
Hello
I'm trying to add a 1sec delay between each instance insertion but I'm having a problem, my transaction is rollback.
public ICommand InsertCommand
{
get
{
return _insertCommand ?? (_insertCommand = new RelayCommand(
async (obj) =>
{
await RevitTask.RunAsync(
async app =>
{
try
{
var document = app.ActiveUIDocument.Document;
using (Transaction transaction = new Transaction(document))
{
transaction.Start("Insert");
int count = 20;
for (int i = 0; i < count; i++)
{
string family_2d = "Family";
string symbol_2d = "Symbol";
var symbol = document.FindSymbol(family_2d, symbol_2d);
FamilyInstance instance = document.Create.NewFamilyInstance(new XYZ(0, 0, 0), symbol, StructuralType.NonStructural);
await Task.Delay(1000);
Informations = $"Insert {i + 1}/{count}";
}
transaction.Commit();
}
Informations = "Finished";
}
catch (Exception ex)
{
}
});
},
(obj) =>
{
return obj as Window != null;
}));
}
}
When I use RevitTast.RunAsync(), it seems to work most of the time. However, I occasionally still get the InvalidOperationException sometimes for running a method outside of the API context. It doesn't make any sense to me that it would only fail sometimes. Maybe its because I am trying to run it consecutively, and the thread is already busy. Maybe its because I have multiple transactions within the same RevitTask.RunAsync. Maybe its because it doesn't like running static methods. I'm not sure what the issue could be.
private async void Replace()
{
await RevitTask.RunAsync(app =>
{
Document doc = app.ActiveUIDocument.Document;
foreach (ElementId id in SelectedFam.ElementIds)
{
Element ele = doc.GetElement(id);
if (ele == null)
throw new NullReferenceException();
HostObject host;
XYZ placementLocation;
Level level;
FamilyInstance fam = ele as FamilyInstance;
if (fam != null)
{
host = fam.Host as HostObject;
level = doc.GetElement(fam.LevelId) as Level;
placementLocation = ((LocationPoint)fam.Location).Point;
ElementId typeId = ele.GetTypeId();
ElementType type = doc.GetElement(typeId) as ElementType;
if (type == null)
throw new NullReferenceException("Can not find the element type");
using (Transaction tx = new Transaction(doc, "Delete Family"))
{
tx.Start("Delete Family");
doc.Delete(id);
tx.Commit();
tx.Dispose();
}
try
{
Methods.PlaceEntityInKnownLocation(doc, app, SelectedReplacement, host, placementLocation, level);
}
catch (Exception ex)
{
SimpleLog.Log(ex);
}
});
public static async void PlaceEntityInKnownLocation(Document doc, UIApplication uiApp, EntityModels.Material entity, Element host, XYZ placementLocation, Level level)
{
//Get application and document objects
Autodesk.Revit.ApplicationServices.Application app = uiApp.Application;
//IT DOESN'T ALREADY EXIST,
//CHECK FILE TO LOAD IT FROM
string dir = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
string localSaveLocation = dir + $"\\{entity.Name}.rfa";
if (!File.Exists(localSaveLocation))
{
//DownloadWindow
await HttpClientInstance.DownloadFamilyAsync(entity);
}
//SEE IF THE ELEMENT ALREADY EXISTS IN THE PROJECT
FilteredElementCollector a
= new FilteredElementCollector(doc)
.OfClass(typeof(Family));
Family family = a.FirstOrDefault<Element>(
e => e.Name.Equals(entity.Name))
as Family;
FamilySymbol symbol = null;
// LOAD THE FAMILY INTO THE PROJECT
using (Transaction tx = new Transaction(doc, "Load Family"))
{
tx.Start("Load Family");
if (family == null)
{
bool res = doc.LoadFamily(localSaveLocation, out family);
}
if (family != null)
{
//DETERMINE THE FAMILY SYMBOL
ISet<ElementId> symbolIds = family.GetFamilySymbolIds();
foreach (ElementId symbolId in symbolIds)
{
symbol = doc.GetElement(symbolId) as FamilySymbol;
if(symbol != null)
{
if (!symbol.IsActive)
symbol.Activate();
break;
}
}
}
tx.Commit();
}
if (symbol != null)
{
using (Transaction tx = new Transaction(doc, "Place Family"))
{
tx.Start("Place Family");
try
{
doc.Create.NewFamilyInstance(placementLocation, symbol, host, level, Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException ex)
{
SimpleLog.Log(ex);
}
tx.Commit();
}
}
return;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.