Resolving duplicate tags Step 4 – transactions and wrapping up

We’re almost done!

The last thing to mention is that any time we use the API to modify the document, we need to first create and start a transaction. When we are finished making changes we commit the transaction. Here is the final code with the transactions added:

public void MakeMarksUnique()
{
    Document document = this.ActiveUIDocument.Document;
    FilteredElementCollector collector = new FilteredElementCollector(document);
    IList<Element> doorList = collector.OfCategory(BuiltInCategory.OST_Doors).OfClass(typeof(FamilyInstance)).ToList();
    if (doorList.Count > 0)
    {
        IList<string> markValues = new List<string>();
        using (Transaction t = new Transaction(document,"Modify Mark values"))
        {
            t.Start();           
            foreach (Element e in doorList)
            {
                Parameter p = e.get_Parameter("Mark");
                if (markValues.Contains(p.AsString()))
                    p.Set(p.AsString() + "*");
                else
                    markValues.Add(p.AsString());
            }
            t.Commit();
        }
    }
}

And here is what the Revit model looks like after running the command. Note that three of the doors have new mark values and the Undo menu includes the “Modify Mark Values” transaction that was created by the macro.

noduplicates

I hope you enjoyed this first little project and found it interesting and useful. Please leave comments about what you think I should do next!

Advertisements

Resolving duplicate tags Step 3 – get & set parameter values

Now that we can find the doors, the next step is to find the Mark parameter of each one. This is done with get_Parameter(“Mark”) and the AsString() method. The code below will show a dialog box with the mark value for each door in the project.

foreach (Element e in collector.OfCategory(BuiltInCategory.OST_Doors).OfClass(typeof(FamilyInstance)).ToList())
{
    Parameter p = e.get_Parameter("Mark");
    TaskDialog.Show("Revit",p.AsString());
}

Now that we can get the mark of every door, there are multiple ways we could proceed:

  1. Renumber every door, regardless of whether or not it is a duplicate. If we start with doors 1, 2, 3, 1, 2, 3 we could renumber them to 100, 101, 102, 103, 104, 105, 106.
  2. Sequentially renumber the doors that are duplicates. If we start with doors 1, 2, 3, 1, 2, 3 we could leave the first 1, 2, 3 doors as-is and renumber the last 3 doors, resulting in 1, 2, 3, 4, 5, 6. This isn’t too hard in the 1, 2, 3, 1, 2, 3 case, but if we are starting with 1, 5, 8, 9, 1, 2, 5 then it will be a bit trickier to figure out that the 2nd door with number 1 should become #3.
  3. Change the duplicate doors so we have something like 1, 2, 3, 1*, 2*, 3*
  4. Prompt the user for new numbers
  5. etc.

Options 1 & 3 are fairly straightforward, and I will show #3 here. Please leave a comment if you are interested in some of the other solutions.

To implement #3, build a list containing each mark # as it is encountered. Then, if a door’s mark value is already in the list it needs to be changed. If the value is not in the list then it needs to be added to the list.

An empty list is created with

IList<string> markValues = new List<string>();

and the logic to add to the list or set a new value is

if (markValues.Contains(p.AsString()))
  p.Set(p.AsString() + "*");
else
  markValues.Add(p.AsString());

The “+” is the string concatenation operator, so p.AsString() + “*” appends an asterisk at the end of the mark’s existing value.

Resolving duplicate tags Step 2 – finding only family instances

Running the command results in this output. But there are only 6 doors in the model (3 added by each user), so where do the other 7 come from?

typesinstances

Lets look at these 2 lines in more detail:

FilteredElementCollector collector = new FilteredElementCollector(documnent);
IList<Element> elements = collector.OfCategory(BuiltInCategory.OST_Doors).ToList();

The FilteredElementCollector is the class that the Revit API uses to filter and collect elements in the model. The first line is a constructor that creates a new FilteredElementCollector.

The next line uses this collector to create a list of elements that belong to the built-in category OST_Doors. But this does more than get the 6 door instances that we want. It also collects the 7 door family types that are loaded in the model, hence the 13 elements that are in the list.

To restrict the filter to find only family instances, the FilteredElementCollector.OfClass method can be used and the that line re-written to:

IList<Element> elements = collector.OfCategory(BuiltInCategory.OST_Doors).OfClass(typeof(FamilyInstance)).ToList();

With this change, the list will have only the 6 family instances of doors, which is what we want for the next step.

Using the API to solve the Duplicate Type Mark warning

A friend asked how we could use the API to resolve the “Duplicate Type Mark” warnings. This situation occurs when multiple users are adding elements (such as doors) and Revit is automatically numbering the instance parameter “Mark” for each instance. Each independent session of Revit does not know what mark values are being applied in the other sessions of Revit, so the elements in both local files get mark values 1, 2, 3…

doormarks

Then one user saves their work to the Central File. Then the other user saves their work to the Central File. Now the Central File looks like this.

central

and the Review Warnings dialog looks like this

reviewwarnings

You could just ignore these warnings, but your colleagues or CAD Manager might not like that. Anyway, having these warnings cluttering things up can give you that dirty feeling like your Revit model needs to take a shower.

In upcoming posts I will go through the steps of creating an API command that will clean up the model to remove these warnings. I hope you find it interesting and useful!