When views are placed on a ViewSheet in Revit, we can access the views using the ViewSheet.GetAllViewports(). However, if a schedule is placed on a viewsheet, it does not get listed using GetAllViewports(). How can we list the Schedule views that are placed on Viewsheet.
One solution to this requirement seems to be is to fake a deletion process. When an element is deleted in Revit, it also returns a list of some other elements that are linked/related to this element and will get deleted as well. These elements are the ones that are linked to or share some relationship to the element that is being deleted. This helps establish a parent-child relationship between the element that is being deleted (parent) and all the elements that get deleted (child) as a consequence of the deletion of the parent. For example, if we delete a wall, all the doors and windows that were placed on the wall get deleted too. Infact, so does the Door and Window tags as well, since they are in turn linked to Door and Window instances, which were linked to the Wall they were placed on. Using the API, when we encapsulate this deletion step in a transaction and later roll back the transaction and thus not affect the model at all.
A standalone tool what was developed on this principle can be accessed from this blog post in Jeremy’s The Building Coder blog.
Using this same approach, when I tried to delete a ViewSheet instance, it deletes the views that were placed on it – including the ScheduleSheetInstance. On the API front, following the same principle, I access the ViewSheet in a model, encapsulate a deletion process of the ViewSheet using a transaction, get the Ids of elements that are returned from Document.Delete(). And then roll back the transaction so that the model remains unaffected. Once I have the Ids of the elements that get deleted on ViewSheet deletion, I can iterate through each of the elements to check which ones are of ScheduleSheetInstance type and those elements would be the Schedules that were placed on that specific ViewSheet.
The code snippet which shows the entire workflow is included here. Please note that if you end up using this approach, you have to ensure that the active view is not the ViewSheet itself – since deleting the active view is not permissible from the User Interface either.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace Revit.SDK.Samples.HelloRevit.CS
{
[Transaction(TransactionMode.Manual)]
public class Command : IExternalCommand
{
public Result Execute(ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
UIApplication uiApp = commandData.Application;
Document doc = uiApp.ActiveUIDocument.Document;
// Find view family type
FilteredElementCollector vftCollector =
new FilteredElementCollector(doc);
vftCollector.OfClass(typeof(ViewSheet));
//for simplicity, lets take one ViewSheet only
ViewSheet vs =
vftCollector.Cast<ViewSheet>().First();
ICollection<ElementId> ids = null;
String msg = String.Empty;
using (Transaction trans =
new Transaction(doc, "TempTransaction"))
{
trans.Start();
// Ensure that ViewSheet is not the active view
// before performing the delete task
ids = doc.Delete(vs);
trans.RollBack();
}
if (null != ids)
{
foreach (ElementId id in ids)
{
ScheduleSheetInstance schInstance =
doc.GetElement(id) as ScheduleSheetInstance;
if (null != schInstance)
{
msg = msg + vs.ViewName
+ " viewsheet contains "
+ schInstance.Name
+ " schedule with ID: "
+ schInstance.Id.ToString()
+ "\n";
}
}
}
TaskDialog.Show("Results", msg);
return Result.Succeeded;
}
}
}