A Bit of Terminology

A reader was having difficulty compiling source code from a previous post, so I thought I’d post on some basics to explain a few terms and requirements about Revit macros:

A macro is an individual command that can be run in Revit. Here is the source and output from the simplest macro

simple

module is a collection of macros. To create a macro you must first create a module that will contain the macro. The macros in a module do not need to have any functional relationship. They can do completely different things and do not need to interact with each other in any way. You can put 1 macro in each module or 100 macros in each module. If you want to create utility functions (maybe to convert feet to meters or radians to degrees) then those functions need to be in the same module as the macros that use them. Here are a few restrictions:

  1. You cannot have two macros in the same module with the same name
  2. All macros in the same module must compile without errors for you to run any of the macros in that module
  3. Each module opens in its own session of SharpDevelop

module

Module_Startup and Module_Shutdown are special pieces of code that are automatically created by Revit when you create a new module. Their purpose is to give you a way to automatically run code when Revit starts and just before Revit exits.  Module_Startup code is also executed every time you compile the module. By default they are empty and have no code that they run, and in most cases this is fine.

Each module must have one and only one instance of Module_Startup and Module_Shutdown. If you delete them you will get errors like:

The name 'Module_Startup' does not exist in the current context

If you have them more than once (or any other macro more than once) you will get errors like:

Type 'BoostYourBIM1.ThisApplication' already defines a member called 'Module_Startup' with the same parameter types

When a sample on this blog requires Module_Startup code (like https://boostyourbim.wordpress.com/2013/02/14/set-adaptive-component-parameter-values-with-placement-point-coordinates/where it is used to register Dynamic Model Update triggers) I will include the startup code in the blog post. You can copy that startup code into your existing startup method, or replace your existing startup method with this new one. But if you paste the new Module_Startup code and leave your existing Module_Startup code that will cause the error that there is already a Module_Startup in that module.

How to toggle the visibility of Automatic Sketch Dimensions

I was asked if the API could be used to make it easier to toggle the visibility of Temporary Sketch Dimensions. This macro will toggle the state of this subcategory for one or all views in the project.

In future post I will examine how this can be made easier to access by adding the command to the ribbon so it can be invoked with a keyboard shortcut instead of having to access it through the Macros dialog.

public void ToggleAutoSketchDimensionVisibility()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;

    // use the following line to set the visibility for only the active view
    // use the FilteredElementCollector and foreach loop to set it for all views
    // View view = doc.ActiveView;

    Category autoSketchDimCat = doc.Settings.Categories.get_Item(BuiltInCategory.OST_Dimensions).SubCategories.get_Item("Automatic Sketch Dimensions");

    using (Transaction t = new Transaction(doc,"Toggle 'Automatic Sketch Dimensions' Visibility"))
    {
        t.Start();
        foreach (View view in new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>())
        {
            // set the visibility of this category to the opposite of its current value (if it is True, set it to False, etc)
            view.SetVisibility(autoSketchDimCat, !autoSketchDimCat.get_Visible(view));
        }
        t.Commit();
    }
}

GetCellText – Contents of Schedule Cells in 2014

In 2013 we could read the contents of a schedule through a bit of a workaround – export the schedule to a text file with ViewSchedule.Export and read the file back in with StreamReader.

Now in 2014 we can read the cell values directly with ViewSchedule.GetCellText as shown below with a macro that reads the schedule and shows the data in a TaskDialog.

GetCellText
publicvoid Schedule2014GetCellText()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;
    ViewSchedule viewSchedule = doc.ActiveView as ViewSchedule;
    SectionType sectionType = SectionType.Body;

    string data = "";
    for (int i = 0; i < getRowColumnCount(viewSchedule, sectionType, true); i++)
    {
        for (int j = 0; j <  getRowColumnCount(viewSchedule, sectionType, false); j++)
        {
            data += viewSchedule.GetCellText(sectionType, i, j) + ",";
        }
        // remove the trailing "," after the last cell and add a newline for the end of this row
        data = data.TrimEnd(',') + "\n";
    }
    TaskDialog.Show(viewSchedule.Name, data);
}

private int getRowColumnCount(ViewSchedule view, SectionType sectionType, bool countRows)
{
    int ctr = 1;
    // loop over the columns of the schedule
    while (true)
    {
        try // GetCellText will throw an ArgumentException is the cell does not exist
        {
            if (countRows)
                view.GetCellText(sectionType, ctr, 1);
            else
                view.GetCellText(sectionType, 1, ctr);
            ctr++;
        }
        catch (Autodesk.Revit.Exceptions.ArgumentException)
        {
            return ctr;
        }
    }
}

What you can do with View Filters in the 2014 API

Here’s are the new Filter items for the View class in 2014:

  • AddFilter – The filter will be added with default overrides, so there will be no change in the view’s display.
  • GetFilterOverrides – Returns all graphic overrides of a specific filter in the view
  • GetFilters – Returns the ids of the filters applied to the view
  • GetFilterVisibility – Returns true if the elements associated with a specific filter are visible in the view
  • IsFilterApplied – Returns true if a specific filter is applied to the view
  • RemoveFilter
  • SetFilterOverrides – Set the OverrideGraphicSettings for a filter in a view. Adds the filter to the view if it hadn’t been previously added.
  • SetFilterVisibility – Set the visibility of a specific filter (true / false) for a view. Adds the filter to the view if it hadn’t been previously added.
public void Filter2014Sample()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;
    View view = doc.ActiveView;

    TaskDialog.Show("Boost Your BIM","# of filters applied to this view = " + view.GetFilters().Count);

    // create list of categories that will for the filter
    IList<ElementId> categories = new List<ElementId>();
    categories.Add(new ElementId(BuiltInCategory.OST_Walls));

    // create a list of rules for the filter
    IList<FilterRule> rules = new List<FilterRule>();
    // This filter will have a single rule that the wall type width must be less than 1/2 foot
    Parameter wallWidth = new FilteredElementCollector(doc).OfClass(typeof(WallType)).FirstElement().get_Parameter(BuiltInParameter.WALL_ATTR_WIDTH_PARAM);
    rules.Add(ParameterFilterRuleFactory.CreateLessRule(wallWidth.Id, 0.5, 0.001));

    ParameterFilterElement filter = null;
    using (Transaction t = new Transaction(doc, "Create and Apply Filter"))
    {
        t.Start();
        filter =  ParameterFilterElement.Create(doc, "Thin Wall Filter", categories, rules);
        view.AddFilter(filter.Id);
        t.Commit();
    }

    string filterNames = "";
    foreach (ElementId id in view.GetFilters())
    {
        filterNames += doc.GetElement(id).Name + "\n";
    }
    TaskDialog.Show("Boost Your BIM","Filters applied to this view: " + filterNames);

    // Create a new OverrideGraphicSettings object and specify cut line color and cut line weight
    OverrideGraphicSettings ogs = new OverrideGraphicSettings();
    ogs.SetCutLineColor(new Color(255,0,0));
    ogs.SetCutLineWeight(9);

    using (Transaction t = new Transaction(doc, "Set Override Appearance"))
    {
        t.Start();
        view.SetFilterOverrides(filter.Id,ogs);
        t.Commit();
    }
}

Revit 2014 API is going to Blow Your Faces Out!

public void explodeMe()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;

    // Create filter for all walls and roofs (RoofBase is the base class for the ExtrusionRoof & FootprintRoof classes
    ElementClassFilter wallFilter = new ElementClassFilter(typeof(Wall),false);
    ElementClassFilter roofFilter = new ElementClassFilter(typeof(RoofBase),false);
    LogicalOrFilter orFilter = new LogicalOrFilter(wallFilter, roofFilter);

    // explode each element 25 feet from its real location
    double displacementDistance = 25;

    using (Transaction t = new Transaction(doc, "Explode"))
    {
        t.Start();
        foreach (Element e in new FilteredElementCollector(doc).WherePasses(orFilter))
        {
            IList<ElementId> idsToDisplace = new List<ElementId>();
            idsToDisplace.Add(e.Id);

            // explode will offset each element in the direction normal to one of its "major" faces (top face for roof, side face for wall)
            Face face = null;
            HostObject hostObj = e as HostObject;
            if (hostObj is Wall)
            {
                Wall w = hostObj as Wall;

                // only explode the exterior walls
                if (!w.WallType.Name.Contains("Exterior"))
                    continue;

                face = hostObj.GetGeometryObjectFromReference(HostObjectUtils.GetSideFaces(hostObj, ShellLayerType.Exterior).First()) as Face;
            }
            else if (hostObj is RoofBase)
                face = hostObj.GetGeometryObjectFromReference(HostObjectUtils.GetTopFaces(hostObj).First()) as Face;
            // Find the face normal at the center of the face
            BoundingBoxUV bbox = face.GetBoundingBox();
            // center of the face in the UV of the face
            UV center = new UV((bbox.Max.U - bbox.Min.U)/2 + bbox.Min.U, (bbox.Max.V - bbox.Min.V)/2 + bbox.Min.V);
            XYZ faceNormal = face.ComputeNormal(center);

            // inserts are not automatically exploded with their hosts, so find them here and add them to the list
            foreach (FamilyInstance fi in new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>().Where(f => f.Host != null && f.Host.Id == hostObj.Id))
            {
                idsToDisplace.Add(fi.Id);
            }

            DisplacementElement displacementElement = DisplacementElement.Create(doc, idsToDisplace, faceNormal.Multiply(displacementDistance), doc.ActiveView, null);

            // Create a Displacement Path annotation connecting the displaced element with its true location
            Options options = new Options();
            options.ComputeReferences = true;
            Reference edgeRef = null;
            // get a reference to any edge in the displaced element
            foreach (GeometryObject gobj in hostObj.get_Geometry(options))
            {
                if (gobj is Solid)
                {
                    Solid solid = gobj as Solid;
                    Edge edge = solid.Faces.get_Item(0).EdgeLoops.get_Item(0).get_Item(0);
                    edgeRef = edge.Reference;
                    break;
                }
            }
            ElementId pathId = DisplacementPath.Create(doc, displacementElement, edgeRef, 0.5);
        }
        t.Commit();
    }
}

What are the most interesting 2014 API topics?

Here’s an overview of the big new areas of the 2014 API. If you leave some comments about what topics you’d like to learn more about, I will do some posts on those topics.

  • Worksharing – reload latest, synchronize with central, editability of elements and worksets, enabling worksharing
  • RVT Links – selecting elements in links, creating rooms in links, loading, unloading, and more
  • Views – category overrides, filters, non-rectangular crop regions, zoom level control,
  • Views on sheets – rotation, better positioning, revisions
  • Exploded views
  • Macros – API to run, edit, and create macros
  • Import – SAT, SketchUp, DWF files, export to Navis
  • Site – API for toposurface, regions, and building pad
  • Paint tool API
  • Component repeater API
  • Copy/Paste API
  • Running a Revit UI command after an API command completes
  • Project Browser interaction with the API
  • Units – formatting, parsing, and converting
  • Schedules – appearance and formatting

Using the API to resolve “identical instances in same place” warnings

The API can be used to parse a warnings HTML file exported from Revit and then delete the “less desirable” instance in each pair of instances in the same place.

The instance that gets deleted is the one

  • without a tag (if one instance has a tag)
  • with less instance parameter data

New App Availabe for download: In-N-Out Family Parameters

Are you creating or modifying lots of Revit Family content?

Or are you quality checking content made by someone else?

Or just looking for an easy way to review or modify type parameter data from many families at once?

Then check out the “In-N-Out Family Parameters” tool which is now available for download!

Extending the Renumber command for Viewports & Detail Number

Here is a response to the comment from Jeff at https://boostyourbim.wordpress.com/2013/01/17/quick-way-to-renumber-doors-grids-and-levels/ on how to extend that tool to work with the Viewport > Detail Number parameter.

2012 2013
private Parameter getParameterForReference(Document doc, Reference r)
{
    Element e = doc.GetElement(r);
    Parameter p = null;
    if (e is Grid)
        p = e.get_Parameter("Name");
    else if (e is Autodesk.Revit.DB.Architecture.Room)
        p = e.get_Parameter("Number");
    else if (e is FamilyInstance)
        p = e.get_Parameter("Mark");
    else
    {
        p = e.get_Parameter("Detail Number");
        if (p == null)
        {
            TaskDialog.Show("Error", "Unsupported element");
            return null;
        }
    }
    return p;
}
private Parameter getParameterForReference(Document doc, Reference r)
{
    Element e = doc.GetElement(r);
    Parameter p = null;
    if (e is Grid)
        p = e.get_Parameter("Name");
    else if (e is Room)
        p = e.get_Parameter("Number");
    else if (e is FamilyInstance)
        p = e.get_Parameter("Mark");
    else if (e is Viewport) // Viewport class is new to Revit 2013 API
        p = e.get_Parameter("Detail Number");
    else
    {
        TaskDialog.Show("Error","Unsupported element");
        return null;
    }
    return p;
}    

The 2012 code can be used in 2013, but I like the 2013 version a bit better for being more explicit about which classes are being checked.

Document.GetElement – Revit 2012 vs. 2013

I was asked how to modify this line in https://boostyourbim.wordpress.com/2012/12/06/sometimes-in-order-to-create-you-have-to-destroy/ so that it will run in Revit 2012.
GetElement
FamilyInstance famInst = doc.GetElement(id) as FamilyInstance;

In 2012, there was only one way to call the Document.GetElement method and that was with a reference.

In 2013, the input to GetElement can be a string (the element name), the element id, or the element reference. My code used the element id. This is fine for 2013 but not in 2012, where the input needs to be a reference.

Trying to compile the 2013 code in 2012 results in these errors.

Error	1	The best overloaded method match for 'Autodesk.Revit.DB.Document.GetElement(Autodesk.Revit.DB.Reference)' has some invalid arguments	
Error	2	Argument '1': cannot convert from 'Autodesk.Revit.DB.ElementId' to 'Autodesk.Revit.DB.Reference'

The solution here is to use the element id and the Document class’s Element property instead of the GetElement method.

FamilyInstance famInst = doc.get_Element(id) as FamilyInstance;

(Unlike most properties that can be called like Document.Element, this needs to be done as Document.get_Element)

Thanks to Revit Add-Ons for the Editors Choice Award!

Revit Add-ons Editor’s Choice Awards

“I’ve long thought that a method for visually flexing families would make a great add-in. Harry Mattison’s Image-O-Matic is the first and only solution that I know of.”