Revit API 2012 provides one more application type, IExternalDBApplication, which is supposed to help do some DB stuffs at Revit startup, monitor some document events, and so on.
The IUpdater interface can help us monitor element additions, deletions, and modifications as were introduced before. Furthermore, it can help us change something or even add something in the Revit model during these events. The SubTransaction has to be used inside the Execute method of the IUpdater implementations as we demonstrated in some early posts.
The ElementTransformUtils utility class provides some userful methods that we can call to copy move and rotate elements as were introduced previously.
In this post, let us combine all these Revit API functionalities together to make a fanastic creature that is available nowhere else, wall twins, meaning when a wall is created its twin will be born at the same time, with a certain offset for us able to to see it and to make it really useful as well.
Here is what the wall twins look like. The left or bottom walls are created by the Revit Wall command and the top or right ones as highlighted are their twincs that are created automatically using the cool code that we are going to talk about in detail.
An IUpdater derivative is needed to create the twin when a wall is being created. Here is the implementation about the IUpdater interface:
public class Updater2 : IUpdater
{
private AddInId mAddinId;
private UpdaterId mUpdaterId;
public Updater2(AddInId id)
{
mAddinId = id;
mUpdaterId = new UpdaterId(mAddinId, new Guid("e2365ea2-f1ee-459f-a1ec-8eee516f9409"));
}
#region IUpdater Members
private bool createdByMe = false;
public void Execute(UpdaterData updaterData)
{
Document doc = updaterData.GetDocument();
if (!createdByMe)
{
ICollection<ElementId> ids = updaterData.GetAddedElementIds();
using (SubTransaction tr = new SubTransaction(doc))
{
tr.Start();
foreach (ElementId id in ids)
{
XYZ translation = new XYZ(1, 1, 0);
ElementTransformUtils.CopyElement(doc, id, translation);
createdByMe = true;
}
tr.Commit();
}
}
else
{
createdByMe = false;
}
}
public string GetAdditionalInformation()
{
return "Updater2";
}
public ChangePriority GetChangePriority()
{
return Autodesk.Revit.DB.ChangePriority.Annotations;
}
public UpdaterId GetUpdaterId()
{
return mUpdaterId;
}
public string GetUpdaterName()
{
return "Updater2";
}
#endregion
}
As can be seen, it is fairly simple and straightforward. Only two points are worth of mentioning a bit, I think. The first is to use SubTransaction instead of Transaction in the Execute() method, otherwise an exception would occur saying a Transaction has alreacy been created.
The second point is that a flag indicating whether the added wall is created by Revit or by us to avoid infinite loop is necessary, otherwise, Revit will complain about it and abort the whole wall creation command. Of course, a better way exists to do so, for example, adding some Entity data to the wall twins indicating what they are. It can be achieved by the Extensible Storage API of the Revit .NET 2012 as was covered extensively by many recent posts.
By the way, the Element Updater wizard can help us implement an IUpdater interface automatically and reliabley in no time.
We need to register and unregister the IUpdater implementation as always in the OnStartup method and the OnShutdown method of an IExternalDBApplication . Here is the code:
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class ExtDbApp1 : IExternalDBApplication
{
#region Cached Variables
public static ControlledApplication _cachedCtrlApp;
#endregion
#region IExternalApplication Members
public ExternalDBApplicationResult OnStartup(ControlledApplication ctrlApp)
{
try
{
_cachedCtrlApp = ctrlApp;
Updater2 updater = new Updater2(_cachedCtrlApp.ActiveAddInId);
UpdaterRegistry.RegisterUpdater(updater);
ElementCategoryFilter catFilter = new ElementCategoryFilter(Autodesk.Revit.DB.BuiltInCategory.OST_Walls);
UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), catFilter, Element.GetChangeTypeElementAddition());
return ExternalDBApplicationResult.Succeeded;
}
catch (Exception ex)
{
MessageBox.Show( ex.ToString() );
return ExternalDBApplicationResult.Failed;
}
}
public ExternalDBApplicationResult OnShutdown(ControlledApplication ctlApp)
{
try
{
UpdaterRegistry.UnregisterUpdater(new Updater2(ctlApp.ActiveAddInId).GetUpdaterId());
return ExternalDBApplicationResult.Succeeded;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return ExternalDBApplicationResult.Failed;
}
}
#endregion
}
The ElementCategoryFilter is used to filter out all walls that we only care about. In case some complex filters are necessary, the Element Finder wizard is right there waiting to help. It supports all available Revit API Filters and can combine them in any good ways we want.
Last but not least, please register the external DB application in a Revit manifest file. Here is a sample:
<AddIn Type="DBApplication">
<Assembly>C:\Temp\RevitAddinCS1\bin\Debug\RevitAddinCS1.dll</Assembly>
<FullClassName>RevitAddinCS1.ExtDbApp1</FullClassName>
<ClientId>5422d57c-7fd6-45e0-a4ba-e00180DB2012</ClientId>
<Name>RevitAddinCS1.ExtDbApp1</Name>
<VendorId>RAW</VendorId>
</AddIn>
Here we go! From this moment on, when any wall is created its twin will be born along the way. Of course, when necessary, please adjust the translation of twin walls to suite some other needs. Enjoy!
Revit Addin Wizard (RevitAddinWizard) provides an External DB Application wizard to help implement the IExternalDBApplication interface automatically and reliably. It also has an Element Updater wizard to help implement the IUpdater interfaces and a Revit Manifest Organizer to help create, maintain and validate Revit manifest files.
Recent Comments