As you probably know, the Revit API provides two methods to load family types, also known as symbols: LoadFamilySymbol, which loads an individual type, and LoadFamily, which loads the entire family and all the types it defines. Use of the latter method can be quite time consuming and wasteful, especially if you only require a few of the types included.
In the past we also discussed a couple of other issues on handling family types, such as creating a new family type using the Duplicate method and unloading an unused type.
Erez van Leeuwen of Nordined voiced the interesting request of how to avoid the overhead of calling LoadFamily and loading all its types by presenting a list of the types to the user and asking her which ones are actually required, to which Saikat Bhattacharya came up with the following solution:
Question: Revit provides multiple ways of loading a family. The two 'Load Families' options displayed in the Revit ribbon are
- Home > Component > Load Family
- Insert > Load Family
The first one loads a family and places an instance of it, and the second one only loads the family. If the chosen family has a TXT file associated with it containing different types, Revit shows a popup window where you can select the types you want to load.
Using the API to load a family, I can choose between the LoadFamily and LoadFamilySymbol methods. Is there any other function I can use to load a family and have Revit pop up the dialog to choose which types I want loaded?
If this does not exist, can you tell me if there is a way to read which types exist within a family?
Answer: There Revit API does not provide any method which will pop up a dialogue listing the types contained in a family.
You could load the entire family using the LoadFamily method and then use the Family.Symbols method to list all the types defined in it. The Revit API help file RevitAPI.chm does in fact include a code snippet showing how to iterate through all the symbols in a loaded family:
public void GetInfoForSymbols( Family family ) { StringBuilder message = new StringBuilder( "Selected element's family name is : " + family.Name ); if( family.Symbols.IsEmpty ) { message.AppendLine( "Contains no family symbols." ); } else { message.AppendLine( "The family symbols contained" + " in this family are : " ); // Get family symbols contained in this family foreach( FamilySymbol sym in family.Symbols ) { // Get family symbol name message.AppendLine( "\nName: " + sym.Name ); foreach( Material material in sym.Materials ) { message.AppendLine( "\tMaterial: " + material.Name ); } } } TaskDialog.Show( "Revit", message.ToString() ); }
The problem is, of course, that this loads the entire family and all its symbols up front. Doing this for all the families needed will cause the Revit project to grow and become bigger very fast.
Access to and iteration over all the family symbols without permanently loading the entire family into the project file can be achieved using the power of transactions in the Revit API.
You can load a family within a transaction, read through its symbols, and then roll back the transaction to undo the loading of the family. During this process, the names of the symbols contained in the loaded family can be extracted and saved. Subsequently, a new transaction can be used to call the LoadFamilySymbol method to load a specific symbol based on the names retrieved in the previous step. The following code snippet illustrates this approach and is quite simple and self-explanatory:
string filename = @"C:\Documents and Settings" + "\All Users\Application Data\Autodesk" + "\RAC 2011\Metric Library\Columns" + "\M_Round Column.rfa"; UIApplication app = commandData.Application; Document doc = app.ActiveUIDocument.Document; Transaction trans = new Transaction( doc, "FakeLoading" ); trans.Start(); Family family = null; string symbName = String.Empty; int counter = 0; if( doc.LoadFamily( filename, out family ) ) { foreach( FamilySymbol symb in family.Symbols ) { TaskDialog.Show( "Symbol names", symb.Name ); if( counter == 0 ) { symbName = symb.Name; } counter++; } } trans.RollBack(); Transaction transNew = new Transaction( doc, "RealLoading" ); transNew.Start(); if( doc.LoadFamilySymbol( filename, symbName ) ) { TaskDialog.Show( "Status", "We managed to load only one desired symbol!" ); } transNew.Commit();
Question: Of course, loading the whole family, rolling everything back and reloading the required types might considerably slow down the complete loading operation.
If the whole family is already being loaded, it might be faster to only unload or delete the unwanted types.
Is there a way to unload specific types? Within Revit I can just delete the unwanted types by pressing delete or I can purge them out. Can I do this via the API?
Answer: As mentioned above, to unload specific family types, you can simply delete them from the Revit document object.
If you want to unload more than one type, you can collect all the types that you wish to unload in an ElementSet or create an ICollection of their element ids and use one of the following overloads of the Document.Delete method:
- Delete(ElementSet)
- Delete(ICollection(Of ElementId))
While editing this, another thought crossed my mind:
The approach above is based on reading the family into the project in order to determine what types it contains. There may be another possible approach, of course, not loading the family into the project but opening the family file as a separate background document via the API and analysing that using its family manager object instead. You can iterate the types that way as well. I don't know whether there is also any need to handle a possibly associated TXT file, and how that would affect the list of types returned by the family manager, but it might be worth a try.
Dear Sir,
How can i get family information (symbols, instances) without loading family?
Thanks & Regards,
Nitin J.
Posted by: Account Deleted | June 01, 2011 at 03:29
Dear Nitin,
That is explained above, in the last paragraph: open the family file as a background document and use element filtering as normal.
Cheers, Jeremy.
Posted by: Jeremy Tammik | June 01, 2011 at 04:44
Dear Sir,
Its working perfectly but when the family already present in revit its showing the dialog to overwrite family.
Do u have an idea how to skip or by default select overwrite programmatically?
Thanks Again!!!
- Nitin J.
Posted by: Account Deleted | June 01, 2011 at 08:13
Dear Nitin,
There are lots of posts here on The Building Coder explaining how to handle or suppress dialogues, including automatically selecting a specific option and then dismissing the dialogue.
Good luck!
Cheers, Jeremy.
Posted by: Jeremy Tammik | June 01, 2011 at 08:30
Hello Jeremy,
I would like to delete family types with opening the family file as a separate background document. Is there any way to get ICollection of Familytype? I would like to collect elementid to use document.delete(ICollection);
When I try it using ElementClassFilter, it gives me an error.
ElementClassFilter filter=new ElementClassFilter(typeof(FamilyType));
Please let me know if you have any ideas.
Thanks,
Posted by: Jinsol Kim | September 15, 2011 at 17:25
Dear Jinsol Kim,
Yes, I would say that filtering for FamilyType objects will only work in the project environment. Within the family context, the family types are not represented using individually accessible elements at all. Instead, they are more like entries in a table managed by the FamilyManager class. The family manager class manages the family types and parameters in a family document. One of its member methods is DeleteCurrentType, which removes the current family type.
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 21, 2011 at 03:08
Hi Jeremy,
I am trying to create a command button to bring the family instead dragging from the family tree.
If the family not in the document load it or run it.
But the below code load it all the time.
If the family is not in the document use doc.LoadFamilySymbol to load it and how can i come back to loadFamily
Not sure what is wrong. help please
public void activeFamily(Document doc, UIDocument uiDoc, string familyName, string fileName)
{
FilteredElementCollector familyCollector = new FilteredElementCollector(doc);
familyCollector.OfClass(typeof(FamilySymbol));
FamilySymbol familySymbolToFind = null;
foreach (FamilySymbol familySymbol in familyCollector)
{
if (familySymbol.Name == familyName)
{
familySymbolToFind = familySymbol;
uiDoc.PromptForFamilyInstancePlacement(familySymbolToFind);
}
else
{
loadFamily(doc, fileName, familyName);
}
}
}
private void loadFamily(Document doc, string fileName, string familyName)
{
Transaction transNew = new Transaction(doc, "RealLoading");
transNew.Start();
if (doc.LoadFamilySymbol(fileName, familyName))
transNew.Commit();
}
Posted by: Babu | June 09, 2012 at 23:33