We already looked at the topic of setting the SetAllowVaryBetweenGroups
flag on a shared parameter
in Scott Conover's parameter definition overview.
The setting was introduced in the Revit 2014 API, cf. Parameter variance among group instances.
Now Miroslav Schonauer raised it again, asking:
Question: Is the following option for group behaviour of shared param instance bindings exposed to API?
If yes, that solves all the issues :-)
If not – the default outcome seems to be aligned per group type.
Is there any way to set can vary by group instance (what I need) the default for API-created bindings?
Later: I found this Revit API discussion forum thread on creating a project parameter with values can vary by group instance selected which explains that it is kind-of possible.
The problem remains that SetAllowVaryBetweenGroups
is available only on InternalDefinition
, while my programmatically created shared param has ExternalDefinition
.
That thread explains that getting the binding after it has been created (i.e., in a 'Step 2') does return InternalDefinition
, so this method can be used.
Can someone at least confirm that there is nothing simpler to do than the above 2-step process?
Answer: Yes. You need to do the two-step process:
The easiest way to go from one to the other:
Response: I implemented this method to handle the setting of the SetAllowVaryBetweenGroups
flag:
/// <summary> /// Helper method to control `SetAllowVaryBetweenGroups` /// option for instance binding param /// </summary> static void SetInstanceParamVaryBetweenGroupsBehaviour( Document doc, Guid guid, bool allowVaryBetweenGroups = true ) { try // last resort { SharedParameterElement sp = SharedParameterElement.Lookup( doc, guid ); // Should never happen as we will call // this only for *existing* shared param. if( null == sp ) return; InternalDefinition def = sp.GetDefinition(); if( def.VariesAcrossGroups != allowVaryBetweenGroups ) { // Must be within an outer transaction! def.SetAllowVaryBetweenGroups( doc, allowVaryBetweenGroups ); } } catch { } // ideally, should report something to log... }
It assumes that guid
comes from a known shared parameter.
Further good news: this can be called not only immediately after programmatically binding a new shared param, but also to silently change this specific setting for an existing shared parameter.
For example, we all typically have our own helper methods to get-or-create a shared parameter binding, cf., e.g., my method to add a category to a shared parameter binding.
Here is a code snippet providing enough to get the gist of how the above can be used (ignore my helper classes and error handling):
// Assumes outer transaction public static Parameter GetOrCreateElemSharedParam( Element elem, string paramName, string grpName, ParameterType paramType, bool visible, bool instanceBinding, bool userModifiable, Guid guid, bool useTempSharedParamFile, string tooltip = "", BuiltInParameterGroup uiGrp = BuiltInParameterGroup.INVALID, bool allowVaryBetweenGroups = true ) { try { // Check if existing Parameter param = elem.LookupParameter( paramName ); if( null != param ) { // NOTE: If you don't want forcefully setting // the "old" instance params to // allowVaryBetweenGroups =true, // just comment the next 3 lines. if( instanceBinding && allowVaryBetweenGroups ) { SetInstanceParamVaryBetweenGroupsBehaviour( elem.Document, guid, allowVaryBetweenGroups ); } return param; } // If here, need to create it (my custom // implementation and classes…) BindSharedParamResult res = BindSharedParam( elem.Document, elem.Category, paramName, grpName, paramType, visible, instanceBinding, userModifiable, guid, useTempSharedParamFile, tooltip, uiGrp ); if( res != BindSharedParamResult.eSuccessfullyBound && res != BindSharedParamResult.eAlreadyBound ) { return null; } // Set AllowVaryBetweenGroups for NEW Instance // Binding Shared Param if( instanceBinding ) { SetInstanceParamVaryBetweenGroupsBehaviour( elem.Document, guid, allowVaryBetweenGroups ); } // If here, binding is OK and param seems to be // IMMEDIATELY available from the very same command return elem.LookupParameter( paramName ); } catch( Exception ex ) { System.Windows.Forms.MessageBox.Show( string.Format( "Error in getting or creating Element Param: {0}", ex.Message ) ); return null; } }
I added Miro's method SetInstanceParamVaryBetweenGroupsBehaviour
to The Building Coder samples release 2018.0.134.11 in
the module CmdCreateSharedParams.cs L441-L470.
Many thanks to Miro for raising this issue and sharing his approach to solve it!