#RTCEUR Wish 5! View Template (partially) exported

T.R. wished for the ability to “compare two view templates to identify/highlight differences. Or a way to spit them to txt/xls for compare”

We can get the data from all parameters stored as numbers, text, and element ids. We can also query the properties of the View class. In the sample code, this is done using reflection so code does not need to be written for each property individually.

Also, GetNonControlledTemplateParameterIds returns a list of parameters that are not marked as included when this view is used as a template. GetTemplateParameterIds returns a list of parameter ids that may be controlled when this view is assigned as a template.

Sample output of all parameter values:

------PARAMETERS---------
Category,-2000279
Category,-2000279
Color Scheme Location,1
Design Option,-1
Detail Level,3
Discipline,1
Display Model,0
Edited by,
Family,
Family,
Family and Type,
Family and Type,
Family Name,
Far Clipping,0
Learning Content,
Parts Visibility,1
Phase Filter,375
Scale Value    1:,100
Show Hidden Lines,1
Sun Path,0
Type,
Type Name,
View Scale,100
View Template,-1
Workset,172
------PROPERTIES---------
AnalysisDisplayStyleId,-1
AreAnalyticalModelCategoriesHidden,False
AreAnnotationCategoriesHidden,False
AreImportCategoriesHidden,False
AreModelCategoriesHidden,False
ArePointCloudsHidden,False
AssemblyInstanceId,-1
AssociatedAssemblyInstanceId,-1
CanBePrinted,False
CreatedPhaseId,-1
DemolishedPhaseId,-1
GroupId,-1
Id,9610
IsAssemblyView,False
IsTemplate,True
IsValidObject,True
LevelId,-1
Name,Architectural Section
OwnerViewId,-1
Pinned,False
RevealConstraintsMode,False
Title,Architectural Section
UniqueId,96dd64a0-c5ef-4337-8f9b-658b5c420b33-0000258a
ViewName,Architectural Section
ViewSpecific,False
ViewTemplateId,-1
---------------
Enable Sketchy Lines,False
Sketchy Line Extension,0
Sketchy Line Jitter,0
public void exportViewTemplate()
{
    Document doc = this.ActiveUIDocument.Document;
    string templateName = "Architectural Section";
    View template = new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>()
        .FirstOrDefault(q => q.IsTemplate == true && q.Name == templateName);
    
    using (StreamWriter sw = new StreamWriter(@"C:\Users\harry_000\Desktop\rtc\View Template Export - " + templateName + ".txt"))
    {
        // quick and easy solution to get data from simple parameters
        sw.WriteLine("------PARAMETERS---------");
        foreach (Parameter p in template.Parameters.Cast<Parameter>().OrderBy(q => q.Definition.Name))
        {
            string val = "<undefined>";
            if (p.StorageType == StorageType.Double)
                val = p.AsDouble().ToString();
            else if (p.StorageType == StorageType.Integer)
                val = p.AsInteger().ToString();
            else if (p.StorageType == StorageType.ElementId)
                val = p.AsElementId().IntegerValue.ToString();
            else if (p.StorageType == StorageType.String)
                val = p.AsString();
            
            if (val != "<undefined>")
                sw.WriteLine(p.Definition.Name + "," + val);
        }
        
        // use reflection to get values from all properties of the view class
        sw.WriteLine("------PROPERTIES---------");
        foreach (System.Reflection.PropertyInfo pi in template.GetType().GetProperties().OrderBy(q => q.Name))
        {
            string propertyName = "";
            string propertyValue = "";
            getPropertyNameAndValue(template, pi, out propertyName, out propertyValue);
            
            if (propertyValue != "<undefined>")
                sw.WriteLine(propertyName + "," + propertyValue);
        }
        
        sw.WriteLine("---------------");
        
        // complex parameters require more work to export values individually
        ViewDisplaySketchyLines sketchyLines = template.GetSketchyLines();
        sw.WriteLine("Enable Sketchy Lines," + sketchyLines.EnableSketchyLines.ToString());
        sw.WriteLine("Sketchy Line Extension," + sketchyLines.Extension.ToString());
        sw.WriteLine("Sketchy Line Jitter," + sketchyLines.Jitter.ToString());
        
    }
}

private void getPropertyNameAndValue(object o, System.Reflection.PropertyInfo pi, out string propertyName, out string propertyValue)
{
    propertyName = pi.Name;
    propertyValue = "<undefined>";
    if (pi.PropertyType == typeof(ElementId) ||
        pi.PropertyType == typeof(Boolean) ||
        pi.PropertyType == typeof(String) ||
        pi.PropertyType == typeof(Enum))
    {
        try
        {
            propertyValue = pi.GetValue(o).ToString();
        }
        catch
        {}
    }
}

#RTCEUR Wish 4 granted! Change parameter value in multiple families

Doug asked ” How about a routine to change the value of a shared parameter in all families in a folder to the same specified value.”

public void setParamInFamilies()
{
    Application app = this.Application;
    foreach (string filename in Directory.GetFiles(@"C:\Users\harry_000\Desktop\rtc", "*.rfa"))
    {
        Document doc = app.OpenDocumentFile(filename);
        using (Transaction t = new Transaction(doc, "Set param value"))
        {
            t.Start();
            
            // get the parameter to set
            FamilyParameter param = doc.FamilyManager.get_Parameter("RTC Parameter");
            
            if (param == null)
                continue;
            
            // loop through all types in the family
            foreach (FamilyType ftype in doc.FamilyManager.Types)
            {
                // set the parameter value for this type
                doc.FamilyManager.Set(param, "Budapest");
            }
            t.Commit();
            doc.Save();
            doc.Close(false);
        }
    }
}

Code from today’s #RTCEUR advanced API course

Today’s course focused on how to use custom classes, overrides, custom enums, source code control (with https://bitbucket.org & https://www.sourcetreeapp.com) and more. Below is the code we developed during the class.

public void groupAudit()
{
    Document doc = this.ActiveUIDocument.Document;
    
    List<Group> groups = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>().ToList();
    List<GroupType> groupTypes = new FilteredElementCollector(doc).OfClass(typeof(GroupType)).Cast<GroupType>().ToList();
    
    List<Tuple<string, int, int>> groupData = new List<Tuple<string, int, int>>();
    
//			using (StreamWriter sw = new StreamWriter(@"C:\Users\harry_000\Desktop\RTC_EUR_2015-2015-10-28\RTC EUR 2015\Advanced API\\output.txt"))
//			{
        foreach (GroupType groupType in groupTypes)
        {
            //int numberOfElements = group.GetMemberIds().Count;
            
            int numberOfElementsInGroupType = 0;
            
//				foreach (Element element in new FilteredElementCollector(doc).OfClass(typeof(Group)))
//				{
//					Group g = element as Group;
//				}
            
            // old way without LINQ
//				foreach (Group group in new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>())
//				{
//					if (group.GroupType.Id == groupType.Id)
//					{
//						countA = group.GetMemberIds().Count;
//						continue;
//					}
//				}
            
            // with LINQ - better
            foreach (Group group in new FilteredElementCollector(doc).OfClass(typeof(Group))
                     .Cast<Group>()
                     .Where(g => g.GroupType.Id == groupType.Id))
            {
                numberOfElementsInGroupType = group.GetMemberIds().Count;
            }
            
            int numberOfGroupInstances = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>()
                .Where(g => g.GroupType != null && g.GroupType.Id == groupType.Id).Count();
            
            TaskDialog.Show("number", groupType.Name + " = " + numberOfElementsInGroupType.ToString()
                           + Environment.NewLine 
                          + numberOfGroupInstances);
            
            // not good to mix collecting the data and using the data in the same loop
            // instead, better to collect the data, store it, and then use it later
            //sw.WriteLine(groupType.Name + ", " + numberOfGroupInstances + "," + numberOfElementsInGroupType);
            
            groupData.Add(new Tuple<string, int, int>(groupType.Name, numberOfGroupInstances, numberOfElementsInGroupType));
            
            //int count = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>()
        }
        
        //writeTupleToFile(groupData);

        
//	}
}

public void getOfficeGroupInfo()
{
    Document doc = this.ActiveUIDocument.Document;
    namedGroupDataImplementation(doc, GroupNamePrefix.Office);
}

public void getKitchenGroupInfo()
{
    Document doc = this.ActiveUIDocument.Document;
    namedGroupDataImplementation(doc, GroupNamePrefix.Kitchen);
}


public void namedGroupDataImplementation(Document doc, GroupNamePrefix prefix)
{
    
}

public enum GroupNamePrefix
{
Office,
Kitchen,
Bath
};

public void getDetailGroupInfo()
{
    Document doc = this.ActiveUIDocument.Document;
    groupDataImplementation(doc, BuiltInCategory.OST_IOSDetailGroups);
}

public void getModelGroupInfo()
{
    Document doc = this.ActiveUIDocument.Document;
    groupDataImplementation(doc, BuiltInCategory.OST_IOSModelGroups);
}

public void groupDataImplementation(Document doc, BuiltInCategory cat)
{
    
    List<Group> groups = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>().ToList();
    List<GroupType> groupTypes = new FilteredElementCollector(doc).OfClass(typeof(GroupType)).Cast<GroupType>()
        .ToList();
    
    // store data in a list of GroupData objects instead of a Tuple
    List<GroupData> groupData = new List<GroupData>();
    int numberOfElementsInGroupType = 0;
    foreach (GroupType groupType in groupTypes)
    {
        // homework - use the temporary transaction trick for group types with no group instances
// http://thebuildingcoder.typepad.com/blog/2012/11/temporary-transaction-trick-touchup.html				
        
        if (new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>()
            .FirstOrDefault(q => q.GroupType.Id == groupType.Id && q.Category.Id.IntegerValue == (int)cat) == null)
            continue;
        
        foreach (Group group in new FilteredElementCollector(doc).OfClass(typeof(Group))
                 .Cast<Group>()
                 .Where(g => g.GroupType.Id == groupType.Id))
        {
            numberOfElementsInGroupType = group.GetMemberIds().Count;
            
            foreach (ElementId id in group.GetMemberIds())
            {
                Element e = doc.GetElement(id);
            }
            
        }
        
        int numberOfGroupInstances = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>()
            .Where(g => g.GroupType != null && g.GroupType.Id == groupType.Id).Count();
        
        groupData.Add(new GroupData(groupType.Name, numberOfGroupInstances, numberOfElementsInGroupType));
    }
    writeGroupDataToFile(groupData);
}



public void groupAuditUsingCustomClassToStoreData()
{
    Document doc = this.ActiveUIDocument.Document;
    
    List<Group> groups = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>().ToList();
    List<GroupType> groupTypes = new FilteredElementCollector(doc).OfClass(typeof(GroupType)).Cast<GroupType>().ToList();
    
    // store data in a list of GroupData objects instead of a Tuple
    List<GroupData> groupData = new List<GroupData>();
    int numberOfElementsInGroupType = 0;
    foreach (GroupType groupType in groupTypes)
    {
        foreach (Group group in new FilteredElementCollector(doc).OfClass(typeof(Group))
                 .Cast<Group>()
                 .Where(g => g.GroupType.Id == groupType.Id))
        {
            numberOfElementsInGroupType = group.GetMemberIds().Count;
        }
        
        int numberOfGroupInstances = new FilteredElementCollector(doc).OfClass(typeof(Group)).Cast<Group>()
            .Where(g => g.GroupType != null && g.GroupType.Id == groupType.Id).Count();
        
//				GroupData data = new GroupData();
//				data.name = groupType.Name;
//				data.numberOfElements = numberOfElementsInGroupType;
//				data.numberOfInstances = numberOfGroupInstances;
//				groupData.Add(data);
        
        //GroupData groupDataWithConstructor = new GroupData(groupType.Name, numberOfGroupInstances, numberOfElementsInGroupType);
        groupData.Add(new GroupData(groupType.Name, numberOfGroupInstances, numberOfElementsInGroupType));
        
        //groupData.Add(new Tuple<string, int, int>(groupType.Name, numberOfGroupInstances, numberOfElementsInGroupType));
    }
        
    writeGroupDataToFile(groupData);
    
    
}

public class GroupData
{
    public GroupData() {}
    
    public GroupData(string _Name, int NumberOfInstances, int NumberOfElements)
    {
        name = _Name;
        numberOfInstances = NumberOfInstances;
        numberOfElements = NumberOfElements;
    }
    public string name;
    public int numberOfInstances;
    public int numberOfElements;
}

private void writeGroupDataToFile(List<GroupData> data)
{
    // now use the data that was previously stored
    
    string filename = @"C:\Users\harry_000\Desktop\RTC_EUR_2015-2015-10-28\RTC EUR 2015\Advanced API\\output.txt";
    using (StreamWriter sw = new StreamWriter(filename))
    {
        foreach (GroupData gd in data)
        {
            sw.WriteLine(gd.name + "," + gd.numberOfElements + "," + gd.numberOfInstances);
        }
    }
    Process.Start(filename);
}

private void writeTupleToFile(List<Tuple<string, int, int>> data)
{
    // now use the data that was previously stored
    using (StreamWriter sw = new StreamWriter(@"C:\Users\harry_000\Desktop\RTC_EUR_2015-2015-10-28\RTC EUR 2015\Advanced API\\output.txt"))
    {
        foreach (Tuple<string, int, int> tup in data)
        {
            sw.WriteLine(tup.Item1 + "," + tup.Item2 + "," + tup.Item3);
        }
    }
}


private void writeTupleToFile(List<Tuple<string, int, int, int>> data)
{
    // now use the data that was previously stored
    using (StreamWriter sw = new StreamWriter(@"C:\Users\harry_000\Desktop\RTC_EUR_2015-2015-10-28\RTC EUR 2015\Advanced API\\output.txt"))
    {
        foreach (Tuple<string, int, int, int> tup in data)
        {
            sw.WriteLine(tup.Item1 + "," + tup.Item2 + "," + tup.Item3 + "," + tup.Item4);
        }
    }
}

#RTCEUR Wish 3 continued & discussed – automatic revision clouds

In the previous post I showed how the API can create a revision and cloud for elements selected by the user. Now, what if you wanted the revisioning to be done automatically every time the user makes a change, addition, or deletion?

It works nicely when the table is moved or a new, wall is created that does not join with other walls.

But what happens when a window is moved or a wall that joins to other walls is created? Too many elements are included in the revision cloud – not good! This is because when the user modifies an element hosted by the wall, Revit makes changes to both the hosted element (such as the door) and also the wall that hosts the door. The same issue comes up when a new wall joins with existing walls – they all are changed. GetModifiedElementIds does not let us differentiate between the element that the user changed and the elements that Revit changed because of the user change.

It might be possible to get the selected element using the Idling event, store its element id, and then in the updater only create the revision cloud around this element if it was modified.

Automatically creating a revision cloud around an element’s former location after it has been deleted is even harder. GetDeletedElementIds gives the ids of the deleted elements, but because they are deleted they no longer have a bounding box or location that you can get to define the curves of the revision cloud. To do this you would need to store the id and bounding box coordinates of every element in the model, and then retrieve that data after the element is deleted. If smoke starts coming out of your computer from doing this, don’t say I didn’t warn you.

public class AutoRevisionCloudUpdater : IUpdater
{
    static AddInId m_appId;
    static UpdaterId m_updaterId;
    public AutoRevisionCloudUpdater(AddInId id)
    {
        m_appId = id;
        m_updaterId = new UpdaterId(m_appId, new Guid("1BF2F6A2-4C06-42d4-97C1-D1B4EB593EFF"));
    }
    public void Execute(UpdaterData data)
    {
        Document doc = data.GetDocument();
        
        // create list of all modified and created elements
        List<ElementId> ids = data.GetModifiedElementIds().ToList();
        ids.AddRange(data.GetAddedElementIds().ToList());
        
        foreach (ElementId id in ids)
        {        
            makeRevCloud(doc, id);
        }
    }
    public string GetAdditionalInformation(){return "Auto Revision Cloud";}
    public ChangePriority GetChangePriority(){return ChangePriority.FloorsRoofsStructuralWalls;}
    public UpdaterId GetUpdaterId(){return m_updaterId;}
    public string GetUpdaterName(){return "Auto Revision Cloud";}
    
    private void makeRevCloud(Document doc, ElementId id)
    {
        Element e = doc.GetElement(id);
            
        // create new revision
        Revision rev = Revision.Create(doc);
        rev.RevisionDate = DateTime.Now.ToLongDateString();
        rev.Description = id.IntegerValue.ToString();
        
        // use bounding box of element and offset to create curves for revision cloud
        BoundingBoxXYZ bbox = e.get_BoundingBox(doc.ActiveView);
        List<Curve> curves = new List<Curve>();
        double offset = 2;
        XYZ pt1 = bbox.Min.Subtract(XYZ.BasisX.Multiply(offset)).Subtract(XYZ.BasisY.Multiply(offset));
        XYZ pt2 = new XYZ(bbox.Min.X - offset, bbox.Max.Y + offset, 0);
        XYZ pt3 = bbox.Max.Add(XYZ.BasisX.Multiply(offset)).Add(XYZ.BasisY.Multiply(offset));;
        XYZ pt4 = new XYZ(bbox.Max.X + offset, bbox.Min.Y - offset, 0);
        curves.Add(Line.CreateBound(pt1, pt2));
        curves.Add(Line.CreateBound(pt2, pt3));
        curves.Add(Line.CreateBound(pt3, pt4));
        curves.Add(Line.CreateBound(pt4, pt1));
        
        // create revision cloud
        RevisionCloud cloud = RevisionCloud.Create(doc, doc.ActiveView, rev.Id, curves);
    }
}

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

    AutoRevisionCloudUpdater updater = new AutoRevisionCloudUpdater(this.Application.ActiveAddInId);
    UpdaterRegistry.RegisterUpdater(updater);
    UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), new ElementClassFilter(typeof(FamilyInstance)), Element.GetChangeTypeGeometry());
    UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), new ElementClassFilter(typeof(FamilyInstance)), Element.GetChangeTypeElementAddition());
    UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), new ElementClassFilter(typeof(Wall)), Element.GetChangeTypeGeometry());
    UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), new ElementClassFilter(typeof(Wall)), Element.GetChangeTypeElementAddition());
}

#RTCEUR Wish 3 granted (part 1)! Multiple Element Revision Creation

Barrie asked: “Can you rev cloud all change elements in views on sheets and write update to rev cloud comments for tagging?”

This post shows how to prompt the user to select multiple elements then create a new revisions and a revision cloud around each element. A following post will discuss to what extent this could be automated to create revisions every time the model changes.

public void makeRevisionAndCloud()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = this.ActiveUIDocument.Document;
    Application app = this.Application;
    
    // get elements from user selection
    List<Element> elements = new List<Element>();
    
    // string of ids of the selected elements to use for Revision description
    string description = "";
    foreach (Reference r in uidoc.Selection.PickObjects(ObjectType.Element))
    {
        elements.Add(doc.GetElement(r));
        description += r.ElementId.IntegerValue + " ";
    }
    
    using (Transaction t = new Transaction(doc, "Make Revision & Cloud"))
    {
        t.Start();
        
        // create new revision
        Revision rev = Revision.Create(doc);
        rev.RevisionDate = DateTime.Now.ToShortDateString();
        rev.Description = description;
        rev.IssuedBy = app.Username;
            
        // use bounding box of element and offset to create curves for revision cloud
        foreach (Element e in elements)
        {
            BoundingBoxXYZ bbox = e.get_BoundingBox(doc.ActiveView);
            List<Curve> curves = new List<Curve>();
            double offset = 2;
            XYZ pt1 = bbox.Min.Subtract(XYZ.BasisX.Multiply(offset)).Subtract(XYZ.BasisY.Multiply(offset));
            XYZ pt2 = new XYZ(bbox.Min.X - offset, bbox.Max.Y + offset, 0);
            XYZ pt3 = bbox.Max.Add(XYZ.BasisX.Multiply(offset)).Add(XYZ.BasisY.Multiply(offset));;
            XYZ pt4 = new XYZ(bbox.Max.X + offset, bbox.Min.Y - offset, 0);
            curves.Add(Line.CreateBound(pt1, pt2));
            curves.Add(Line.CreateBound(pt2, pt3));
            curves.Add(Line.CreateBound(pt3, pt4));
            curves.Add(Line.CreateBound(pt4, pt1));
            
            // create revision cloud
            RevisionCloud cloud = RevisionCloud.Create(doc, doc.ActiveView, rev.Id, curves);
            
            // set Comments of revision cloud
            cloud.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)
                .Set(e.GetType().Name + "-" + e.Name + ": " + doc.Application.Username);
            
            // tag the revision cloud
            IndependentTag tag = doc.Create.NewTag(doc.ActiveView, cloud, true, TagMode.TM_ADDBY_CATEGORY, TagOrientation.Horizontal, pt3);
            tag.TagHeadPosition = pt3.Add(new XYZ(2,2,0));
        }
        t.Commit();
    }
}

Code from this morning’s class at #RTCEUR

Not at #RTCEUR?

Did something else with your morning?

Came to the class and would like the source code?

public void CountElements()
{
    try
    {
        Document doc = this.ActiveUIDocument.Document;
        ICollection<Element> walls = new FilteredElementCollector(doc)
            .OfCategory(BuiltInCategory.OST_Walls).WhereElementIsCurveDriven().ToList();
        // TaskDialog.Show("Count", "# of Walls " + walls.Count);
        
        string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        
        // these two lines do the same thing
        string otherway = desktop + @"\" + @"RTC_EUR_2015-2015-10-28\RTC EUR 2015\You can be a Revit Programmer\output.";
		string pathToFile = Path.Combine(desktop, @"RTC_EUR_2015-2015-10-28\RTC EUR 2015\You can be a Revit Programmer\output.");
		
		//using (StreamWriter writer = new StreamWriter(@"C:\Users\harry_000\Desktop\RTC_EUR_2015-2015-10-28\RTC EUR 2015\You can be a Revit Programmer\output.", true))
		using (StreamWriter writer = new StreamWriter(pathToFile, true))
		{
			// Write doesn't add a new line at the end, WriteLine does
			// or Environment.NewLine to insert a line break
		    writer.WriteLine(walls.Count);
		    
		    foreach (Element element in walls) {
		    	
		    	writer.Write(element.Name + " " + element.Id.IntegerValue);
		    	// need to cast from Element to Wall
		    	
		    	Wall wall = element as Wall;
		    	
		    	
		    	Parameter paramVolume = wall.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED);
		    	StorageType storageVolume = paramVolume.StorageType;
		    	
		    	// match the method (AsDouble, AsInteger) with how the value is stored
		    	double volume = paramVolume.AsDouble();
		    	volume = UnitUtils.ConvertFromInternalUnits(volume, DisplayUnitType.DUT_CUBIC_METERS);
		    	writer.WriteLine(" " + wall.Width + " " + volume);
		    	
		    	writer.WriteLine();
		    	
		    }
		    
		}
	}
	catch (Exception ex)
	{
		TaskDialog.Show("",ex.Message);
	}
		
}

#RTCEUR Wish #2 Granted! Rename views

Jason asked “How about a find / replace tool for view names?”

Wish granted!

public void viewRename()
{
    string find = "Level";
    string replace = "LEV";
    
    Document doc = this.ActiveUIDocument.Document;
    string errors = "";
    using (Transaction t = new Transaction(doc, "Rename views"))
    {
        t.Start();
        // find all views whose name contains the "find" string
        foreach (Element view in new FilteredElementCollector(doc).OfClass(typeof(View))
                 .Where(q => q.Name.Contains(find)))
        {
            try
            {
                view.Name = view.Name.Replace(find, replace);
            }
            catch // error handling if there is already a view with what would be the new name
            {
                errors += view.Name + ", ";
            }
        }
        t.Commit();
     }
    
    // show error dialog if there are errors
    if (errors != "")
    {
        TaskDialog td = new TaskDialog("Error");
        td.MainInstruction = "Could not rename views because duplicate names would be created";
        td.MainContent = errors;
        td.Show();
    }
}

#RTCEUR Wish 1 granted!

Chris asked how the API could help copy a legend to multiple sheets. Wish granted!

public void legendOnSheets()
{
    Document doc = this.ActiveUIDocument.Document;
    
    // create list of element ids for the sheets that will get the legends
    List<ElementId> sheetIds = new List<ElementId>();
        
    // use SheetNumber to find the desired sheets
    // add each sheet to the list of sheet ids
    sheetIds.Add(new FilteredElementCollector(doc)
        .OfClass(typeof(ViewSheet))
        .Cast<ViewSheet>()
        .FirstOrDefault(q => q.SheetNumber == "A101").Id);
    sheetIds.Add(new FilteredElementCollector(doc)
        .OfClass(typeof(ViewSheet))
        .Cast<ViewSheet>()
        .FirstOrDefault(q => q.SheetNumber == "A102").Id);                
    sheetIds.Add(new FilteredElementCollector(doc)
        .OfClass(typeof(ViewSheet))
        .Cast<ViewSheet>()
        .FirstOrDefault(q => q.SheetNumber == "A103").Id);    
    
    // find the legend to put on the sheets
    // use ViewType.Legend and the view name to find the legend view
    Element legend = new FilteredElementCollector(doc)
        .OfClass(typeof(View))
        .Cast<View>()
        .FirstOrDefault(q => q.ViewType == ViewType.Legend && q.Name == "Legend 1");
    
    // create a transaction so that the document can be modified
    using (Transaction t = new Transaction(doc, "Legends on Sheets"))
    {
        // start the transaction
        t.Start();
        
        // loop through the list of sheet ids
        foreach (ElementId sheetid in sheetIds)
        {
            // create a new viewport for the legend on each sheet
            // place the legend view at the 0,0,0 location on each sheet
            // user will need to move the legend to the desired location
            Viewport.Create(doc, sheetid, legend.Id, new XYZ(0,0,0));
        }
        
        // commit the changes
        t.Commit();
    }
}

It’s #RTCEUR Wish List Time

Revit Technology Conference Europe is here, so its time for Boost Your BIM to grant your API wishes? What is this all about? Check out https://boostyourbim.wordpress.com/category/api-wish/rtc/ to see previous wishes granted. Submit yours as comments here or tweet to @BoostYourBIM with hastag #RTCEUR