In this section, we change the type of a standard family instance element. We make use of a windows form that displays all available families and types for the category of the single selected instance. Since the lab involves some user interface elements, it may take too long for a hands-on lab. If so, compile and debug the solved lab and discuss the code and its comments with the instructor and your peers. If you are confident with windows forms development, you may try to design similar code in your own project as well. The relevant code in the command does three things:
Before proceeding with the selected instance change, we need to ensure that only a single element is selected. We also need to ensure that the element is of FamilyInstance type.
public class Lab3_4_ChangeSelectedInstanceType : IExternalCommand
{
public IExternalCommand.Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
Application app = commandData.Application;
Document doc = app.ActiveDocument;
//
// Make sure we have a single FamilyInstance selected:
//
ElementSet ss = doc.Selection.Elements;
// First make sure we have a single FamilyInstance selected
if( 1 != ss.Size )
{
LabUtils.ErrorMsg( "Please pre-select a single family instance element." );
return IExternalCommand.Result.Cancelled;
}
ElementSetIterator itTmp = ss.ForwardIterator();
itTmp.MoveNext();
Element elTmp = itTmp.Current as Element;
if( !(elTmp is FamilyInstance) )
{
LabUtils.ErrorMsg( "Selected element is NOT a standard family instance." );
return IExternalCommand.Result.Cancelled;
}
FamilyInstance inst = elTmp as FamilyInstance;
Category instCat = inst.Category;
//
// todo:
//
// Collect all types applicable to this category and sort them into
// a dictionary mapping the family name to a list of its types.
//
// todo:
//
// Display the form, allowing the user to select a family
// and a type, and assign this type to the instance.
//
return IExternalCommand.Result.Succeeded;
}
}
' Form-utility to change Type for a selected standard Instance
Public Class Lab3_4_ChangeSelectedInstanceType
Implements IExternalCommand
Public Function Execute(ByVal commandData As Autodesk.Revit.ExternalCommandData, ByRef message As String, ByVal elements As Autodesk.Revit.ElementSet) As Autodesk.Revit.IExternalCommand.Result Implements Autodesk.Revit.IExternalCommand.Execute
Dim app As Revit.Application = commandData.Application
Dim doc As Revit.Document = app.ActiveDocument
'First make sure we have a single FamilyInstance selected
'--------------------------------------------------------
Dim inst As FamilyInstance
Dim instCat As Category
Dim ss As ElementSet = doc.Selection.Elements
' First make sure we have a single FamilyInstance selected
If Not ss.Size = 1 Then
MsgBox("You must pre-select a single element!")
Return IExternalCommand.Result.Cancelled
Else
Dim itTmp As ElementSetIterator = ss.ForwardIterator
itTmp.MoveNext()
Dim elTmp As Revit.Element = itTmp.Current
If Not TypeOf elTmp Is FamilyInstance Then
MsgBox("Selected element is NOT a standard family instance!")
Return IExternalCommand.Result.Cancelled
Else
inst = elTmp
instCat = inst.Category
End If
End If
'
' todo:
'
' Collect all types applicable to this category and sort them into
' a dictionary mapping the family name to a list of its types.
'
'
' todo:
'
' Display the form, allowing the user to select a family
' and a type, and assign this type to the instance.
'
Return IExternalCommand.Result.Succeeded
End Function
End Class
To create the dictionary of relevant families with a list of all the symbols of each, we can use the new filtering mechanism to create a type filter (of Family type). With this set of family elements, we will match the category of the family (or the symbol, as the case might be) with that of the selected family instance element. It is a good practice to check for category match against category id instead of category name (and more so while comparing with built-in category). The following code illustrates the approach described above:
Dictionary<string, List<FamilySymbol>> dictFamilyToSymbols = new Dictionary<string, List<FamilySymbol>>();
{
WaitCursor waitCursor = new WaitCursor();
//
// We create a collection of all loaded families for this category
// and for each one, the list of all loaded types (symbols).
//
// There are many ways how to store the matching objects, but we choose whatever is most suitable for the relevant UI:
// We could use Revit's generic Map class, but it's probably more efficient to use the new 2005 .NET strongly-typed Dictionary with
// KEY = Family name (String)
// VALUE = ArrayList (implements iList so we can elegantly bind it to combobox) of corresponding FamilySymbol obects
//
// Find all the corresponding Families/Types:
List<Element> families = new List<Element>();
Filter filterFamily = app.Create.Filter.NewTypeFilter( typeof( Family ) );
doc.get_Elements( filterFamily, families );
foreach( Family f in families )
{
bool categoryMatches = false;
if( null == f.FamilyCategory )
{
foreach( FamilySymbol sym in f.Symbols )
{
categoryMatches = sym.Category.Id.Equals( instCat.Id );
break;
}
}
else
{
categoryMatches = f.FamilyCategory.Id.Equals( instCat.Id );
}
if( categoryMatches )
{
List<FamilySymbol> familySymbols = new List<FamilySymbol>();
foreach( FamilySymbol sym in f.Symbols )
{
familySymbols.Add( sym );
}
dictFamilyToSymbols.Add( f.Name, familySymbols );
}
}
}
'
' We create a collection of all loaded families for this category
' and for each one, the list of all loaded types (symbols).
'
' There are many ways how to store the matching objects, but we choose whatever is most suitable for the relevant UI:
' We could use Revit's generic Map class, but it's probably more efficient to use the new 2005 .NET strongly-typed Dictionary with
' KEY = Family name (String)
' VALUE = ArrayList (implements iList so we can elegantly bind it to combobox) of corresponding FamilySymbol obects
Dim dictFamilyToSymbols As New Dictionary(Of String, ArrayList)
' Looping may take a few seconds, so let user know by changing the cursor
Dim oldCursor As Cursor = Cursor.Current
Cursor.Current = Cursors.WaitCursor
' using Revit 2009 element filtering
Dim families As List(Of Revit.Element) = New List(Of Revit.Element)
Dim filterFamily As Filter = commandData.Application.Create.Filter.NewTypeFilter(GetType(Family))
Dim nRetVal = doc.Elements(filterFamily, families)
Dim f As Family
Dim categoryMatches = False
For Each f In families
categoryMatches = False
If (f.FamilyCategory Is Nothing) Then
For Each sym As FamilySymbol In f.Symbols
categoryMatches = sym.Category.Id.Equals(instCat.Id)
Exit For
Next
Else
categoryMatches = f.FamilyCategory.Id.Equals(instCat.Id)
End If
If (categoryMatches) Then
Dim familySymbols As New ArrayList
For Each sym As FamilySymbol In f.Symbols
familySymbols.Add(sym)
Next
dictFamilyToSymbols.Add(f.Name, familySymbols)
End If
Next
Displaying the form and assigning the selected type is short and sweet:
//
// Display the form, allowing the user to select a family
// and a type, and assign this type to the instance.
//
Lab3_4_Form frm = new Lab3_4_Form( dictFamilyToSymbols );
if( WinForms.DialogResult.OK == frm.ShowDialog() )
{
try
{
inst.Symbol = frm.cmbType.SelectedItem as FamilySymbol;
LabUtils.InfoMsg( "Successfully changed Family:Type to " + frm.cmbFamily.Text + " : " + frm.cmbType.Text );
}
catch( Exception )
{
}
}
'
' Display the form, allowing the user to select a family
' and a type, and assign this type to the instance.
'
Dim frm As New Lab3_4_Form(dictFamilyToSymbols)
Cursor.Current = oldCursor ' restore cursor
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Try
inst.Symbol = frm.cmbType.SelectedItem
MsgBox("Successfully changed Family:Type to " & frm.cmbFamily.Text & " : " & frm.cmbType.Text)
Catch
End Try
End If
Compile and link the project and update the Revit.ini file accordingly. Debug the code and discuss with the course instructor and your peers.
next previous home copyright © 2007-2009 jeremy tammik, autodesk inc. all rights reserved.