Building a Construction Schedule Daily Report with Silverlight (Part 1)

Recently, a client requested a construction schedule report that could visually display the implementation timeline of various plans while allowing modifications. I decided to build this using Silverlight. The result looks like this:

Users can mark items as completed through the context menu's "Done" option, and use left-click to select cells for setting time periods. How is the data passed into this Silverlight application? The application defines these classes:

namespace ScheduleViewer
{
    /// <summary>
    /// Schedule data container
    /// </summary>
    public class ScheduleData
    {
        public List<ScheduleItem> Items { get; set; }
        /// <summary>
        /// Collection of schedule ranges
        /// </summary>
        public List<ScheduleRange> Ranges { get; set; }
    }

    /// <summary>
    /// Individual schedule item
    /// </summary>
    public class ScheduleItem
    {
        /// <summary>
        /// Schedule name
        /// </summary>
        public string Name { get; set; }
    }

    /// <summary>
    /// Schedule time range
    /// </summary>
    public class ScheduleRange
    {
        /// <summary>
        /// Description
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// Start date
        /// </summary>
        public DateTime? StartDate { get; set; }
        /// <summary>
        /// End date
        /// </summary>
        public DateTime? EndDate { get; set; }
        /// <summary>
        /// Minimum allowed date
        /// </summary>
        public DateTime? MinDate { get; set; }
        /// <summary>
        /// Maximum allowed date
        /// </summary>
        public DateTime? MaxDate { get; set; }
        /// <summary>
        /// Read-only flag
        /// </summary>
        public bool IsReadOnly { get; set; }
        /// <summary>
        /// Allow dates beyond current time
        /// </summary>
        public bool AllowFutureDates { get; set; }
        /// <summary>
        /// Has been edited
        /// </summary>
        public bool IsModified { get; set; }
        /// <summary>
        /// Is completed
        /// </summary>
        public bool IsCompleted { get; set; }
        /// <summary>
        /// Allow cancellation
        /// </summary>
        public bool AllowUndo { get; set; }
        /// <summary>
        /// Allow empty value (required field validation)
        /// </summary>
        public bool IsRequired { get; set; }
        /// <summary>
        /// Custom metadata
        /// </summary>
        public string Metadata { get; set; }
    }
}

This class provides granular control for each data row (time row). For example, whether its read-only, whether it has been edited, whether it can be empty (not selected), weather it can be cancelled (when marked as complete), maximum and minimum allowed values, and whether the end date is allowed to exceed the current date. While this class primarily handles row-level data control, there are also global controls for the entire report. These include date formats (day, week, month) (this needs further improvement), start time, number of date columns, number of text columns, schedule headers, and date column background colors, among others. Essentially, this schedule is highly customizable and can be displayed according to specific data requirements.

For example, the test data is constructed as follows:

public string InitializationParameters { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        try
        {
            InitializationParameters = "Title=Construction Schedule,DateColumnCount=80,ItemHeaders=Task Name,Data=";

            List<ScheduleData> scheduleList = new List<ScheduleData>();
            PopulateScheduleData(scheduleList);
            InitializationParameters += HttpUtility.UrlEncode(JsonConvert.SerializeObject(scheduleList, Formatting.Indented));
        }
        catch (Exception ex)
        {
            Logger.LogError(ex);
        }
    }
}

private static void PopulateScheduleData(List<ScheduleData> scheduleList)
{
    ScheduleData item1 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Light Steel Partition Walls" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now, EndDate = DateTime.Now.AddDays(3), Description = "Baseline", IsReadOnly = true },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(1), EndDate = DateTime.Now.AddDays(4), Description = "Planned", AllowFutureDates = true },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(2), EndDate = DateTime.Now.AddDays(5), Description = "Actual", IsCompleted = true, IsRequired = true }
        }
    };
    scheduleList.Add(item1);

    ScheduleData item2 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Decorative Stone & Painting" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(5), EndDate = DateTime.Now.AddDays(16), Description = "Planned", AllowFutureDates = true },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(4), EndDate = DateTime.Now.AddDays(15), Description = "Actual" }
        }
    };
    scheduleList.Add(item2);

    ScheduleData item3 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Gypsum Board Ceiling" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(5), EndDate = DateTime.Now.AddDays(18), Description = "Baseline", IsReadOnly = true, AllowFutureDates = true },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(8), EndDate = DateTime.Now.AddDays(12), Description = "Planned" }
        }
    };
    scheduleList.Add(item3);

    ScheduleData item4 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Whitewash & Latex Paint" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(15), EndDate = DateTime.Now.AddDays(18), Description = "Baseline", IsReadOnly = true },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(19), EndDate = DateTime.Now.AddDays(25), Description = "Planned" },
            new ScheduleRange() { StartDate = DateTime.Now, EndDate = DateTime.Now.AddDays(5), Description = "Actual" }
        }
    };
    scheduleList.Add(item4);

    ScheduleData item5 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Floor Installation" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(3), EndDate = DateTime.Now.AddDays(5), Description = "Baseline" },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(6), EndDate = DateTime.Now.AddDays(15), Description = "Planned" },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(7), EndDate = DateTime.Now.AddDays(19), Description = "Actual" }
        }
    };
    scheduleList.Add(item5);

    ScheduleData item6 = new ScheduleData()
    {
        Items = new List<ScheduleItem>()
        {
            new ScheduleItem() { Name = "Test Item 1" }
        },
        Ranges = new List<ScheduleRange>()
        {
            new ScheduleRange() { StartDate = DateTime.Now, EndDate = DateTime.Now, Description = "Baseline" },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(6), EndDate = DateTime.Now.AddDays(15), Description = "Planned" },
            new ScheduleRange() { StartDate = DateTime.Now.AddDays(79), EndDate = DateTime.Now.AddDays(79), Description = "Actual" }
        }
    };
    scheduleList.Add(item6);
}

Tags: Silverlight WPF gantt-chart construction-scheduling data-visualization

Posted on Fri, 16 Oct 2026 16:28:24 +0000 by Apollo_Ares