🏗️ Revit APIintermediateSchedules & Data

Create and Export Revit Schedules Programmatically

Build custom schedules, add fields, apply filters, and export data to Excel using the Revit API.

0 views12/14/2025

Create and Export Revit Schedules Programmatically

Application: Revit API
Category: Schedules & Data
Difficulty: Intermediate
Language: C#

Problem

You need to create schedules programmatically, add fields, apply filters, or export schedule data to Excel for further analysis and reporting.

Solution

Use the ViewSchedule class to create and configure schedules. Schedules are views that display element data in tabular format, with full control over fields, filters, sorting, and formatting.

Code Example: Create Wall Schedule

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Linq;

[Transaction(TransactionMode.Manual)]
public class CreateWallSchedule : IExternalCommand
{
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        
        using (Transaction trans = new Transaction(doc, "Create Wall Schedule"))
        {
            trans.Start();
            
            // Create schedule
            ViewSchedule schedule = ViewSchedule.CreateSchedule(
                doc,
                new ElementId(BuiltInCategory.OST_Walls));
            
            schedule.Name = "Wall Schedule - Custom";
            
            // Get schedulable fields
            SchedulableField typeField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Type");
            
            SchedulableField lengthField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Length");
            
            SchedulableField areaField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Area");
            
            SchedulableField volumeField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Volume");
            
            SchedulableField fireRatingField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Fire Rating");
            
            // Add fields to schedule
            if (typeField != null)
                schedule.Definition.AddField(typeField);
            
            if (lengthField != null)
                schedule.Definition.AddField(lengthField);
            
            if (areaField != null)
                schedule.Definition.AddField(areaField);
            
            if (volumeField != null)
                schedule.Definition.AddField(volumeField);
            
            if (fireRatingField != null)
                schedule.Definition.AddField(fireRatingField);
            
            // Sort by Type
            ScheduleSortGroupField sortField = new ScheduleSortGroupField(
                schedule.Definition.GetFieldId(0)); // First field (Type)
            sortField.ShowHeader = true;
            schedule.Definition.AddSortGroupField(sortField);
            
            // Add filter for exterior walls only
            ScheduleFilter filter = new ScheduleFilter(
                schedule.Definition.GetFieldId(0),
                ScheduleFilterType.Contains,
                "Exterior");
            schedule.Definition.AddFilter(filter);
            
            trans.Commit();
            
            // Open the schedule
            uidoc.ActiveView = schedule;
            
            TaskDialog.Show("Success", "Wall schedule created successfully!");
            
            return Result.Succeeded;
        }
    }
}

Advanced: Export Schedule to Excel

using System.IO;
using System.Text;

[Transaction(TransactionMode.ReadOnly)]
public class ExportScheduleToExcel : IExternalCommand
{
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        
        // Check if current view is a schedule
        if (!(doc.ActiveView is ViewSchedule schedule))
        {
            TaskDialog.Show("Error", "Please open a schedule view first.");
            return Result.Failed;
        }
        
        try
        {
            // Export to CSV (can be opened in Excel)
            string csvPath = @"C:\Temp\" + schedule.Name + ".csv";
            ExportScheduleToCSV(schedule, csvPath);
            
            TaskDialog.Show("Success", $"Schedule exported to:\n{csvPath}");
            
            // Open file in Excel
            System.Diagnostics.Process.Start(csvPath);
            
            return Result.Succeeded;
        }
        catch (System.Exception ex)
        {
            message = ex.Message;
            return Result.Failed;
        }
    }
    
    private void ExportScheduleToCSV(ViewSchedule schedule, string filePath)
    {
        TableData table = schedule.GetTableData();
        TableSectionData section = table.GetSectionData(SectionType.Body);
        
        StringBuilder csv = new StringBuilder();
        
        int rowCount = section.NumberOfRows;
        int colCount = section.NumberOfColumns;
        
        // Export data
        for (int row = 0; row < rowCount; row++)
        {
            List<string> rowData = new List<string>();
            
            for (int col = 0; col < colCount; col++)
            {
                string cellValue = schedule.GetCellText(SectionType.Body, row, col);
                
                // Escape commas and quotes
                if (cellValue.Contains(",") || cellValue.Contains("\""))
                {
                    cellValue = "\"" + cellValue.Replace("\"", "\"\"") + "\"";
                }
                
                rowData.Add(cellValue);
            }
            
            csv.AppendLine(string.Join(",", rowData));
        }
        
        File.WriteAllText(filePath, csv.ToString());
    }
}

Create Room Schedule with Calculated Fields

[Transaction(TransactionMode.Manual)]
public class CreateRoomSchedule : IExternalCommand
{
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        
        using (Transaction trans = new Transaction(doc, "Create Room Schedule"))
        {
            trans.Start();
            
            // Create room schedule
            ViewSchedule schedule = ViewSchedule.CreateSchedule(
                doc,
                new ElementId(BuiltInCategory.OST_Rooms));
            
            schedule.Name = "Room Schedule with Calculations";
            
            // Add fields
            AddScheduleField(doc, schedule, "Number");
            AddScheduleField(doc, schedule, "Name");
            AddScheduleField(doc, schedule, "Area");
            AddScheduleField(doc, schedule, "Level");
            AddScheduleField(doc, schedule, "Department");
            
            // Add calculated field for area in square meters
            ScheduleField areaField = schedule.Definition.GetField(2); // Area field
            
            // Create calculated value field
            ScheduleField calcField = schedule.Definition.AddField(
                ScheduleFieldType.ViewBased);
            calcField.ColumnHeading = "Area (m²)";
            
            // Set formula: Area * 0.092903 (convert sq ft to sq m)
            calcField.SetFormula("Area * 0.092903");
            
            // Format the calculated field
            FormatOptions formatOptions = new FormatOptions();
            formatOptions.UseDefault = false;
            formatOptions.Accuracy = 0.01; // 2 decimal places
            calcField.DisplayType = ScheduleFieldDisplayType.Totals;
            calcField.SetFormatOptions(formatOptions);
            
            // Group by Level
            ScheduleSortGroupField sortField = new ScheduleSortGroupField(
                schedule.Definition.GetFieldId(3)); // Level field
            sortField.ShowHeader = true;
            sortField.ShowFooter = true;
            sortField.ShowBlankLine = true;
            schedule.Definition.AddSortGroupField(sortField);
            
            // Calculate totals
            schedule.Definition.IsItemized = true;
            schedule.Definition.ShowGrandTotal = true;
            schedule.Definition.ShowGrandTotalTitle = true;
            schedule.Definition.GrandTotalTitle = "Total Area";
            
            trans.Commit();
            
            uidoc.ActiveView = schedule;
            
            return Result.Succeeded;
        }
    }
    
    private void AddScheduleField(Document doc, ViewSchedule schedule, string fieldName)
    {
        SchedulableField field = schedule.Definition.GetSchedulableFields()
            .FirstOrDefault(f => f.GetName(doc) == fieldName);
        
        if (field != null)
        {
            schedule.Definition.AddField(field);
        }
    }
}

Read Schedule Data Programmatically

[Transaction(TransactionMode.ReadOnly)]
public class ReadScheduleData : IExternalCommand
{
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        
        if (!(doc.ActiveView is ViewSchedule schedule))
        {
            TaskDialog.Show("Error", "Please open a schedule view first.");
            return Result.Failed;
        }
        
        TableData table = schedule.GetTableData();
        TableSectionData section = table.GetSectionData(SectionType.Body);
        
        int rowCount = section.NumberOfRows;
        int colCount = section.NumberOfColumns;
        
        StringBuilder report = new StringBuilder();
        report.AppendLine($"Schedule: {schedule.Name}");
        report.AppendLine($"Rows: {rowCount}, Columns: {colCount}\n");
        
        // Read header row
        for (int col = 0; col < colCount; col++)
        {
            string header = schedule.GetCellText(SectionType.Header, 0, col);
            report.Append(header.PadRight(20));
        }
        report.AppendLine();
        report.AppendLine(new string('-', colCount * 20));
        
        // Read data rows
        for (int row = 0; row < rowCount; row++)
        {
            for (int col = 0; col < colCount; col++)
            {
                string cellValue = schedule.GetCellText(SectionType.Body, row, col);
                report.Append(cellValue.PadRight(20));
            }
            report.AppendLine();
        }
        
        TaskDialog.Show("Schedule Data", report.ToString());
        
        return Result.Succeeded;
    }
}

Key Points

  • Schedules are views (ViewSchedule inherits from View)
  • Use GetSchedulableFields() to find available fields for a category
  • Calculated fields can use formulas with basic arithmetic
  • Filters support Contains, Equals, GreaterThan, LessThan, etc.
  • Sorting and grouping can show headers, footers, and totals
  • GetCellText() retrieves formatted display values, not raw data

Common Pitfalls

Mistake: Assuming field names are constant

// WRONG - field names vary by language/version
AddScheduleField(doc, schedule, "Area");

// CORRECT - use BuiltInParameter when possible
SchedulableField field = schedule.Definition.GetSchedulableFields()
    .FirstOrDefault(f => f.ParameterId.IntegerValue == (int)BuiltInParameter.ROOM_AREA);

Mistake: Not checking if schedule has data before reading

// WRONG - may throw exception
string value = schedule.GetCellText(SectionType.Body, 0, 0);

// CORRECT - check row count first
TableSectionData section = table.GetSectionData(SectionType.Body);
if (section.NumberOfRows > 0)
{
    string value = schedule.GetCellText(SectionType.Body, 0, 0);
}

Related Topics

  • Schedule templates
  • Multi-category schedules
  • Material takeoff schedules
  • Schedule appearance formatting
  • Schedule on sheets

Code Example

C#
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Linq;

[Transaction(TransactionMode.Manual)]
public class CreateWallSchedule : IExternalCommand
{
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements)
    {
        UIDocument uidoc = commandData.Application.ActiveUIDocument;
        Document doc = uidoc.Document;
        
        using (Transaction trans = new Transaction(doc, "Create Wall Schedule"))
        {
            trans.Start();
            
            // Create schedule
            ViewSchedule schedule = ViewSchedule.CreateSchedule(
                doc,
                new ElementId(BuiltInCategory.OST_Walls));
            
            schedule.Name = "Wall Schedule - Custom";
            
            // Get schedulable fields
            SchedulableField typeField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Type");
            
            SchedulableField lengthField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Length");
            
            SchedulableField areaField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Area");
            
            SchedulableField volumeField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Volume");
            
            SchedulableField fireRatingField = schedule.Definition.GetSchedulableFields()
                .FirstOrDefault(f => f.GetName(doc) == "Fire Rating");
            
            // Add fields to schedule
            if (typeField != null)
                schedule.Definition.AddField(typeField);
            
            if (lengthField != null)
                schedule.Definition.AddField(lengthField);
            
            if (areaField != null)
                schedule.Definition.AddField(areaField);
            
            if (volumeField != null)
                schedule.Definition.AddField(volumeField);
            
            if (fireRatingField != null)
                schedule.Definition.AddField(fireRatingField);
            
            // Sort by Type
            ScheduleSortGroupField sortField = new ScheduleSortGroupField(
                schedule.Definition.GetFieldId(0)); // First field (Type)
            sortField.ShowHeader = true;
            schedule.Definition.AddSortGroupField(sortField);
            
            // Add filter for exterior walls only
            ScheduleFilter filter = new ScheduleFilter(
                schedule.Definition.GetFieldId(0),
                ScheduleFilterType.Contains,
                "Exterior");
            schedule.Definition.AddFilter(filter);
            
            trans.Commit();
            
            // Open the schedule
            uidoc.ActiveView = schedule;
            
            TaskDialog.Show("Success", "Wall schedule created successfully!");
            
            return Result.Succeeded;
        }
    }
}