Application: Revit API
Category: Schedules & Data
Difficulty: Intermediate
Language: C#
You need to create schedules programmatically, add fields, apply filters, or export schedule data to Excel for further analysis and reporting.
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.
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;
}
}
}
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());
}
}
[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);
}
}
}
[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;
}
}
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);
}
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;
}
}
}