This C# ASP.NET application demonstrates one way to implement automatic date adjustment. Both the code below and its associated “AdjustTaskDates.aspx” file should be installed and configured to run under the URL: http://[your SharePoint server] /_layouts/ custom/ AdjustTaskDates. Note that the parent .aspx file will not contain any web controls, as the end-user will never interact with this application. The AdjustTaskDates application will simply execute the following code, then immediately redirect the end-user’s browser back to the task list’s default page.

// These are the required .NET library
// references + Microsoft.SharePoint +
// Microsoft.SharePoint.WebControls. You
// will need to create a reference to the
// Windows.SharePoint.Services .NET library
// to include the last two
using System;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
 
public partial class _Default : System.Web.UI.Page
{
    // LAG_DAYS ensures that there will be
    // at least one day delay between a
    // predecessor and its successor task
    const int LAG_DAYS = 1;
 
    // This page assumes that the following querystring
    // parameters have been passed:
    //
    // l = Guid (unique ID) of the task list
    // id (optional) = integer uniqu ID of task to process
    //
    protected void Page_Load(object sender, EventArgs e)
    {
        // Get handle to web site and enable updates
        SPWeb web = SPControl.GetContextWeb(Context);
        web.AllowUnsafeUpdates = true;
 
        // Get handle to task list to be processed
        SPList tasks = web.Lists[new Guid(Request.QueryString["l"])];
        SPQuery query = new SPQuery();
        SPListItemCollection taskItems = tasks.Items;
 
        // If this routine has been called from the
        // item-level menu, get the ID of the task from
        // which is was called
        int taskId = 0;
        try
        {
            if (Request.QueryString["id"] != null)
            {
                // Instead of processing all tasks, only process
                // the specific task calling this routine
                taskId = int.Parse(Request.QueryString["id"].ToString());
                query.Query = “<Where><Eq><FieldRef Name=’ID’/>” +
                    “<Value Type=’Number’>” + taskId + “</Value></Eq></Where>”;
                taskItems = tasks.GetItems(query);
            }
        }
        catch
        {}
 
        // Start by clearing out the processing notes field, so
        // that the only notes to be displayed after processing
        // is complete will be for the current run
        foreach (SPListItem task in taskItems)
        {
            if (task["Processing Notes"] != null && task["Processing Notes"].ToString() != “”)
            {
                task["Processing Notes"] = “”;
                task.Update();
            }
        }
 
        // Initialize working variables
        bool datesChanged = true;
        int overlap;
        DateTime startDate;
        DateTime dueDate;
 
        // Loop until no more dates need to be changed
        while (datesChanged)
        {
            // Clear flag that indicates whether
            // any date changes were made
            datesChanged = false;
 
            // Loop through the task(s) list, changing any dates as necessary
            foreach (SPListItem task in taskItems)
            {
                // The dependency behavior will control how dates
                // are adjusted for the current task. Values are:
                //
                // Ignore Dependencies … no change
                // No Earlier Than ……. only move if predecessor overlaps
                // As Early as Possible .. compress dates if possible
                string dependencyBehavior = “”;
                try
                {
                    dependencyBehavior = task["Dependency Behavior"].ToString();
                }
                catch { }
 
                // Get the current task’s LAG DAYS, if it has not been set,
                // use the default.
                int lagDays = LAG_DAYS;
                try
                {
                    if (task["Lag Days"] != null)
                    {
                        lagDays = int.Parse(task["Lag Days"].ToString());
                    }
                }
                catch { }
 
                // Skip current task if its dependency behavior is set
                // to “Ignore Dependencies”
                if (dependencyBehavior != “Ignore Dependencies”)
                {
                    // Get any perdecessor tasks for the current task,
                    // note that this field can have multiple values
                    SPFieldLookupValueCollection predecessors =
                     (SPFieldLookupValueCollection)task["Predecessors"];
 
                    // Get dates of current task
                    startDate = (DateTime)task["Start Date"];
                    try
                    {
                        dueDate = (DateTime)task["Due Date"];
                    }
                    catch
                    {
                        // If due date has not been filled in, assume
                        // it’s the same as the start date
                        dueDate = startDate;
                    }
 
 
                    // If one or more predecessors exist, parse and check their end dates
                    DateTime maxPredecessorDueDate = DateTime.MinValue;
                    int maxPredecessorId = 0;
                    string maxPredecessorTitle = “”;
 
                    for (int i = 0; i < predecessors.Count; i++)
                    {
                        // Get handle to predecessor task
                        int id = predecessors[i].LookupId;
                        SPListItem predecessor = tasks.GetItemById(id);
 
                        // Get the due (end) date of the predecessor
                        DateTime predecessorDueDate = (DateTime)predecessor["Due Date"];
 
                        // Store the latest predecessor due date for use
                        // later if current task’s dependency behavior is
                        // set to “As Early as Possible”
                        if (predecessorDueDate > maxPredecessorDueDate)
                        {
                            maxPredecessorDueDate = predecessorDueDate;
                            maxPredecessorId = predecessor.ID;
                            maxPredecessorTitle = predecessor.Title;
                        }
 
                        // Calculate the current overlap of the predecessor due
                        // date and current task start date. Add in the lag days.
                        overlap = (predecessorDueDate – startDate).Days + lagDays;
 
                        // If predecessor overlaps with current task, move the
                        // current task out by the amount of the overlap
                        if (overlap > 0)
                        {
                            // Move the dependent task and set the processing
                            // comment
                            startDate = startDate.AddDays(overlap);
                            dueDate = dueDate.AddDays(overlap);
                            task["Start Date"] = startDate;
                            task["Due Date"] = dueDate;
                            task["Processing Notes"]
                                = string.Format(“Task moved to start no earlier than end of task
                                  # {0}: \”{1}\”.” ,
                                    predecessor.ID.ToString(),
                                    predecessor.Title);
 
                            // Because dates were changed, will need to loop through
                            // tasks again, in case this change will impact other
                            // tasks dependent on the task that was just moved.
                            datesChanged = true;
                        }
                    }
 
                    // If dependency behavior is set to “As Early as Possible”, set
                    // start date of current task to be no later than latest predecessor
                    // plus lag days
                    overlap = (maxPredecessorDueDate – startDate).Days + lagDays;
                    if (dependencyBehavior == “As Early as Possible”
                     && maxPredecessorDueDate != DateTime.MinValue && overlap < 0)
                    {
                        startDate = startDate.AddDays(overlap);
                        dueDate = dueDate.AddDays(overlap);
                        task["Start Date"] = startDate;
                        task["Due Date"] = dueDate;
                        task["Processing Notes"]
                            = string.Format(“Task moved to start no later than end of task # {0}:
                                  \”{1}\”.” ,
                                maxPredecessorId.ToString(),
                                maxPredecessorTitle);
 
                        datesChanged = true;
                    }
                }
 
                // Save changes
                task.Update();
            }
        }
 
        // Return to the task list after processing has been completed
        Response.Redirect(tasks.DefaultViewUrl);
    }

}