Working with ViewSchedule

The ScheduleDefinition class helps define the ViewSchedule.

The ScheduleDefinition class contains various settings related to the contents of a schedule view, including:

Most schedules contain a single ScheduleDefinition which is retrieved via the ViewSchedule.Definition property. In Revit, schedules of certain categories can contain an "embedded schedule" containing elements associated with the elements in the primary schedule, for example a room schedule showing the elements inside each room or a duct system schedule showing the elements associated with each system. An embedded schedule has its own category, fields, filters, etc. Those settings are stored in a second ScheduleDefinition object. When present, the embedded ScheduleDefinition is obtained from the ScheduleDefinition.EmbeddedDefinition property.

Adding Fields

Once a ViewSchedule is created, fields can be added. The ScheduleDefinition.GetSchedulableFields() method will return a list of SchedulableField objects representing the non-calculated fields that may be included in the schedule. A new field can be added from a SchedulableField object or using a ScheduleFieldType. The following table describes the options available from the ScheduleFieldType enumeration.

Member name Description
Instance An instance parameter of the scheduled elements. All shared parameters also use this type, regardless of whether they are instance or type parameters.
ElementType A type parameter of the scheduled elements.
Count The number of elements appearing on the schedule row.
ViewBased

A specialized type of field used for a few parameters whose displayed values can change based on the settings of the view:

  • ROOM_AREA and ROOM_PERIMETER in room and space schedules.
  • PROJECT_REVISION_REVISION_NUM in revision schedules.
  • KEYNOTE_NUMBER in keynote legends that are numbered by sheet.
Formula A formula calculated from the values of other fields in the schedule.
Percentage A value indicating what percent of the total of another field each element represents.
Room A parameter of the room that a scheduled element belongs to.
FromRoom A parameter of the room on the "from" side of a door or window.
ToRoom A parameter of the room on the "to" side of a door or window.
ProjectInfo A parameter of the Project Info element in the project that the scheduled element belongs to, which may be a linked file. Only allowed in schedules that include elements from linked files.
Material In a material takeoff, a parameter of one of the materials of a scheduled element.
MaterialQuantity In a material takeoff, a value representing how a particular material is used within a scheduled element. The parameter ID can be MATERIAL_AREA, MATERIAL_VOLUME, or MATERIAL_ASPAINT.
RevitLinkInstance A parameter of the RevitLinkInstance that an element in a linked file belongs to. Currently RVT_LINK_INSTANCE_NAME is the only supported parameter. Only allowed in schedules that include elements from linked files.
RevitLinkType A parameter of the RevitLinkType that an element in a linked file belongs to. Currently RVT_LINK_FILE_NAME_WITHOUT_EXT is the only supported parameter. Only allowed in schedules that include elements from linked files.
StructuralMaterial A parameter of the structural material of a scheduled element.
Space A parameter of the space that a scheduled element belongs to.

Using one of the ScheduleDefinition.AddField() methods will add the field to the end of the field list. To place a new field in a specific location in the field list, use one of the ScheduleDefinition.InsertField() methods. Fields can also be ordered after the fact using ScheduleDefinition.SetFieldOrder().

The following is a simple example showing how to add fields to a view if they are not already in the view schedule.

Code Region: Adding fields to a schedule

/// <summary>
/// Add fields to view schedule.
/// </summary>
/// <param name="schedules">List of view schedule.</param>
public void AddFieldToSchedule(List<ViewSchedule> schedules)
{
    IList<SchedulableField> schedulableFields = null;

    foreach (ViewSchedule vs in schedules)
    {
        //Get all schedulable fields from view schedule definition.
        schedulableFields = vs.Definition.GetSchedulableFields();

        foreach (SchedulableField sf in schedulableFields)
        {
            bool fieldAlreadyAdded = false;
            //Get all schedule field ids
            IList<ScheduleFieldId> ids = vs.Definition.GetFieldOrder();
            foreach (ScheduleFieldId id in ids)
            {
                //If the GetSchedulableField() method of gotten schedule field returns same schedulable field,
                // it means the field is already added to the view schedule.
                if (vs.Definition.GetField(id).GetSchedulableField() == sf)
                {
                    fieldAlreadyAdded = true;
                    break;
                }
            }

            //If schedulable field doesn't exist in view schedule, add it.
            if (fieldAlreadyAdded == false)
            {
                vs.Definition.AddField(sf);
            }
        }
    }
}

The ScheduleField class represents a single field in a ScheduleDefinition's list of fields. Each (non-hidden) field becomes a column in the schedule.

Most commonly, a field represents an instance or type parameter of elements appearing in the schedule. Some fields represent parameters of other related elements, like the room to which a scheduled element belongs. Fields can also represent data calculated from other fields in the schedule, specifically Formula and Percentage fields.

The ScheduleField class has properties to control column headings, both the text as well as the orientation. Column width and horizontal alignment of text within a column can also be defined.

The ScheduleField.IsHidden property can be used to hide a field. A hidden field is not displayed in the schedule, but it can be used for filtering, sorting, grouping, and conditional formatting and can be referenced by Formula and Percentage fields.

DisplayType

The ScheduleField has a DisplayType property which indicates the display type for the field. Possible values are:
  • Standard - Nothing is displayed if the values of the elements are different, otherwise, the common value will be displayed
  • Totals -Calculates and displays the total value
  • MinMax - Calculates and displays the minimum and maximum values
  • Min -Calculates and displays the maximum value
  • Max - Calculates and displays the minimum value

The ScheduleField.CanDisplayMinMax() method indicates whether this field can display minimum and maximum values.

In a non-itemized schedule, values for the non-Standard display types are displayed in regular rows when multiple elements appear on the same row.

Style and Formatting of Fields

ScheduleField.GetStyle() and ScheduleField.SetStyle() use the TableCellStyle class to work with the style of fields in a schedule. Using SetStyle(), various attributes of the field can be set, including the line style for the border of the cell as well as the text font, color and size.

ScheduleField.SetFormatOptions() and ScheduleField.GetFormatOptions() use the FormatOptions class to work with the formatting of a field's data. The FormatOptions class contains settings that control how to format numbers with units as strings. It contains those settings that are typically chosen by an end user in the Format dialog and stored in the document.

In the following example, all length fields in a ViewSchedule are formatted to display in feet and fractional inches.

Code Region: Formatting a field

// format length units to display in feet and inches format
public void FormatLengthFields(ViewSchedule schedule)
{
    int nFields = schedule.Definition.GetFieldCount();
    for (int n = 0; n < nFields; n++)
    {
        ScheduleField field = schedule.Definition.GetField(n);
        if (field.UnitType == UnitType.UT_Length)
        {
            FormatOptions formatOpts = new FormatOptions();
            formatOpts.UseDefault = false;
            formatOpts.DisplayUnits = DisplayUnitType.DUT_FEET_FRACTIONAL_INCHES;
            field.SetFormatOptions(formatOpts);
        }
    }
}

The example below applies both formatting and style overrides to a given field.

Code Region: Apply formatting and style overrides to field

public static void ApplyFormattingToField(ViewSchedule schedule, int fieldIndex)
{
    // Get the field.
    ScheduleDefinition definition = schedule.Definition;
    ScheduleField field = definition.GetField(fieldIndex);

    // Build unit formatting for the field.
    FormatOptions options = field.GetFormatOptions();
    options.UseDefault = false;
    options.DisplayUnits = DisplayUnitType.DUT_SQUARE_INCHES;
    options.UnitSymbol = UnitSymbolType.UST_IN_SUP_2;

    // Build style overrides for the field
    // Use override options to indicate fields that are overridden and apply changes
    TableCellStyle style = field.GetStyle();
    TableCellStyleOverrideOptions overrideOptions = style.GetCellStyleOverrideOptions();
    overrideOptions.BackgroundColor = true;
    style.BackgroundColor = new Color(0x00, 0x00, 0xFF);
    overrideOptions.FontColor = true;
    style.TextColor = new Color(0xFF, 0xFF, 0xFF);
    overrideOptions.Italics = true;
    style.IsFontItalic = true;

    style.SetCellStyleOverrideOptions(overrideOptions);

    double width = field.GridColumnWidth;

    using (Transaction t = new Transaction(schedule.Document, "Set style etc"))
    {
        t.Start();
        field.SetStyle(style);
        field.SetFormatOptions(options);
        // Change column width (affects width in grid and on sheet) - units are in Revit length units - ft.
        field.GridColumnWidth = width + 0.5;
        t.Commit();
    }
}

Title and Headers

Display of the schedule title and/or headers is optional. Whether the title or headers are shown can be controlled with the ScheduleDefinition properties ShowTitle and ShowHeaders.

Grouping and Sorting in Schedules

A schedule may be sorted or grouped by one or more of the schedule's fields. Several methods can be used to control grouping and sorting of fields. The ScheduleSortGroupField class represents one of the fields that the schedule is sorted or grouped by. Sorting and grouping are related operations. In either case, elements appearing in the schedule are sorted based on their values for the field by which the schedule is sorted/grouped, which automatically causes elements with identical values to be grouped together. By enabling extra header, footer, or blank rows, visual separation between groups can be achieved.

If the ScheduleDefinition.IsItemized property is false, elements having the same values for all of the fields used for sorting/grouping will be combined onto the same row. Otherwise the schedule displays each element on a separate row

A schedule can be sorted or grouped by data that is not displayed in the schedule by marking the field used for sorting/grouping as hidden using the ScheduleField.IsHidden property.

Code Region: Add grouping/sorting to schedule

public static void AddGroupingToSchedule(ViewSchedule schedule, BuiltInParameter paramEnum, bool withTotalsAndDecoration, ScheduleSortOrder order)
{
    // Find field 
    ScheduleField field = FindField(schedule, paramEnum);

    if (field == null)
        throw new Exception("Unable to find field.");

    // Build sort/group field.
    ScheduleSortGroupField sortGroupField = new ScheduleSortGroupField(field.FieldId, order);
    if (withTotalsAndDecoration)
    {
        sortGroupField.ShowFooter = true;
        sortGroupField.ShowFooterTitle = true;
        sortGroupField.ShowFooterCount = true;
        sortGroupField.ShowHeader = true;
        sortGroupField.ShowBlankLine = true;
    }

    // Add the sort/group field
    ScheduleDefinition definition = schedule.Definition;

    using (Transaction t = new Transaction(schedule.Document, "Add sort/group field"))
    {
        t.Start();
        definition.AddSortGroupField(sortGroupField);
        t.Commit();
    }
}

public static ScheduleField FindField(ViewSchedule schedule, BuiltInParameter paramEnum)
{
    ScheduleDefinition definition = schedule.Definition;
    ScheduleField foundField = null;
    ElementId paramId = new ElementId(paramEnum);

    foreach (ScheduleFieldId fieldId in definition.GetFieldOrder())
    {
        foundField = definition.GetField(fieldId);
        if (foundField.ParameterId == paramId)
        {
            return foundField;
        }
    }

    return null;
}

Headers can also be grouped. ViewSchedule.GroupHeaders() method can be used to specify which rows and columns to include in a grouping of the header section. The last parameter is a string for the caption of the grouped rows and columns.

In the following example, columns are grouped for a newly created single-category schedule.

Code Region: Grouping headers

public static void CreateSingleCategoryScheduleWithGroupedColumnHeaders(Document doc)
{
    using (Transaction t = new Transaction(doc, "Create single-category with grouped column headers"))
    {
        // Build the schedule
        t.Start();
        ViewSchedule vs = ViewSchedule.CreateSchedule(doc, new ElementId(BuiltInCategory.OST_Windows));

        AddRegularFieldToSchedule(vs, new ElementId(BuiltInParameter.WINDOW_HEIGHT));
        AddRegularFieldToSchedule(vs, new ElementId(BuiltInParameter.WINDOW_WIDTH));
        AddRegularFieldToSchedule(vs, new ElementId(BuiltInParameter.ALL_MODEL_MARK));
        AddRegularFieldToSchedule(vs, new ElementId(BuiltInParameter.ALL_MODEL_COST));

        doc.Regenerate();

        // Group the headers in the body section using ViewSchedule methods
        vs.GroupHeaders(0, 0, 0, 1, "Size");
        vs.GroupHeaders(0, 2, 0, 3, "Other");
        vs.GroupHeaders(0, 0, 0, 3, "All");

        t.Commit();
    }
}

public static void AddRegularFieldToSchedule(ViewSchedule schedule, ElementId paramId)
{
    ScheduleDefinition definition = schedule.Definition;

    // Find a matching SchedulableField
    SchedulableField schedulableField =
        definition.GetSchedulableFields().FirstOrDefault<SchedulableField>(sf => sf.ParameterId == paramId);

    if (schedulableField != null)
    {
        // Add the found field
        definition.AddField(schedulableField);
    }
}

Filtering

A ScheduleFilter can be used to filter the elements that will be displayed in a schedule. A filter is a condition that must be satisfied for an element to appear in the schedule. All filters must be satisfied for an element to appear in the schedule.

A schedule can be filtered by data that is not displayed in the schedule by marking the field used for filtering as hidden using the ScheduleField.IsHidden property.

Code Region: Add filter to schedule

public static void AddFilterToSchedule(ViewSchedule schedule, ElementId levelId)
{
    // Find level field
    ScheduleDefinition definition = schedule.Definition;

    ScheduleField levelField = FindField(schedule, BuiltInParameter.ROOM_LEVEL_ID);

    // Add filter
    using (Transaction t = new Transaction(schedule.Document, "Add filter"))
    {
        t.Start();

        // If field not present, add it
        if (levelField == null)
        {
            levelField = definition.AddField(ScheduleFieldType.Instance, new ElementId(BuiltInParameter.ROOM_LEVEL_ID));
        }

        // Set field to hidden
        levelField.IsHidden = true;
        ScheduleFilter filter = new ScheduleFilter(levelField.FieldId, ScheduleFilterType.Equal, levelId);
        definition.AddFilter(filter);

        t.Commit();
    }
}

/// <summary>
/// Finds an existing ScheduleField matching the given parameter
/// </summary>
/// <param name="schedule"></param>
/// <param name="paramEnum"></param>
/// <returns></returns>
public static ScheduleField FindField(ViewSchedule schedule, BuiltInParameter paramEnum)
{
    ScheduleDefinition definition = schedule.Definition;
    ScheduleField foundField = null;
    ElementId paramId = new ElementId(paramEnum);

    foreach (ScheduleFieldId fieldId in definition.GetFieldOrder())
    {
        foundField = definition.GetField(fieldId);
        if (foundField.ParameterId == paramId)
        {
            return foundField;
        }
    }

    return null;
}

Working with Schedule Data

The following example shows how to determine the list of elements in a schedule.

Code Region: Get a schedule's contents

public static void GetScheduleContents(ViewSchedule viewSchedule)
{
    // Collect types displayed in the schedule
    FilteredElementCollector typeCollector = new FilteredElementCollector(viewSchedule.Document, viewSchedule.Id);
    typeCollector.WhereElementIsElementType();

    int numberOfTypes = typeCollector.Count();

    // Collect instances displayed in the schedule
    FilteredElementCollector instCollector = new FilteredElementCollector(viewSchedule.Document, viewSchedule.Id);
    instCollector.WhereElementIsNotElementType();

    int numberOfInstances = instCollector.Count();

    TaskDialog.Show("Elements in schedule", String.Format("Types {0} instances {1}", numberOfTypes, numberOfInstances));
}

To work with the actual data in the schedule, the ViewSchedule.GetTableData() returns a TableData object that holds most of the data that describe the style and contents of the rows, columns, and cells in a table. More information can be found under TableView and TableData.