In this section, we define a shared FireRating parameter and attach it to all doors in the model. The current values of the parameter are exported to Excel, where they can be analysed and modified, and the updated values are imported back into the Revit building model. The same functionality is also demonstrated by the SDK FireRating sample.
This Lab consists of 3 separate commands which provide a full utility to:
In this section, we create and bind a new shared FireRating parameter to all doors.
It is likely that you will be adding shared parameters programmatically
in other situations as well, so it makes sense to factor out some common
helper utilities. Add the following shared functions to the
LabUtils
class:
#region Helpers for shared parameters /// <summary> /// Helper to get shared parameters file. /// </summary> public static DefinitionFile GetSharedParamsFile( Application app ) { // Get current shared params file name string sharedParamsFileName; try { sharedParamsFileName = app.Options.SharedParametersFilename; } catch( Exception ) { ErrorMsg( "No shared params file set." ); return null; } if( 0 == sharedParamsFileName.Length ) { string fullPath = LabConstants.gsSharedParamFilePath; StreamWriter stream; stream = new StreamWriter( fullPath ); stream.Close(); app.Options.SharedParametersFilename = fullPath; sharedParamsFileName = app.Options.SharedParametersFilename; } // Get the current file object and return it DefinitionFile sharedParametersFile; try { sharedParametersFile = app.OpenSharedParameterFile(); } catch( Exception ) { ErrorMsg( "Cannnot open shared params file." ); sharedParametersFile = null; } return sharedParametersFile; } /// <summary> /// Helper to get shared params group. /// </summary> public static DefinitionGroup GetOrCreateSharedParamsGroup( DefinitionFile sharedParametersFile, string groupName ) { DefinitionGroup g = sharedParametersFile.Groups.get_Item( groupName ); if( null == g ) { try { g = sharedParametersFile.Groups.Create( groupName ); } catch( Exception ) { g = null; } } return g; } /// <summary> /// Helper to get shared params definition. /// </summary> public static Definition GetOrCreateSharedParamsDefinition( DefinitionGroup defGroup, ParameterType defType, string defName, bool visible ) { Definition definition = defGroup.Definitions.get_Item( defName ); if( null == definition ) { try { definition = defGroup.Definitions.Create( defName, defType, visible ); } catch( Exception ) { definition = null; } } return definition; } #endregion // Helpers for shared parameters
#Region "Helpers for shared parameters" ''' <summary> ''' Helper to get shared parameters file. ''' </summary> Public Shared Function GetSharedParamsFile(ByVal app As Revit.Application) _ As Parameters.DefinitionFile ' Get current shared params file name Dim sharedParamsFileName As String Try sharedParamsFileName = app.Options.SharedParametersFilename Catch MsgBox("No Shared params file set !?") Return Nothing End Try If "" = sharedParamsFileName Then Dim fullPath As String = gsSharedParamFilePath Dim stream As StreamWriter stream = New StreamWriter(fullPath) stream.Close() app.Options.SharedParametersFilename = fullPath sharedParamsFileName = app.Options.SharedParametersFilename End If ' Get the current file object and return it Dim sharedParametersFile As Autodesk.Revit.Parameters.DefinitionFile Try sharedParametersFile = app.OpenSharedParameterFile Catch MsgBox("Cannnot open Shared Params file !?") sharedParametersFile = Nothing End Try Return sharedParametersFile End Function ''' <summary> ''' Helper to get shared params group. ''' </summary> Public Shared Function GetOrCreateSharedParamsGroup( _ ByVal sharedParametersFile As Parameters.DefinitionFile, _ ByVal groupName As String) _ As Parameters.DefinitionGroup Dim msProjectGroup As Autodesk.Revit.Parameters.DefinitionGroup 'Get Shared Parameter group msProjectGroup = sharedParametersFile.Groups.Item(groupName) If (msProjectGroup Is Nothing) Then Try 'create shared paramteter group msProjectGroup = sharedParametersFile.Groups.Create(groupName) Catch msProjectGroup = Nothing End Try End If Return msProjectGroup End Function ''' <summary> ''' Helper to get shared params definition. ''' </summary> Public Shared Function GetOrCreateSharedParamsDefinition( _ ByVal defGroup As Parameters.DefinitionGroup, _ ByVal defType As Parameters.ParameterType, _ ByVal defName As String, _ ByVal visible As Boolean) As Parameters.Definition 'Get parameter definition Dim definition As Parameters.Definition = defGroup.Definitions.Item(defName) If definition Is Nothing Then Try 'create parameter definition definition = defGroup.Definitions.Create(defName, defType, visible) Catch definition = Nothing End Try End If Return definition #End Region
For clarity, we add the group and definition name constants
to the LabConstants
module:
// Lab 4_3 public const string gsSharedParamFilePath = _temp_dir + "SharedParams.txt"; public const string gsSharedParamsGroupAPI = "API Parameters"; public const string gsSharedParamsDefFireRating = "API FireRating";
' Lab 4_3 Public Const gsSharedParamFilePath As String = _temp_dir + "SharedParams.txt" Public Const gsSharedParamsGroupAPI As String = "API Parameters" Public Const gsSharedParamsDefFireRating As String = "API FireRating"
Now we can add a new command class to Labs4
.
All the above helpers are first utilised and then the newly created parameter
is bound to the doors category for the current Revit document:
/// <summary> /// 4.3.1 Create and bind shared parameter. /// </summary> public class Lab4_3_1_CreateAndBindSharedParam : IExternalCommand { // what element type are we interested in? // standard sample uses BuiltInCategory.OST_Doors, we can also test using // BuiltInCategory.OST_Walls to demonstrate that the same technique // works with system families just as well as with standard ones: static public BuiltInCategory Bic = BuiltInCategory.OST_Doors; //static public BuiltInCategory Bic = BuiltInCategory.OST_Walls; public IExternalCommand.Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { Application app = commandData.Application; Document doc = app.ActiveDocument; // Get the current Shared Params Definition File DefinitionFile sharedParamsFile = LabUtils.GetSharedParamsFile( app ); if( null == sharedParamsFile ) { LabUtils.ErrorMsg( "Error getting the shared params file." ); return IExternalCommand.Result.Failed; } // Get or Create the Shared Params Group DefinitionGroup sharedParamsGroup = LabUtils.GetOrCreateSharedParamsGroup( sharedParamsFile, LabConstants.gsSharedParamsGroupAPI ); if( null == sharedParamsGroup ) { LabUtils.ErrorMsg( "Error getting the shared params group." ); return IExternalCommand.Result.Failed; } // Get or Create the Shared Params Definition Definition fireRatingParamDef = LabUtils.GetOrCreateSharedParamsDefinition( sharedParamsGroup, ParameterType.Number, LabConstants.gsSharedParamsDefFireRating, true ); if( null == fireRatingParamDef ) { LabUtils.ErrorMsg( "Error in creating shared parameter." ); return IExternalCommand.Result.Failed; } // create the category set for binding and add the category we are // interested in, doors or walls or whatever: CategorySet catSet = app.Create.NewCategorySet(); Category cat = doc.Settings.Categories.get_Item( Bic ); try { catSet.Insert( cat ); } catch( Exception ) { LabUtils.ErrorMsg( string.Format( "Error adding '{0}' category to parameters binding set.", cat.Name ) ); return IExternalCommand.Result.Failed; } // Bind the Param try { Binding binding = app.Create.NewInstanceBinding( catSet ); // We could check if already bound, but looks like Insert will just ignore it in such case doc.ParameterBindings.Insert( fireRatingParamDef, binding ); } catch( Exception ) { LabUtils.ErrorMsg( "Error binding shared parameter" ); return IExternalCommand.Result.Failed; } return IExternalCommand.Result.Succeeded; } }
''' <summary> ''' 4.3.1 Create and bind shared parameter. ''' </summary> Public Class Lab4_3_1_CreateAndBindSharedParam Implements IExternalCommand Public Shared Bic As BuiltInCategory = BuiltInCategory.OST_Doors 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 ' Get the current Shared Params Definition File Dim sharedParamsFile As DefinitionFile = LabUtils.GetSharedParamsFile(app) If (sharedParamsFile Is Nothing) Then MsgBox("Error in getting the Shared Params File?") Return IExternalCommand.Result.Failed End If ' Get or Create the Shared Params Group Dim sharedParamsGroup As Parameters.DefinitionGroup sharedParamsGroup = LabUtils.GetOrCreateSharedParamsGroup(sharedParamsFile, gsSharedParamsGroupAPI) If (sharedParamsGroup Is Nothing) Then MsgBox("Error in getting the Shared Params Group?") Return IExternalCommand.Result.Failed End If ' Get or Create the Shared Params Definition Dim fireRatingParamDef As Parameters.Definition = LabUtils.GetOrCreateSharedParamsDefinition( _ sharedParamsGroup, ParameterType.Number, gsSharedParamsDefFireRating, True) If (fireRatingParamDef Is Nothing) Then MsgBox("Error in creating 'API Added' parameter?") Return IExternalCommand.Result.Failed End If ' Create the Category Set for binding and add "Doors" Dim catSet As CategorySet = app.Create.NewCategorySet() Try catSet.Insert(app.ActiveDocument.Settings.Categories.Item(Bic)) Catch MsgBox("Error when adding 'Doors' category to parameters binding set?") Return IExternalCommand.Result.Failed End Try ' Bind the Param Try Dim binding As Parameters.Binding = app.Create.NewInstanceBinding(catSet) ' We could check if already bound, but looks like Insert will just ignore it in such case app.ActiveDocument.ParameterBindings.Insert(fireRatingParamDef, binding) Catch MsgBox("Error in binding shared parameter !?") Return IExternalCommand.Result.Failed End Try MsgBox("Parameter binding Successful!") Return IExternalCommand.Result.Succeeded End Function End Class
Compile the code, update Revit.ini and test the command. Examine the outcome.
In this section, we export the door element ids and FireRating parameter values to Excel.
Before adding the command, we implement another reusable utility to get the
gloally unique identifier or GUID for a given shared parameter group and name.
We will need this later for the GUID argument of the
Revit.Element.Parameter()
method.
Add the following method to LabUtils
:
/// <summary> /// Get GUID for a given shared param name. /// </summary> /// <param name="app">Revit application</param> /// <param name="defGroup">Definition group name</param> /// <param name="defName">Definition name</param> /// <returns>GUID</returns> public static Guid SharedParamGUID( Application app, string defGroup, string defName ) { Guid guid = Guid.Empty; try { Autodesk.Revit.Parameters.DefinitionFile file = app.OpenSharedParameterFile(); Autodesk.Revit.Parameters.DefinitionGroup group = file.Groups.get_Item( defGroup ); Autodesk.Revit.Parameters.Definition definition = group.Definitions.get_Item( defName ); Autodesk.Revit.Parameters.ExternalDefinition externalDefinition = definition as ExternalDefinition; guid = externalDefinition.GUID; } catch( Exception ) { } return guid; } #endregion // Helpers for shared parameters
''' <summary> ''' Get GUID for a given shared param name. ''' </summary> Shared Function SharedParamGUID(ByVal app As Revit.Application, _ ByVal defGroup As String, _ ByVal defName As String) As Guid Dim guid As Guid = guid.Empty Try Dim file As Autodesk.Revit.Parameters.DefinitionFile = app.OpenSharedParameterFile Dim group As Autodesk.Revit.Parameters.DefinitionGroup = file.Groups.Item(defGroup) Dim definition As Autodesk.Revit.Parameters.Definition = group.Definitions.Item(defName) Dim externalDefinition As Autodesk.Revit.Parameters.ExternalDefinition = definition guid = externalDefinition.GUID Catch End Try Return guid End Function
Add the following full command class to Labs4
.
We first launch or get Excel and then loop and export all doors row by row.
Note how we use the GUID to get the parameter and that we also export the
doors' standard tag (mark) and level parameters.
#region Lab4_3_2_ExportSharedParamToExcel /// <summary> /// 4.3.2 Export all door ids and FireRating param values to Excel. /// </summary> public class Lab4_3_2_ExportSharedParamToExcel : IExternalCommand { public IExternalCommand.Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { Application app = commandData.Application; Document doc = app.ActiveDocument; Category cat = doc.Settings.Categories.get_Item( Lab4_3_1_CreateAndBindSharedParam.Bic ); // Launch Excel (same as in Lab 4_2, so we really should have better created some utils...) X.Application excel = new X.ApplicationClass(); if( null == excel ) { LabUtils.ErrorMsg( "Failed to get or start Excel." ); return IExternalCommand.Result.Failed; } excel.Visible = true; X.Workbook workbook = excel.Workbooks.Add( Missing.Value ); X.Worksheet worksheet; //while( 1 < workbook.Sheets.Count ) //{ // worksheet = workbook.Sheets.get_Item( 0 ) as X.Worksheet; // worksheet.Delete(); //} worksheet = excel.ActiveSheet as X.Worksheet; worksheet.Name = "Revit " + cat.Name; worksheet.Cells[1, 1] = "ID"; worksheet.Cells[1, 2] = "Level"; worksheet.Cells[1, 3] = "Tag"; worksheet.Cells[1, 4] = LabConstants.gsSharedParamsDefFireRating; worksheet.get_Range( "A1", "Z1" ).Font.Bold = true; // since walls are not standard system families, implement and use // GetAllModelInstancesForACategory() instead to support both doors // and walls: //ElementSet doors = LabUtils.GetAllStandardFamilyInstancesForACategory( app, cat.Name ); List<Element> elems = LabUtils.GetAllModelInstancesForACategory( app, Lab4_3_1_CreateAndBindSharedParam.Bic ); // Get Shared param Guid Guid paramGuid = LabUtils.SharedParamGUID( app, LabConstants.gsSharedParamsGroupAPI, LabConstants.gsSharedParamsDefFireRating ); if( paramGuid.Equals( Guid.Empty ) ) { LabUtils.ErrorMsg( "No Shared param found in the file - aborting..." ); return IExternalCommand.Result.Failed; } // Loop all elements and export each to an Excel row int row = 2; foreach( Element elem in elems ) { worksheet.Cells[row, 1] = elem.Id.Value; // ID worksheet.Cells[row, 2] = elem.Level.Name; // Level // Tag: Autodesk.Revit.Parameter tagParameter = elem.get_Parameter( BuiltInParameter.ALL_MODEL_MARK ); if( null != tagParameter ) { worksheet.Cells[row, 3] = tagParameter.AsString(); } // FireRating: Parameter parameter = elem.get_Parameter( paramGuid ); if( null != parameter ) { worksheet.Cells[row, 4] = parameter.AsDouble(); } ++row; } return IExternalCommand.Result.Succeeded; } } #endregion // Lab4_3_2_ExportSharedParamToExcel
#Region "Lab4_3_2_ExportSharedParamToExcel" ''' <summary> ''' 4.3.2 Export all door ids and FireRating param values to Excel. ''' </summary> Public Class Lab4_3_2_ExportSharedParamToExcel Implements IExternalCommand Public Function Execute( _ ByVal commandData As ExternalCommandData, _ ByRef message As String, _ ByVal elements As ElementSet) _ As IExternalCommand.Result Implements IExternalCommand.Execute Dim app As Revit.Application = commandData.Application ' Launch Excel (same as in Lab 4_2, so we really should have better created some utils...) Dim excel As MsExcel.Application = New MsExcel.ApplicationClass() If (excel Is Nothing) Then MsgBox("Failed to get or start Excel!?") Return IExternalCommand.Result.Failed End If excel.Visible = True Dim workbook As MsExcel.Workbook = excel.Workbooks.Add() Dim worksheet As MsExcel.Worksheet Do While workbook.Sheets.Count > 1 worksheet = workbook.Sheets.Item(1) worksheet.Delete() Loop worksheet = excel.ActiveSheet worksheet.Name = "Revit Doors" ' Write the header row worksheet.Cells(1, 1).Value = "ID" worksheet.Cells(1, 2).Value = "Level" worksheet.Cells(1, 3).Value = "Tag" worksheet.Cells(1, 4).Value = gsSharedParamsDefFireRating excel.Rows("1").Font.Bold = True ' Use our utility from LabUtils to get all Doors Dim doors As List(Of Revit.Element) doors = LabUtils.GetAllModelInstancesForACategory(app, Lab4_3_1_CreateAndBindSharedParam.Bic) ' Get Shared param Guid Dim paramGuid As Guid = LabUtils.SharedParamGUID( _ app, gsSharedParamsGroupAPI, gsSharedParamsDefFireRating) If paramGuid.Equals(Guid.Empty) Then MsgBox("No Shared param found in the file !? - aborting...") Return IExternalCommand.Result.Failed End If ' Loop all doors and export each to an Excel row Dim door As Revit.Element Dim row As Integer = 2 For Each door In doors 'ID worksheet.Cells(row, 1).Value = door.Id.Value 'Level worksheet.Cells(row, 2).Value = door.Level.Name 'Tag Dim tagParameter As Autodesk.Revit.Parameter = _ door.Parameter(Autodesk.Revit.Parameters.BuiltInParameter.ALL_MODEL_MARK) If Not (tagParameter Is Nothing) Then worksheet.Cells(row, 3).Value = tagParameter.AsString End If '*FireRating* Dim parameter As Autodesk.Revit.Parameter = door.Parameter(paramGuid) If Not (parameter Is Nothing) Then worksheet.Cells(row, 4).Value = parameter.AsDouble End If row = row + 1 Next Return IExternalCommand.Result.Succeeded End Function End Class #End Region
Compile the code and update Revit.ini. Before running this command, add some doors to the model and set their FireRating parameters. You may also save the file for use in the next command.
In this section, we import the modified door FireRating parameter values back into the Revit building model.
The third and last command complements the previous one,
reading the updated FireRating parameter values back in from Excel
and updating the existing Revit element parameters.
Add the following full command to Labs4
:
#region Lab4_3_3_ImportSharedParamFromExcel /// <summary> /// 4.3.3 Import updated FireRating param values from Excel. /// </summary> public class Lab4_3_3_ImportSharedParamFromExcel : IExternalCommand { public IExternalCommand.Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { Application app = commandData.Application; Document doc = app.ActiveDocument; // Let user select the Excel file WinForms.OpenFileDialog dlg = new WinForms.OpenFileDialog(); dlg.Title = "Select source Excel file from which to update Revit shared parameters"; dlg.Filter = "Excel spreadsheet files (*.xls;*.xlsx)|*.xls;*.xlsx|All files (*)|*"; if( WinForms.DialogResult.OK != dlg.ShowDialog() ) { return IExternalCommand.Result.Cancelled; } // // Launch/Get Excel via COM Interop: // X.Application excel = new X.Application(); if( null == excel ) { LabUtils.ErrorMsg( "Failed to get or start Excel." ); return IExternalCommand.Result.Failed; } excel.Visible = true; X.Workbook workbook = excel.Workbooks.Open( dlg.FileName, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value ); X.Worksheet worksheet = workbook.ActiveSheet as X.Worksheet; // // Starting from row 2, loop the rows and extract Id and FireRating param. // int id; double fireRatingValue; int row = 2; while( true ) { try { // Extract relevant XLS values X.Range r = worksheet.Cells[row, 1] as X.Range; if( null == r.Value2 ) { break; } double d = (double) r.Value2; id = (int) d; if( 0 >= id ) { break; } r = worksheet.Cells[row, 4] as X.Range; fireRatingValue = (double) r.Value2; // Get document's door element via Id ElementId elementId; elementId.Value = id; Element door = doc.get_Element( ref elementId ); // Set the param if( null != door ) { Parameter parameter = door.get_Parameter( LabConstants.gsSharedParamsDefFireRating ); parameter.Set( fireRatingValue ); } } catch( Exception ) { break; } ++row; } // // Set focus back to Revit (there may be a better way, but this works :-) // #if USE_PROCESS_GET_PROCESSES foreach( Process p in Process.GetProcesses() ) { try { if( "REVIT" == p.ProcessName.ToUpper().Substring( 0, 5 ) ) { // In VB, we can use AppActivate( p.Id ); // Pre-3.0, I think you may need to use p/invoke and call the native Windows // SetForegroundWindow() function directly. // http://www.codeproject.com/csharp/windowhider.asp?df=100 break; } } catch( Exception ) { } } #endif // USE_PROCESS_GET_PROCESSES JtRevitWindow w = new JtRevitWindow(); w.Activate(); return IExternalCommand.Result.Succeeded; } } #endregion // Lab4_3_3_ImportSharedParamFromExcel
#Region "Lab4_3_3_ImportSharedParamFromExcel" ''' <summary> ''' 4.3.3 Import updated FireRating param values from Excel. ''' </summary> Public Class Lab4_3_3_ImportSharedParamFromExcel Implements IExternalCommand Public Function Execute( _ ByVal commandData As ExternalCommandData, _ ByRef message As String, _ ByVal elements As ElementSet) _ As IExternalCommand.Result Implements IExternalCommand.Execute Dim app As Revit.Application = commandData.Application ' Let user select the Excel file Dim dlgFileXLS As New OpenFileDialog() With dlgFileXLS .Title = "Select the Excel file to update Revit Shared Parameters from" .Filter = "Excel XLS Files (*.xls)|*.xls" If Not .ShowDialog() = DialogResult.OK Then Return IExternalCommand.Result.Cancelled End If End With ' Launch Excel and open the selected file Dim excel As MsExcel.Application = New MsExcel.ApplicationClass() If (excel Is Nothing) Then MsgBox("Failed to get or start Excel!?") Return IExternalCommand.Result.Failed End If excel.Visible = True Dim workbook As MsExcel.Workbook = excel.Workbooks.Open(dlgFileXLS.FileName) Dim worksheet As MsExcel.Worksheet = workbook.ActiveSheet ' Starting from row 2, loop the rows and extract Id and FireRating param. Dim id As Integer Dim fireRatingValue As Double Dim row As Integer = 2 Do Try ' Extract relevant XLS values id = worksheet.Cells(row, 1).Value If id <= 0 Then Exit Do fireRatingValue = worksheet.Cells(row, 4).Value ' Get document's door element via Id Dim elementId As Autodesk.Revit.ElementId elementId.Value = id Dim door As Autodesk.Revit.Element = app.ActiveDocument.Element(elementId) ' Set the param If Not (door Is Nothing) Then Dim parameter As Parameter = door.Parameter(gsSharedParamsDefFireRating) parameter.Set(fireRatingValue) End If Catch Exit Do End Try row = row + 1 Loop ' Set focus back to Revit (there may be a better way, but this works :-) Dim p, cPs() As Process cPs = Process.GetProcesses() For Each p In cPs Try If p.ProcessName.ToUpper.Substring(0, 5) = "REVIT" Then AppActivate(p.Id) Exit For End If Catch End Try Next Return IExternalCommand.Result.Succeeded End Function End Class #End Region
Compile the code and update Revit.ini again. Before running this command, make sure you first export some Revit door elements to the Excel sheet using the previous command, update some of the FireRating values in Excel and save the file, remembering the path which you will need to select it to read it in again.
next previous home copyright © 2007-2008 jeremy tammik, autodesk inc. all rights reserved.