The day before yesterday, I presented the contents of Steven Campbell's key family concepts and parametric family classes from the Revit API DevCamp in Moscow, and an overview of the related family API topics representing my humble contribution to the latter.
If you read that post immediately after it was first published, I suggest you go back and have a quick look again now, since I revisited it and added a significant chunk of information after the initial publication.
Now I would like to delve deeper into the detailed API aspects.
Before proceeding with that, let me mention two other good bits of news:
- Trillions – thriving in the emerging information technology
- Visual Studio 2013 supports 64 bit edit and continue
Trillions – Thriving in the Emerging Information Technology
I just finished reading Trillions – Thriving in the Emerging Information Technology by Peter Lucas, Joe Ballay, Mickey McManus of MAYA Design.
This is the first pure technology related book I have read all the way through in ages, and I am very enthused. It was distributed to all participants of the internal Autodesk Tech Summit in Boston and discusses design and technology for pervasive computing.
It provides numerous shockingly obvious and often quite old insights, a very critical view of our current use of Internet and so-called cloud technologies, and an inspiring and thrilling read.
The topic is how to harness and live in – as opposed to 'with' – the unbounded complexity of the trillion-node network of connected devices that we are inevitable heading towards, and will lead to catastrophe unless we radically change some fundamental aspects of the ways we handle and share information.
I highly recommend reading this book.
I highly recommend abstaining from all use of the word 'cloud computing' until you have completed it :-)
Visual Studio 2013 Supports 64 Bit Edit and Continue
Visual Studio 2013 and the CLR 4.5.1 now support 64-bit Edit and Continue when debugging C# and VB applications. Stephen Preston is happy about that.
You can download the preview version of Visual Studio 2013 to test it right away.
64-bit Edit and Continue for C++ is not yet supported. Please vote for 64-bit Edit and Continue for C++ to be implemented as well.
You have three votes, and you can apply them all to this one wish, if you like.
Family API Add-in
Getting back to the Revit API and programmatic work with families in particular:
As I pointed out, Steve's presentation covers key family editor concepts from a user point of view. From a programming point of view, there are two main family related areas to consider:
- Creation of a family, i.e. working in the context of family document.
- Use of a family, mostly in the context of project document.
Since a family instance can also be inserted into a family document to create a nested family structure, the second point is actually relevant both in the family and project context. It includes tasks such as loading a family, placing instances, manipulating types and existing instances.
Both of these aspects represent large important topics that have been frequently discussed here in the past.
Before Revit 2010, only the second area was covered by the API. Programmatic creation of families and the entire use of the API in the family context was impossible before the introduction of the Family API.
The basic steps to programmatically create a family from scratch are demonstrated in detail in section 3, Revit Family API, of the ADN Revit API training material.
The programmatic creation and insertion of an extrusion family provides a more recent and simplified example of defining and loading a family and placing an instance without exploring the more complex and interesting aspects of the definition such as setting alignments and adding types.
The family API topics concluding the preceding post list the programming concepts that Steve and I decided to revisit:
- Load family and place instances
- Pick interaction and modify instances
- Instance and symbol retrieval, nested type modification
It also provides a full snapshot of the source code and Visual Studio solution implementing the three external commands implementing these concepts and an external application wrapper defining a nice compact user interface to access and launch them:
- CmdTableLoadPlace
- CmdTableNewTypeModify
- CmdKitchenUpdate
Here is the list of topics to explain their implementation and functionality in full detail:
- Scenario 1 – load family and place instances
- Checking whether a family is loaded
- Find a database element by type and name
- Loading a family
- Placing family instances
- Accessing the newly placed instances
- Complete source code of CmdTableLoadPlace
- Scenario 2 – select and modify instances
- Creating a new family type
- Selecting instances with pre- and post-selection
- Modifying a family instance symbol
- API scenario 3 – instance and symbol retrieval, nested type modification
- Retrieve specific family symbols
- Retrieve specific family instances
- Display available door panel types
- Modify a nested family type
- External application
- Conclusion and download
I'll discuss scenario 1 right here and now, and return to the rest in the next few days.
Scenario 1 – Load Family and Place Instances
The external command CmdTableLoadPlace demonstrates loading a family and placing family instances.
The loading requires a prior check whether the family is already present in the project.
Placing the instances can be done either completely automatically or by prompting the user for their locations.
The former makes use of one of the many overloads of the NewFamilyInstance method (how to test all NewFamilyInstance overloads) and is not discussed here.
The latter can be implemented using PromptForFamilyInstancePlacement, in which case the retrieval of the newly placed instances requires an interesting additional little twist.
Checking Whether a Family is Loaded
Before we can start placing any family instances, we need to load our family definition.
Loading it into the project will cause an error if it is already present beforehand, so we need to check for its prior existence before attempting.
All retrieval of database elements is performed using filtered element collectors. Effective element filtering requires application of as many filters as possible to limit the number of elements retrieved.
Of these, the quick filters are highly preferred, since they enable filtering of elements without loading the entire element information into memory. Slow filters are also effective, since they enable checking of element properties inside the Revit memory space before the element information is marshalled and transferred out into the .NET universe. The slowest filtering is achieved by post-processing the element data in .NET after extracting it from Revit.
Assuming that we do not have a terribly large number of families loaded in the project, we can get by in this case by looking at all the families and applying a .NET post-processing step filtering for the desired family name, e.g. using a language integrated LINQ query and the generic FirstOrDefault method like this:
FilteredElementCollector a = new FilteredElementCollector( doc ) .OfClass( typeof( Family ) ); Family family = a.FirstOrDefault<Element>( e => e.Name.Equals( FamilyName ) ) as Family;
Find a Database Element by Type and Name
We check whether the family is already loaded by retrieving all element by filtering of a specific class, also known as .NET type, in this case the Family class, and then post-processing the results looking for a given target name.
This functionality is useful for many different kinds of searches, which led to us already discussing a helper function implementing it, as well as possible optimisations to it.
In this add-in, for the same of simplicity, I implement it like this without any further attempts at optimisation:
/// <summary> /// Retrieve a database element /// of the given type and name. /// </summary> public static Element FindElementByName( Document doc, Type targetType, string targetName ) { return new FilteredElementCollector( doc ) .OfClass( targetType ) .FirstOrDefault<Element>( e => e.Name.Equals( targetName ) ); }
This add-in makes use of this helper method for three different purposes:
- Retrieve a specific family to check whether it has been loaded into the project
- Retrieve a specific family symbol to check whether it has been defined in the project
- Retrieve a specific material to apply it to a family symbol
Loading a Family
Once we have determined that the required family is not yet present in the project, loading it is a trivial task:
if( null == family ) { // It is not present, so check for // the file to load it from: if( !File.Exists( FamilyPath ) ) { Util.ErrorMsg( string.Format( "Please ensure that the sample table " + "family file '{0}' exists in '{1}'.", FamilyName, _family_folder ) ); return Result.Failed; } // Load family from file: using( Transaction tx = new Transaction( doc ) ) { tx.Start( "Load Family" ); doc.LoadFamily( FamilyPath, out family ); tx.Commit(); } }
I added a check to ensure that the family definition file is available in the expected location.
Loading the family modifies the database, so a transaction is required.
If you only need a few symbols (also known as types) from a large family, you can load them more effectively one by one by using LoadFamilySymbol instead of loading them all at once through the single call to LoadFamily.
Here is a detailed discussion and implementation of loading only selected family types.
Placing Family Instances
As said, we decided to manually place the table instances in this particular sample application, which is easily implemented using PromptForFamilyInstancePlacement.
The only input required is the symbol to be placed:
// Determine the family symbol FamilySymbol symbol = null; foreach( FamilySymbol s in family.Symbols ) { symbol = s; // Our family only contains one // symbol, so pick it and leave break; } // Place the family symbol: // PromptForFamilyInstancePlacement cannot // be called inside transaction. uidoc.PromptForFamilyInstancePlacement( symbol );
Note that this method call requires that no transactions be open, presumably because Revit prefers taking care of that internally instead.
Accessing the Newly Placed Instances
As already noted, the retrieval of the instances placed by the call to PromptForFamilyInstancePlacement requires an interesting additional little twist.
Theoretically, this method could return a list of the instance element ids. Lacking that, we can find out for ourselves by temporarily registering to the DocumentChanged event before the call, unregistering immediately afterwards, and making a note of all element ids added in between.
Here is the OnDocumentChanged event handler and the element id container I use for that:
/// <summary> /// Collection of newly added family instances /// </summary> List<ElementId> _added_element_ids = new List<ElementId>(); void OnDocumentChanged( object sender, DocumentChangedEventArgs e ) { _added_element_ids.AddRange( e.GetAddedElementIds() ); }
Here is the full source code showing the event registration details to activate this.
Complete Source Code of CmdTableLoadPlace
For completeness sake, here is the full source code of the CmdTableLoadPlace external command showing the steps described above in their complete glorious, elegant and cooperative context:
/// <summary> /// Load table family if not already present and /// place table family instances. /// </summary> [Transaction( TransactionMode.Manual )] public class CmdTableLoadPlace : IExternalCommand { /// <summary> /// Family name. /// </summary> public const string FamilyName = "family_api_table"; /// <summary> /// Family file path. /// Normally, you would either search the library /// paths provided by Application.GetLibraryPaths /// method. In this case, we store the sample /// family in the same location as the add-in. /// </summary> //const string _family_folder = "Z:/a/rvt"; static string _family_folder = Path.GetDirectoryName( typeof( CmdTableLoadPlace ) .Assembly.Location ); /// <summary> /// Family filename extension RFA. /// </summary> const string _family_ext = "rfa"; /// <summary> /// Family file path /// </summary> static string _family_path = null; /// <summary> /// Return complete family file path /// </summary> static string FamilyPath { get { if( null == _family_path ) { _family_path = Path.Combine( _family_folder, FamilyName ); _family_path = Path.ChangeExtension( _family_path, _family_ext ); } return _family_path; } } /// <summary> /// Collection of newly added family instances /// </summary> List<ElementId> _added_element_ids = new List<ElementId>(); /// <summary> /// External command mainline /// </summary> public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; // Retrieve the family if it is already present: Family family = Util.FindElementByName( doc, typeof( Family ), FamilyName ) as Family; if( null == family ) { // It is not present, so check for // the file to load it from: if( !File.Exists( FamilyPath ) ) { Util.ErrorMsg( string.Format( "Please ensure that the sample table " + "family file '{0}' exists in '{1}'.", FamilyName, _family_folder ) ); return Result.Failed; } // Load family from file: using( Transaction tx = new Transaction( doc ) ) { tx.Start( "Load Family" ); doc.LoadFamily( FamilyPath, out family ); tx.Commit(); } } // Determine the family symbol FamilySymbol symbol = null; foreach( FamilySymbol s in family.Symbols ) { symbol = s; // Our family only contains one // symbol, so pick it and leave break; } // Place the family symbol: // Subscribe to document changed event to // retrieve family instance elements added by the // PromptForFamilyInstancePlacement operation: app.DocumentChanged += new EventHandler<DocumentChangedEventArgs>( OnDocumentChanged ); _added_element_ids.Clear(); // PromptForFamilyInstancePlacement cannot // be called inside transaction. uidoc.PromptForFamilyInstancePlacement( symbol ); app.DocumentChanged -= new EventHandler<DocumentChangedEventArgs>( OnDocumentChanged ); // Access the newly placed family instances: int n = _added_element_ids.Count(); string msg = string.Format( "Placed {0} {1} family instance{2}{3}", n, family.Name, Util.PluralSuffix( n ), Util.DotOrColon( n ) ); string ids = string.Join( ", ", _added_element_ids.Select<ElementId, string>( id => id.IntegerValue.ToString() ) ); Util.InfoMsg2( msg, ids ); return Result.Succeeded; } void OnDocumentChanged( object sender, DocumentChangedEventArgs e ) { _added_element_ids.AddRange( e.GetAddedElementIds() ); } }
As said, the detailed description of the other two commands will follow soon, and the complete Visual Studio solution is available from the overview of the family API topics concluding the preceding post.
I wish you a wonderful weekend.
Jeremy,
I have run into an issue a few times while attempting to PromptForFamilyInstancePlacement, when there are multiple types (symbols) for a family, occasionally a user attempts to select a different symbol.
Seems like the easiest way to solve this is to create a dialog with a list of symbols for that specific family (for easy selection). It just seems like there should be an easier way to load a different type "on the fly" if a user decides to select something different after the fact. Am I expecting too much? Thanks.
Posted by: Nick Kovach | June 29, 2013 at 22:48
Dear Sir,
I am placing family instance using below method:
FamilyInstance fi = Document.Create.NewFamilyInstance(pCurve, symbol,pipe.ReferenceLevel, StructuralType.NonStructural);
but fi is getting null & it is not placing in Document.
please Help
Regards
Dhiraj.
Posted by: Dhiraj Patil | August 13, 2013 at 07:35
Dear Sir,
I am placing family instance using below method:
FamilyInstance fi = Document.Create.NewFamilyInstance(pCurve, symbol,pipe.ReferenceLevel, StructuralType.NonStructural);
but fi is getting null & it is not placing in Document.
please Help
Regards
Dhiraj.
Posted by: Dhiraj patil | September 25, 2013 at 06:57
Dear Dhiraj,
Can you place the symbol manually?
You need to find out which NewFamilyInstance overload is suitable for your specific family:
http://thebuildingcoder.typepad.com/blog/2011/01/newfamilyinstance-overloads.html
http://thebuildingcoder.typepad.com/blog/2011/06/placing-a-line-based-detail-item-instance.html
http://thebuildingcoder.typepad.com/blog/2012/02/hosting-a-light-fitting-on-a-reference-plane.html
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 26, 2013 at 06:44
Dear Nick,
Yes, sorry, currently there is no way that I know of to programmatically control default type that gets placed by this method. We are working on it, though :-)
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 29, 2013 at 11:55
Dear Sir,
Yes i can place instance manually.
My goal is to break pipe as soon as union get placed on it.
I have tried placing on XYZs but it is not breaking pipe.
Regards
Dhiraj.
Posted by: Dhiraj Patil | October 03, 2013 at 03:33
Dear Dhiraj,
You can try going the other way:
Instead of starting by placing the pipe, and then adding fittings, you could place the fittings at the correct locations without any pipes at all.
Then when you call the API methods to connect the fittings with each other, the intermediate pipes may be placed automatically.
Cheers, Jeremy.
Posted by: Jeremy Tammik | October 08, 2013 at 11:22
Hi Jeremy,
I wish to place the instance of a family object once i click a button. I need some replacement for the method "PromptForFamilyInstancePlacement" . It should not prompt user in UI to place the instance on a phase. It should prompt the user to select the 2 or more multpile phase and then it should place it in the joining point of the selected phases. Is this possible in API programming?
Posted by: vinothkumar | October 11, 2013 at 01:53
Hi Jeremy
Another interesting and very useful article.
I have utilised some of your code above to create an icon/image driven system where families are presented in a dialogue. I have everything working almost exactly as I need it with the exception of one thing. When I select an image in my dialogue my dialogue form has focus. I then need to click in my Revit model twice (once for it to regain focus, again to place the family) Is there any way I can programmatically give focus back to the Revit Model/document.
Revit API newcomer
Paul M
Posted by: Paul M | December 12, 2013 at 17:42
ear Paul,
Thank you for your update and appreciation.
I am very glad you find it useful.
For one thing, your form should set Revit as its parent window.
To see how, search for JtWindowHandle
http://lmgtfy.com/?q=JtWindowHandle
Here is a current implementation of this trivial wrapper class:
https://github.com/jeremytammik/RoomEditorApp/blob/master/RoomEditorApp/JtWindowHandle.cs
As you can see, it is used in my RoomEditorApp.
With that in place, I think your problem should disappear immediately.
If not, the RoomEditorApp also includes code to explicitly set the foreground window.
Try not to use it, though, since that is something the user should retain control over.
Cheers, Jeremy.
Posted by: Jeremy Tammik | December 20, 2013 at 10:13
Jeremy,
I want to load some mass instances from one file(the store file) to another file(the proj. file) in revit arch. 2013. I know there is a new method copy and paste between open documents in the same session however i would have to reference the 2014 api, which I cant at the moment. I think that I would have to do this by loading mass families (12 of them) onto the same x,y coordinates simultaneously. Is this even possible? Could you provide tips to achieve this?
Many thanx,
Nik
Posted by: Nik Mulconray | February 15, 2014 at 20:12
Dear Nik,
Yes indeed, the copy and paste API sounds appropriate and was introduced in Revit 2014.
The other approach you suggest sounds perfectly feasible as well for this simple case.
Yes, I believe this is possible.
As usual, you should try it out through the user interface first.
Placing multiple overlapping instances might cause errors or warnings, and you may have to use the failure API to handle those.
Cheers, Jeremy.
Posted by: Jeremy Tammik | February 18, 2014 at 06:48
Thanx, Jeremy I will give it a shot.
Posted by: Nik Mulconray | February 19, 2014 at 03:30
Hi Jeremy,
Do you know of a method of obtaining the families file path without using editFamily method? this requires a transaction I believe and I have to work in a read only mode transaction within my external command.
cheers,
fabs
Posted by: frank halliday | November 08, 2014 at 03:24
Hi Jeremy
Thanks for the code.One thing only where can I find the Util class. I got the error that Util is not in the current context. I am a biginner. :-)
thanks
Posted by: Frank | December 24, 2014 at 06:25
Dear Frank,
Thank you for your appreciation.
Look in the archive file FamilyApi10.zip in the third and final part of the detailed discussion on use of the Family API:
http://thebuildingcoder.typepad.com/blog/2013/07/family-api-nested-type-instance-and-symbol-retrieval.html
Good luck getting started with the Revit API!
Have you worked through the getting started material, especially the 'My First Plugin' and DevTV tutorials?
http://thebuildingcoder.typepad.com/blog/about-the-author.html#2
Have you installed RevitLookup? An absolute must!
Merry Christmas and a Happy New Year to you!
Cheers, Jeremy.
Posted by: Jeremy Tammik | December 24, 2014 at 07:16
Hi Jeremy,
I tried to place a Generic Model to a column face :
NewFamilyInstance(Reference reference, XYZ location, XYZ referenceDirection, FamilySymbol symbol);
the face normal is (0,1,0)
for each location XYZ (x,y,z) I putted, the location of the new familyinstance is (x, 0.34, z) !!
why y is not equal to my input value?
please Help ..
Posted by: Samer Habib | April 05, 2015 at 09:16