Elegant way to get possible ElementId values for specific Parameter

Elegant way to get possible ElementId values for specific Parameter

Anonymous
Not applicable
3,667 Views
11 Replies
Message 1 of 12

Elegant way to get possible ElementId values for specific Parameter

Anonymous
Not applicable

Hello,

 

I'm creating a sort of "Property Editor" dialog for user selected elements. In my dialog, the user may pick from a list of non read-only parameters, and specify an alternate value that my application will use later on under particular circumstances (see dialog image below).

 

The challenge is to provide a list of relevant alternate "Element Id Values" based on the selected parameter. So far, my approach is to explicitly exam the "Current Value (ElementId)" of selected parameter and create alternate value list accordingly. For some "Current Values" I use the "GetSimilarTypes" function, while others I simply filter the document for elements of same category (see code below image).

 

So far I have two questions...

 

  1. Can someone suggest a less explicit, more elegant way of populating the alternate ElementId values?
  2. Based on my current approach, if the "Current Value" is NULL, I have no way of getting alternate values.

Thanks,

 

Jon

 

 

 

2017-06-18_18-07-28.jpg

 

 

if (request == EditPropertyOptRequest.GetAvailableValues) {

cond.SelElemValue = null; cond.PropertyOpt.ElementValues = new ObservableCollection<Element>(); try { dynamic CurrentElem = doc.GetElement(cond.SelProperty.AsElementId); if ((CurrentElem) is WallType) { WallType obj = CurrentElem; foreach (void ElemId_loopVariable in obj.GetSimilarTypes) { ElemId = ElemId_loopVariable; cond.PropertyOpt.ElementValues.Add(doc.GetElement(ElemId)); } } else if ((CurrentElem) is FloorType) { FloorType obj = CurrentElem; foreach (void ElemId_loopVariable in obj.GetSimilarTypes) { ElemId = ElemId_loopVariable; cond.PropertyOpt.ElementValues.Add(doc.GetElement(ElemId)); } } else if ((CurrentElem) is FamilySymbol) { FamilySymbol obj = CurrentElem; foreach (void ElemId_loopVariable in obj.GetSimilarTypes) { ElemId = ElemId_loopVariable; cond.PropertyOpt.ElementValues.Add(doc.GetElement(ElemId)); } } else if ((CurrentElem) is Level) { FilteredElementCollector collector = new FilteredElementCollector(doc); collector.OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType(); foreach (void elem_loopVariable in collector) { elem = elem_loopVariable; cond.PropertyOpt.ElementValues.Add(elem); } } else if ((CurrentElem) is Phase) { FilteredElementCollector collector = new FilteredElementCollector(doc); collector.OfCategory(BuiltInCategory.OST_Phases).WhereElementIsNotElementType(); foreach (void elem_loopVariable in collector) { elem = elem_loopVariable; cond.PropertyOpt.ElementValues.Add(elem); } } cond.SelElemValue = cond.PropertyOpt.ElementValues(0); } catch (Exception ex) { } }

 

0 Likes
Accepted solutions (3)
3,668 Views
11 Replies
Replies (11)
Message 2 of 12

Anonymous
Not applicable
Accepted solution

Quick update...

 

After realizing "GetSimilarTypes" method is inherited from "ElementType" base class, I now attempt to cast Element to ElementType, then use "GetSimilarTypes" method if "ElemType" is Not NULL. Otherwise I filter document for NonElementTypes of same Category. I still need to handle elements with No Category, which so far I've identified Line Styles as being this way.

 

I am quite happy with this approach!

 

Jon 

 

if (request == EditPropertyOptRequest.GetAvailableValues) {
	cond.SelElemValue = null;
	cond.PropertyOpt.ElementValues = new ObservableCollection<Element>();

	if (cond.SelProperty.StorageType == StorageType.ElementId) {
		ElementId ElemId = cond.SelProperty.AsElementId;
		if (ElemId == null)
			return;

		Element Elem = doc.GetElement(ElemId);
		ElementType ElemType = Elem as ElementType;

		if (ElemType != null) {
			foreach ( ElemId in ElemType.GetSimilarTypes) {
				cond.PropertyOpt.ElementValues.Add(doc.GetElement(ElemId));
			}

		} else if (Elem.Category != null) {
			FilteredElementCollector collector = new FilteredElementCollector(doc);
			collector.OfCategory(Elem.Category.Id.IntegerValue).WhereElementIsNotElementType();

			foreach ( Elem in collector) {
				cond.PropertyOpt.ElementValues.Add(Elem);
			}
		} else {
			//
			//Handle non-category elements. Namely Line Styles(aka GraphicStlyes)
			//
		}

		cond.SelElemValue = cond.PropertyOpt.ElementValues(0);

	}

}

 

0 Likes
Message 3 of 12

jeremytammik
Autodesk
Autodesk

Dear Jon,

 

Thank you for sharing this.

 

How does this fit in with the combo box dropdown workarounds suggested here?

 

http://thebuildingcoder.typepad.com/blog/2015/11/drop-down-enumerated-parameter-values.html

 

Should I add your solution as a new workaround, or is it a different class of animal?

 

Thank you!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 4 of 12

Anonymous
Not applicable

Hi Jeremy,

 

I believe this is a "different class of animal". In brief, the post touches on how to mimic contextual content of Element Properties Panel using Revit API. More specifically, how to get a list of available ElementId values for a given Parameter whos Storage Type = ElementId and has an existing value to use as reference. Mostly revolving around use of 'ElementType.GetSimilarTypes' method.

 

Note: I still cant find a way to get available values for a Parameter that does not have existing value for reference.

 

Thanks,

 

Jon

 

 

0 Likes
Message 5 of 12

jeremytammik
Autodesk
Autodesk

Dear Jon,

 

Thank you, all clear now.

 

To get available values for a Parameter that does not have an existing value for reference, you might be able to create a suitable template element in a temporary transaction, grab the data you require from it, and roll back the transaction without committing, also know as the 'temporary transaction trick' TTT:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.53

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 6 of 12

Anonymous
Not applicable

Thanks for suggestion Jeremy,

 

TTT may work, but I don't see a way to determine what a "suitable template element" might be. Given a parameter with <null> ElementID value, the only helpful information looks like BuiltInParameter Id, but that doesn't get me very far. Worst case, I make exceptions for my favorite BuiltInParameter Id's to assist in determining "suitable template element".

 

Almost as if the Parameter class would benefit from something like a "GetSampleValueType" method.

 

Jon

 

 

0 Likes
Message 7 of 12

FAIR59
Advisor
Advisor
Accepted solution

you can use a ElementParameterFilter, to get all the elements that have a value other then ElementId.InvalidElementId  for a specific parameter.

 

this code filters for elements in the active view, that are demolished in some phase.

 

            ParameterValueProvider pvp_Demolished = new ParameterValueProvider(new ElementId((int)BuiltInParameter.PHASE_DEMOLISHED));
            FilterNumericGreater fgreater = new FilterNumericGreater();
            FilterElementIdRule IdFilter = new FilterElementIdRule(pvp_Demolished, fgreater, ElementId.InvalidElementId);
            ElementParameterFilter efilter = new ElementParameterFilter(IdFilter);

            FilteredElementCollector elems = new FilteredElementCollector(doc, doc.ActiveView.Id)
                .WherePasses(efilter);

 

 

0 Likes
Message 8 of 12

Anonymous
Not applicable

Thanks for the suggestion FAIR59,

 

Unfortunately that's not what I'm looking to accomplish. Please see video below for better explanation of what I'm trying to do. Maybe you might have another idea.

 

Thanks,

 

Jon

 

0 Likes
Message 9 of 12

FAIR59
Advisor
Advisor
Accepted solution

Actually, it is part of what you are trying to accomplish. With an ElementParameterFilter you get a list of Elements (if there are any) that have a value for the parameter in question, and then you may find all the relevant values, or display all the found values.  

Message 10 of 12

Anonymous
Not applicable

Oh I see now, VERY CLEVER!!!

 

Just implemented and works like a charm. Only caveat like you say is "if there are any", but I can live with that.

 

Thanks for the great tip!!!

 

Jon

 

 

 

 

0 Likes
Message 11 of 12

jeremytammik
Autodesk
Autodesk

Rescued for posterity here:

 

http://thebuildingcoder.typepad.com/blog/2017/06/finding-an-exit-path-and-elementid-parameter-values...

 

Thanks guys, especially Fair59!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 12 of 12

schipmanU5A9G
Participant
Participant

Old thread, but was just working through this problem myself.

 

The accepted solution will only find ElementId values which have been used somewhere in the model. So if for example you have 10 phases, but only 2 of them are used somewhere, your dropdown will only display those 2 phases.

 

But you can get all available options though if any element in the model has a value set for that parameter. As long as there is one element that has chosen an option besides the None option for a parameter, you can find all options for that parameter using the below. The below code will capture all these options for ElementId parameters like Phase, Level, End Connection, etc. Note if you want to retrieve possible values for the Family, Family and Type, and Type parameters, you would have to supply an element or category of interest and just collect all the ElementTypes pertinent to that category. Note it is important to use the below approach rather than ElementType.GetSimilarTypes(), because some ElementId parameters do not represent ElementTypes - for example base level, phase created, etc.

 

Parameter param = //Whatever parameter you supply
var paramDef = param.Definition as InternalDefinition;
ElementId id = param.Id;
// Need to filter for elements that have a value of the parameter, and for value not equal to InvalidElementId
var filter = new ElementParameterFilter(new HasValueFilterRule(id));
var filter2 = new ElementParameterFilter(new FilterInverseRule(new FilterElementIdRule(new ParameterValueProvider(id), new FilterNumericEquals(), ElementId.InvalidElementId)));

var elem = new FilteredElementCollector(doc).WherePasses(filter).WherePasses(filter2).FirstOrDefault();

if (elem is not null)
{
    var valueId = elem.get_Parameter(paramDef).AsElementId();
    var valueElem = doc.GetElement(valueId); 
    var allOfType = new FilteredElementCollector(doc).OfClass(valueElem.GetType()).ToElements().GroupBy(e => e.Name).Select(g => g.First());
}

 

If all elements in the model have the "None" option (if available) set for the parameter, or if there are no elements modeled with the parameter available, elem will be null and you can't retrieve the available options.

 

It's another can of worms to get available options for some of the built-in parameters that are an enumeration, like Workset, "Export to IFC", and View Scale. These are just stored as an Integer internally, and the names of the options don't seem to generally be exposed. For Workset you can just use a FilteredWorksetCollector, but for the other enumerations, the best way I have found to get them is to run a dummy transaction where you try to set the parameter to each integer in a reasonable range of values (most of the enumerations are in the range 0-3, but some like View Scale go much higher). If the parameter.Set(integer) succeeds, you can collect the enumeration name of the result using parameter.ToValueString() and collect that name and corresponding integer value into a dictionary and make them unique by the name. Then cancel the transaction.

 

0 Likes