Category Archives: RevitAPI

Utilizzo dei Generics per un FilteredElementCollector universale

Scenario

Quando scriviamo un componente per le RevitAPI capita spesso di dover selezionare da un documento tutti gli elementi di un certo tipo, o eventualmente un sottoinsieme di questi – senza dover passare per un PickBox.

Utilizzo dei Generics per una soluzione universale

Essendomi trovato in moltissimi casi ad affrontare la selezione di elementi di un certo tipo non conosciuto, ho scritto una piccola classe che consente di selezionare tutti le istanze di un certo tipo rapidamente. La classe fornisce accesso ad un solo metodo statico.

	public static class ElementsOfType<T> where T : Element
	{
		public static List<T> GetElements(Document doc, BuiltInCategory category) 
		{
			FilteredElementCollector collector = new FilteredElementCollector(doc);
			ElementCategoryFilter filter = new ElementCategoryFilter(category);
			IList<Element> elements = collector.WherePasses(filter).ToElements();
			List<T> passes = new List<T>();
			foreach (Element element in elements)
			{
				if (element is T)
				{
					passes.Add(element as T);
				}
			}
			return passes;
		}
	}

Questo è proprio uno di quei casi in cui è utilissimo ottimizzare il codice tramite Generics 🙂

ISelectionFilter per selezionare elementi via RevitAPI

Scenario

Uno scenario base nella progettazione di un plugin di Revit consiste nella selezione di elementi appartenenti a determinate categorie.

Implementazione di un nuovo ISelection Filter

Per creare un box di selezione, semplicemente:

public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			UIApplication uiapp = commandData.Application;
			UIDocument uidoc = uiapp.ActiveUIDocument;
			Application app = uiapp.Application;
			Document doc = uidoc.Document;

			Selection sel = uidoc.Selection;
			var pickedColumns = sel.PickObjects(ObjectType.Element, new OnlyColumns(doc), "Scegli Colonne");
...

Come si vede dal codice il mio obiettivo è, in questo caso, selezionare solo elementi che siano del tipo “Pilastr strutturale”. Potremmo con questa tecnica selezionare qualsiasi FamilySymbol o qualsiasi altro tipo.
Il metodo PickObject di document supporta istanze che implementino ISelectionFilter – che a sua volta richiede due metodi: AllowElement e AllowReference. La mia implementazione per selezionare solo pilastri strutturali:

	public class OnlyColumns : ISelectionFilter
	{
		private Document document { get; set; }
		public bool AllowElement(Element elem)
		{
			if (elem is FamilyInstance)
			{
				Category c = (elem as FamilyInstance).Category;
				if(c.Name == "Pilastri strutturali") { return true; }
			}
			return false;
		}

		public bool AllowReference(Reference reference, XYZ position)
		{
			var e = document.GetElement(reference.ElementId);
			return AllowElement(e);
		}

		public OnlyColumns(Document document)
		{
			this.document = document;
		}
	}

Una tecnica base che utilizzo sempre per ottenere informazioni sull’elemento che voglio selezionare tramite un filtro consiste nel passare al costruttore del nostro ISelectionFilter un’istanza del documento. Solo in questo modo possiamo indagare sulla natura dell’elemento selezionato andandolo a ripescare dal documento attivo. Facile, no?

Ottimizzazione

Il codice presentato presenta almeno una bruttura: fa riferimento al nome-stringa “Pilastri strutturali”. Cosa succede con componenti esterni, magari con parametri in inglese? Semplice: non funzionerà.
Per risolvere il problema bisogna convertire le BuiltInCategory in Category. Un banale casting non è di aiuto.
Le categorie vengono gestite da Revit all’intero dei DocumentSettings:

public class OnlyColumns : ISelectionFilter
	{
		private Document document { get; set; }
		private Categories categories { get; set; }
		public bool AllowElement(Element elem)
		{
			if (elem is FamilyInstance)
			{
				Category c = (elem as FamilyInstance).Category;
				Category builtInCategory = categories.get_Item(BuiltInCategory.OST_StructuralColumns);
				if (c.Id == builtInCategory.Id) { return true; } // c == builtInCategory darà FALSE!
			}
			return false;
		}

		public bool AllowReference(Reference reference, XYZ position)
		{
			var e = document.GetElement(reference.ElementId);
			return AllowElement(e);
		}

		public OnlyColumns(Document document)
		{
			this.document = document;
			this.categories = document.Settings.Categories;
		}
	}

E questa è la versione più pulita del codice.