In this second episode we break down the code line by line, understanding each method, making modifications and exploring the Revit API docs.
Entendiendo el código de un addin de Revit
En el artículo anterior configuramos Visual Studio, generamos nuestro primer archivo DLL y creamos el manifiesto .addin para cargar un addin en Revit. El comando aparecía en la interfaz y funcionaba correctamente.
Ahora toca entender qué hace realmente el código.
Copiar y pegar es suficiente para empezar, pero si queremos modificar un addin, ampliarlo o crear uno desde cero, necesitamos comprender qué ocurre en cada línea. Vamos a analizar la estructura básica de un comando externo de Revit y cómo interactúa con el modelo.
Using y namespaces
Las primeras líneas del archivo suelen ser estas:
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.Attributes;
Cuando añadimos referencias a las DLL de Revit, Visual Studio tiene acceso a miles de clases. Los using sirven para indicar en qué namespaces queremos trabajar y evitar escribir rutas completas constantemente.
Por ejemplo, gracias a:
using Autodesk.Revit.DB;
podemos escribir:
Document
en lugar de:
Autodesk.Revit.DB.Document
Esto simplifica enormemente la lectura y escritura del código.
Además, los namespaces ayudan a evitar conflictos entre clases con el mismo nombre pertenecientes a librerías distintas.
En este caso:
-
Autodesk.Revit.DByAutodesk.Revit.Attributespertenecen aRevitAPI.dll -
Autodesk.Revit.UIyAutodesk.Revit.UI.Selectionpertenecen aRevitAPIUI.dll
La clase y IExternalCommand
El siguiente bloque suele tener esta estructura:
[Transaction(TransactionMode.Manual)]
public class Class1 : IExternalCommand
Aquí hay dos conceptos importantes.
Atributo [Transaction]
La línea entre corchetes es un atributo.
[Transaction(TransactionMode.Manual)]
Le indica a Revit cómo debe gestionar las transacciones del comando. En este caso usamos Manual, lo que significa que el addin será responsable de abrir y cerrar las transacciones desde el código.
Más adelante veremos por qué esto es obligatorio para modificar el modelo.
La clase
public class Class1 : IExternalCommand
public
Es el modificador de acceso.
Revit necesita poder acceder a esta clase para ejecutar el comando. Si no es pública, el addin no podrá cargarse.
class
Define una clase, es decir, una plantilla para crear objetos.
Class1
Es simplemente el nombre de la clase.
Conviene usar nombres descriptivos en proyectos reales.
: IExternalCommand
Este es el elemento clave.
IExternalCommand es una interfaz de la API de Revit. Implementarla le dice a Revit que esta clase es un comando ejecutable desde la interfaz.
Sin esto, Revit no sabría que debe tratar la clase como un addin.
El método Execute()
Dentro de la clase encontramos el corazón del addin:
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
Cuando el usuario hace clic en el comando dentro de Revit, este método se ejecuta automáticamente.
commandData
Es el parámetro más importante.
A través de él accedemos a:
-
La aplicación de Revit
-
El documento activo
-
La interfaz
-
La selección del usuario
Es la puerta de entrada al entorno de Revit.
message
Es un texto que podemos modificar si ocurre un error.
Si el comando falla, Revit mostrará este mensaje al usuario en un cuadro de diálogo.
elements
Permite indicar qué elementos deben resaltarse en pantalla si el comando falla.
Es útil en addins más complejos para ayudar al usuario a localizar el problema.
El ritual de inicio
Las primeras líneas dentro de Execute() suelen repetirse en prácticamente todos los addins:
UIApplication uiapp = commandData.Application;
Document doc = uiapp.ActiveUIDocument.Document;
Aquí obtenemos:
-
La aplicación de Revit (
uiapp) -
El documento activo (
doc)
El objeto Document representa la base de datos del modelo: contiene elementos, familias, parámetros y toda la información del proyecto.
Selección de elementos
Primero declaramos una variable vacía de tipo Reference:
Reference pickedref;
Reference es la clase que utiliza Revit para representar referencias a elementos del modelo.
Después accedemos al sistema de selección:
Selection sel = uiapp.ActiveUIDocument.Selection;
Y pedimos al usuario que seleccione un elemento:
pickedref = sel.PickObject(
ObjectType.Element,
"Please select a group");
¿Qué hace PickObject()?
Este método:
-
Espera que el usuario seleccione algo
-
Devuelve una
Reference -
Muestra un mensaje en la esquina inferior izquierda de Revit
En este caso usamos:
ObjectType.Element
lo que permite seleccionar cualquier elemento del modelo.
Si el usuario pulsa ESC, el método lanza una excepción.
Obtener el elemento real
Cuando usamos PickObject(), no obtenemos el elemento directamente. Obtenemos una Reference.
Una Reference es simplemente una referencia al elemento, no el elemento en sí.
Para recuperar el objeto real usamos:
Element elem = doc.GetElement(pickedref);
GetElement() busca dentro del documento el elemento asociado a esa referencia.
Casting a Group
Ahora convertimos el elemento genérico en un grupo:
Group? group = elem as Group;
La API devuelve un Element, pero nosotros sabemos que esperamos un grupo.
El operador as intenta convertir el objeto:
-
Si realmente es un
Group, la conversión funciona -
Si no lo es, devuelve
null
Por eso usamos:
Group?
El ? indica que la variable puede contener null.
Esto evita errores si el usuario selecciona algo incorrecto, como una pared o una puerta.
Selección de un punto
Después pedimos al usuario un punto del modelo:
XYZ point = sel.PickPoint(
"Please pick a point to place group");
Diferencia entre PickObject() y PickPoint()
PickObject()
Devuelve una Reference a un elemento.
PickPoint()
Devuelve un XYZ, es decir, una coordenada en el espacio 3D.
Aquí no necesitamos saber qué elemento existe en ese punto, solo su posición.
Transacciones
Para modificar el modelo de Revit es obligatorio trabajar dentro de una transacción.
Transaction trans = new Transaction(doc);
trans.Start("Lab");
doc.Create.PlaceGroup(point, group?.GroupType);
trans.Commit();
La secuencia siempre es la misma:
-
Abrir transacción
-
Realizar cambios
-
Confirmar con
Commit()
¿Por qué existen las transacciones?
Revit necesita controlar exactamente qué cambios se realizan en el modelo.
Si ocurre un error durante la ejecución:
-
Revit puede hacer rollback
-
Todos los cambios se deshacen automáticamente
Cada transacción se convierte además en una entrada del historial de deshacer de Revit.
Colocar el grupo
La línea que realmente modifica el modelo es:
doc.Create.PlaceGroup(point, group?.GroupType);
Aquí:
-
pointes la coordenada elegida por el usuario -
group?.GroupTypeobtiene el tipo del grupo seleccionado
El operador ?. evita errores si group es null.
Resultado del comando
Finalmente:
return Result.Succeeded;
Esto le indica a Revit que el comando terminó correctamente.
Si devolvemos:
Result.Failed
o
Result.Cancelled
Revit deshará automáticamente los cambios realizados durante la transacción.
Conclusión
La estructura básica de un addin de Revit siempre sigue el mismo patrón:
-
Importar namespaces
-
Implementar
IExternalCommand -
Usar
Execute() -
Obtener el documento activo
-
Interactuar con el usuario
-
Modificar el modelo mediante transacciones
Aunque el ejemplo sea sencillo, aquí aparecen muchos de los conceptos fundamentales de la API de Revit:
-
Namespaces
-
Selección
-
References
-
Casting
-
Coordenadas
-
Transacciones
Entender bien esta base hace mucho más fácil desarrollar addins más complejos en el futuro.