Guy Robinson recently explained an easy way to access all elements in a schedule.
Victor Chekalin picked up and expanded on that.
Here is his solution to retrieve all schedules in a view and elements from a schedule. In his own words:
One wonderful day I had to retrieve schedules which are presented on a sheet:
At first I looked at the Revit API Help but unfortunately there is no method such as GetSchdulesOnView in the ViewSheet class. There is no explicit way to get it. After a couple of hours trying to achieve that I need I encountered Guy's simple solution to access all elements in a schedule. The solution is use FilteredElementCollector and pass in the element id of the view in which you want to collect elements.
I created the following simple extension method GetSchedules to get schedules on view:
public static class ViewSheetExtensions { public static IEnumerable<ViewSchedule> GetSchedules( this ViewSheet viewSheet ) { var doc = viewSheet.Document; FilteredElementCollector collector = new FilteredElementCollector( doc, viewSheet.Id ); var scheduleSheetInstances = collector .OfClass( typeof( ScheduleSheetInstance ) ) .ToElements() .OfType<ScheduleSheetInstance>(); foreach( var scheduleSheetInstance in scheduleSheetInstances ) { var scheduleId = scheduleSheetInstance .ScheduleId; if( scheduleId == ElementId.InvalidElementId ) continue; var viewSchedule = doc.GetElement( scheduleId ) as ViewSchedule; if( viewSchedule != null ) yield return viewSchedule; } } }
It can be used like this:
var schedules = viewSheet .GetSchedules() .ToList(); foreach( var viewSchedule in schedules ) { // Do something }
Here is the demo result:
But I decided not to stop and go deeper. The next step is to get all elements which are involved in a schedule. The approach is the same as with schedules. Using FiltereredElementCollector but instead of the ViewSheet element id we pass in the ViewSchedule id.
The extension method is similar:
public static class ViewScheduleExtensions { public static IEnumerable<ElementId> GetElementIdsInSchedule( this ViewSchedule viewSchedule ) { var doc = viewSchedule.Document; FilteredElementCollector collector = new FilteredElementCollector( doc, viewSchedule.Id ); var elementIds = collector .WhereElementIsNotElementType() .ToElementIds(); return elementIds; } }
But there is an additional little nuance. If I retrieve elements from a material takeoff schedule, the method will return all materials in the project together with other elements. The solution is to just skip all elements which are materials:
foreach( var id in elementIds ) { var element = doc.GetElement( id ); if( element is Material ) continue; // Do something }
Here is the result of my demo project:
I attached the demo Visual Studio solution and Revit project.
I hope this information will useful for developers.
Have a nice day,
Victor.
Hi Jeremy,
Nice work, but i have a question. It's possible to hide the schedules of project using API?..
Thanks!
Posted by: santosbj | February 16, 2013 at 18:34
Dear Santos,
Yes, of course! You can simply delete them, just like any other element, can't you?
Cheers, Jeremy.
Posted by: Jeremy Tammik | September 29, 2013 at 10:34
Hi Jeremy, I have a question.
Is it possible to retrieve the viewsheet from a ScheduleSheetInstance without iterating through all viewsheets?
I have 500 sheets and 7 ScheduleSheetInstance. It takes ten minutes to retrieve the seven ScheduleSheetInstances.
Thanks!
José Ignacio Montes
Posted by: José Ignacio Montes | February 12, 2014 at 08:41
Ok, I´ve got it:
I only have to ask the ScheduleSheetInstance for its sheet through the property OwnerViewId;
I have 500 sheets and only 7 schedules. Goes a lot faster than going through all sheets:
ScheduleSheetInstance vsch =
viewSchedule as ScheduleSheetInstance;
ElementId shid = vsch.OwnerViewId;
Posted by: Jose Ignacio Montes | February 12, 2014 at 13:33
Dear Jeremy,
How would you go about retrieving only the materials that are scheduled in a material takeoff schedule?
As you said above, inserting the ViewScheduleId as a filter in an elementscollector, in case of a material takeoff, retrieves the scheduled elements but also the entire material library..
This is annoying as I would like to make unique records of 'ElementId + MaterialId' for each entry in each material takeoff in the project, to check if the Revit user hasn't created duplicate measurements. This happens and can be quite a costly error.
Thanks in advance for any help or direction,
Kind regards,
Paulus
Posted by: Paulus Present | June 10, 2014 at 04:53
I got it to work, but it is quite ugly code, making use of some collectors and filters (if anyone cares to see it, be my guest =).
It would be a lot easier and save a lot of trouble if the API would be reprogrammed to only include the scheduled materials instead of the entire library.
There's no point in having the entire material library as the result of a schedulefilter, as you can get all materials simply by applying an OffCategory filter to a collector.
Kind regards,
Paulus
Posted by: Paulus Present | June 20, 2014 at 06:25
Dear Paulus,
Thank you for letting us know.
Yes indeed, I would love to take a look.
Maybe we can find some room for improvement and possibly even simplification.
Can you share it as a gist or something?
https://gist.github.com
Thank you!
Cheers, Jeremy.
Posted by: Jeremy Tammik | June 25, 2014 at 16:57