Project Parameters - General Observations
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Ok Guys n gals, let's talk about Project Parameters (yet again).
This is not a question, but instead just a few findings that may, hopefully, get the developers to take another look at how they handle Project Parameters via the API.
I have spent days reading through these groups (wasted a really nice weekend even) going back to discussions started in 2013 that keep getting additional comments added to them because the source problem has yet to be resolved, regardless of cases being submitted for this aspect or that.
My "wish list" item would be to simply clean up the mess so we can actually work with what is suppossed to be the most powerful aspect of Revit.
Attached is a code snippet that I have put together (2020, 2021) that actually walks us through the problems working with Project Parameters (written in VB)...
Dump it into a macro, build it and run it if desired.
Inserting it into a code block here makes it unreadable due to wrapping but I'll do it anyway:
'
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports RVT = Autodesk.Revit
Imports RUI = Autodesk.Revit.UI
Imports RDB = Autodesk.Revit.DB
Imports RES = Autodesk.Revit.DB.ExtensibleStorage
'copy all but the first line into the attributes section preceding the Visual Studio Class
<RDB.Macros.AddInId("FB02757D-D54F-4832-AD8D-AF59AE8DFEE9")> _
<RVT.Attributes.Transaction(RVT.Attributes.TransactionMode.Manual)> _
<RVT.Attributes.Regeneration(RVT.Attributes.RegenerationOption.Manual)> _
<RVT.Attributes.Journaling(RVT.Attributes.JournalingMode.NoCommandData)> _
Partial Public Class ThisApplication
Private Sub Module_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup
End Sub
Private Sub Module_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown
End Sub
'This is a generic stand in for the Execute Function required in a Visual Studio application
Public Sub ExecFunc()
'create these as needed in Visual Studio Execute Method to pass along to the Sub and/or Function calls
'Dim thisAppplication As RVT.ApplicationServices.Application = commandData.Application.Application
'Dim thisUIApplication As RUI.UIApplication = commandData.Application
'Dim thisUIDocument As RUI.UIDocument = commandData.Application.ActiveUIDocument
'Dim thisDocument As RDB.Document = commandData.Application.ActiveUIDocument.Document
'Dim thisDocumentView As RDB.View = commandData.View
'create them in a macro with:
Dim thisUIApplication As RUI.UIApplication = ActiveUIDocument.Application
Dim thisApplication As RVT.ApplicationServices.Application = ActiveUIDocument.Application.Application
Dim thisUIDocument As RUI.UIDocument = ActiveUIDocument
Dim thisDocument As RDB.Document = ActiveUIDocument.Document
Dim thisDocumentView As RDB.View = ActiveUIDocument.Document.ActiveView
GetDocParameters(thisDocument, thisUIDocument)
End Sub
Private Sub GetDocParameters(thisDoc As RDB.Document, thisUIDoc As RUI.UIDocument)
'start a log file:
Dim logFile As System.IO.FileInfo = New System.IO.FileInfo(My.Computer.FileSystem.CombinePath(Me.AddinFolder, "Global Parameters.txt"))
Dim writer As System.IO.StreamWriter = logFile.CreateText()
writer.WriteLine("Document: " & thisDoc.Title)
writer.WriteLine(vbCr & "*** Start Global Patameters ***" & vbCr)
'global Parameters can only be Local (non shared) parameters... WHY???
Dim globalParams As IList(Of RDB.ElementId) = RDB.GlobalParametersManager.GetGlobalParametersOrdered(thisDoc)
For Each paramID As RDB.ElementId In globalParams
Dim paraElem As RDB.GlobalParameter = DirectCast(thisDoc.GetElement(paramID), RDB.GlobalParameter)
writer.WriteLine("Global Par Return: " & paraElem.GetType().ToString)
Dim paraDef As RDB.InternalDefinition = paraElem.GetDefinition
writer.WriteLine("Internal Def Return: " & paraDef.GetType().ToString)
writer.WriteLine("Parameter Definition ID: " & paraDef.Id.ToString & " - Name: " & paraDef.Name)
writer.WriteLine("Value Format Type: " & paraDef.ParameterType & " : " & paraDef.ParameterType.ToString)
writer.WriteLine("Group: " & paraDef.ParameterGroup & " : " & paraDef.ParameterGroup.ToString & " : " & RDB.LabelUtils.GetLabelFor(paraDef.ParameterGroup))
If paraElem.IsDrivenByFormula = True Then
writer.WriteLine("Is Formula Driven: " & paraElem.IsDrivenByFormula.ToString & " : Formula: " & paraElem.GetFormula)
Else
writer.WriteLine("Is Formula Driven: " & paraElem.IsDrivenByFormula.ToString)
End If
'Why is the Value Storage type not part of the definition since that's where it is defined?
writer.WriteLine("Value Type Return: " & paraElem.GetValue().ToString)
Select Case paraElem.GetValue().GetType()
Case GetType(RDB.IntegerParameterValue)
Dim paraVal As RDB.IntegerParameterValue = DirectCast(paraElem.GetValue(), RDB.IntegerParameterValue)
writer.WriteLine("Actual Value Type: Integer: " & paraVal.Value)
Case GetType(RDB.DoubleParameterValue)
Dim paraVal As RDB.DoubleParameterValue = DirectCast(paraElem.GetValue(), RDB.DoubleParameterValue)
writer.WriteLine("Actual Value Type: Double: " & paraVal.Value)
Case GetType(RDB.ElementIdParameterValue)
Dim paraVal As RDB.ElementIdParameterValue = DirectCast(paraElem.GetValue(), RDB.ElementIdParameterValue)
writer.WriteLine("Actual Value Type: Element ID: " & paraVal.Value.ToString)
Case GetType(RDB.StringParameterValue)
Dim paraVal As RDB.StringParameterValue = DirectCast(paraElem.GetValue(), RDB.StringParameterValue)
writer.WriteLine("Actual Value Type: String: " & paraVal.Value)
End Select
writer.WriteLine()
Next
writer.WriteLine(vbCr & "*** Start Project Parameters ***" & vbCr)
Dim paraBind As RDB.BindingMap = thisDoc.ParameterBindings
Dim paraIter As RDB.DefinitionBindingMapIterator = paraBind.ForwardIterator()
paraIter.Reset()
While paraIter.MoveNext = True
Dim thisDef As RDB.Definition = paraIter.[key]
'they all return as InternalDefinition, even those that are actually External definitions and are declared as base defs
writer.WriteLine("Base Definition Return: " & thisDef.GetType().ToString)
'Trying to cast it to an external definition always fails,
'regardless of how it was defined because it's already an Internal Def and the two are not compatible
Dim extDef As RDB.ExternalDefinition = TryCast(paraIter.[key], RDB.ExternalDefinition)
If extDef IsNot Nothing Then
writer.WriteLine("Shared Parameter: " & extDef.Name)
Else
'so we cast it specifically to Internal Definition from base definition to get it's ID
Dim intDef As RDB.InternalDefinition = TryCast(paraIter.[key], RDB.InternalDefinition)
writer.WriteLine("Parameter Definition ID: " & intDef.Id.ToString & " - Name: " & intDef.Name)
'which allows us to map the definition to an element as if it's in use.... or is it???
Dim sharedParam As RDB.SharedParameterElement = TryCast(thisDoc.GetElement(intdef.Id), RDB.SharedParameterElement)
If sharedParam IsNot Nothing Then
'but the shared parameter element still returns as an Internal Definition...
writer.WriteLine("Shared Param Return: " & sharedParam.GetType().ToString)
'but the shared parameter Element seems to be derived from Base Element, not from Parameter Element
'so it doesn't contain appropriate methods and properties for a parameter element...
writer.WriteLine("Shared Parameter: " & sharedParam.Name & " : GUID: " & sharedParam.GuidValue.ToString)
'one would think that we could finally cast it to an external definition using the GetDefinition method:
'Dim extDef As RDB.ExternalDefinition = sharedParam.GetDefinition
'but no... that returns an Internal Def again.
'so the crux of the entire issue is the lack of implementation of ExternalDefinition within a project file...
'compounded by not being able to get typical parameter element returns on a SharedParameterElement...
'compounded even further by the fact that the only type of parameter that you can create is an external definition...
'compounded even further by the fact that you can only retrieve Internal Definitions regardless of actual parameter type...
'and why the heck do the definition objects not contain the storage type since that's where they're defined???
Else
writer.WriteLine("Parameter Type: " & thisDef.GetType().ToString)
writer.WriteLine("Document Parameter: " & intDef.Name & " : " & intDef.Id.ToString)
End If
End If
'but yet, we have to go back to the base definition object to get the category
'since this property isn't inherited by either Internal or External definitions,
'yet this should be on an Element because this can actually be changed (even when importing shared)
'of course, you can get the Parameter Type from the base definition,
'but you can't get the Storage type even though the definition defines it???
writer.WriteLine("Value Format Type: " & thisDef.ParameterType & " : " & thisDef.ParameterType.ToString)
writer.WriteLine("Group: " & thisDef.ParameterGroup & " : " & thisDef.ParameterGroup.ToString & " : " & RDB.LabelUtils.GetLabelFor(thisDef.ParameterGroup))
'then we can determine if it's type bound or instance bound by trying to cast the type binding to one or the other...
Dim typeBound As RDB.TypeBinding = TryCast(paraIter.Current, RDB.TypeBinding)
If typeBound IsNot Nothing Then
writer.WriteLine(" - Type Bound Parameter - ")
Dim Cats As RDB.CategorySet = typeBound.Categories
For Each cat As RDB.Category In Cats
writer.WriteLine("Bound To: " & cat.Name)
Next
Else
Dim instBound As RDB.InstanceBinding = TryCast(paraIter.Current, RDB.InstanceBinding)
If instBound IsNot Nothing Then
writer.WriteLine(" - Instance Bound Parameter - ")
Dim Cats As RDB.CategorySet = instBound.Categories
For Each cat As RDB.Category In Cats
writer.WriteLine("Bound To: " & cat.Name)
Next
End If
End If
writer.WriteLine()
End While
'finalize the log file
writer.Flush()
writer.Close()
System.Diagnostics.Process.Start("notepad.exe", logFile.FullName)
End Sub
End Class
the comments in the snippet explain the root causes of all of our problems when trying to manipulate, or even simply query, Project Parameters within a Project File.
But they come down to these:
'they all return as InternalDefinition, even those that are actually External definitions and are declared as base defs
'Trying to cast it to an external definition always fails,
'regardless of how it was defined because it's already an Internal Def and the two are not compatible
'so we cast it specifically to Internal Definition from base definition to get it's ID
'which allows us to map the definition to an element as if it's in use.... or does it???
'but the shared parameter element still returns as an Internal Definition...
'but the shared parameter Element seems to be derived from Base Element, not from Parameter Element
'so it doesn't contain appropriate methods and properties for a parameter element...
'one would think that we could finally cast it to an external definition using the GetDefinition method:
'but no... that returns an Internal Def again.
'so the crux of the entire issue is the lack of implementation of ExternalDefinition within a project file...
'compounded by not being able to get typical parameter element returns on a SharedParameterElement...
'compounded even further by the fact that the only type of parameter that you can create is an external definition...
'compounded even further by the fact that you can only retrieve Internal Definitions regardless of actual parameter type...
'and why the heck do the definition objects not contain the storage type since that's where they're defined???
'but yet, we have to go back to the base definition object to get the category
'since this property isn't inherited by either Internal or External definitions,
'yet this should be on an Element because this can actually be changed (even when importing shared)
'of course, you can get the Parameter Type from the base definition,
'but you can't get the Storage type even though the definition defines it???
Those are my findings, Am I wrong? Am I missing something?
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)