Features

  • ASP.NET Gantt Control
  • Customizable scale - we are displaying one cell per day
  • Customizable time header - we are displaying days grouped by month
  • Adding a new task using a modal dialog
  • Editing a task using a modal dialog
  • PNG Export
  • PDF Export
  • Customizable PDF page format (Letter, A4)
  • Customizable PDF page orientation (portrait, landscape)

License

Licensed for testing and evaluation purposes. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for ASP.NET WebForms. Buy a license.

Gantt ASP.NET Control

gantt-asp.net-control-setup.png

We will create the Gantt chart using DayPilot ASP.NET scheduler control. Add the DayPilotScheduler control to the web page.

  • Switch to the Gantt chart mode using ViewType="Gantt". The Gantt mode will display one task per row. The rows will be generated from the supplied event set automatically.
  • Set the scale (cell duration) using Scale="Day".
  • Define the time headers using TimeHeaders. We will display months in the first row and days (as defined using Scale property) in the second header row.
  • Define the row header columns using HeaderColumns. We will use the columns to display additional data about the Gantt tasks (such as duration).
<DayPilot:DayPilotScheduler 
  runat="server" 
  ID="GanttControl"
  ViewType="Gantt"
  Scale="Day"
  >
  <TimeHeaders>
      <DayPilot:TimeHeader GroupBy="Month" Format="MMMM yyyy" />
      <DayPilot:TimeHeader GroupBy="Cell" />
  </TimeHeaders>
  <HeaderColumns>
      <DayPilot:RowHeaderColumn Title="Task" Width="120" />
      <DayPilot:RowHeaderColumn Title="Duration" Width="90" />
  </HeaderColumns>
</DayPilot:DayPilotScheduler>

Loading the Gantt Tasks

Load the event/task data to the Gantt control. You can load events from any data sources like List, DataTable, SqlDataSource, etc.. We will use a simple DataTable returned by GetData() method.

C#

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
      SetDataSourceAndBind();
  }

}

private void SetDataSourceAndBind()
{
  GanttControl.DataSource = GetData(GanttControl.StartDate, GanttControl.EndDate);
  GanttControl.DataStartField = "eventstart";
  GanttControl.DataEndField = "eventend";
  GanttControl.DataIdField = "id";
  GanttControl.DataTextField = "name";
  GanttControl.DataBind();

}

private DataTable GetData(DateTime start, DateTime end)
{
  SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM [event] WHERE NOT (([eventend] <= @start) OR ([eventstart] >= @end))", ConfigurationManager.ConnectionStrings["daypilot"].ConnectionString);
  da.SelectCommand.Parameters.AddWithValue("start", start);
  da.SelectCommand.Parameters.AddWithValue("end", end);

  DataTable dt = new DataTable();
  da.Fill(dt);

  return dt;
}

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
  If Not IsPostBack Then
    SetDataSourceAndBind()
  End If

End Sub

Private Sub SetDataSourceAndBind()
  GanttControl.DataSource = GetData(GanttControl.StartDate, GanttControl.EndDate)
  GanttControl.DataStartField = "eventstart"
  GanttControl.DataEndField = "eventend"
  GanttControl.DataIdField = "id"
  GanttControl.DataTextField = "name"
  GanttControl.DataBind()

End Sub

Private Function GetData(ByVal start As Date, ByVal [end] As Date) As DataTable
  Dim da As New SqlDataAdapter("SELECT * FROM [event] WHERE NOT (([eventend] <= @start) OR ([eventstart] >= @end))", ConfigurationManager.ConnectionStrings("daypilot").ConnectionString)
  da.SelectCommand.Parameters.AddWithValue("start", start)
  da.SelectCommand.Parameters.AddWithValue("end", [end])

  Dim dt As New DataTable()
  da.Fill(dt)

  Return dt
End Function

SQL Server Database Schema for the Gantt Data

gantt-asp.net-sql-schema.png

Our sample uses a very simple database structure. All Gantt chart data are stored in a single database table called [event] with four fields:

  • [id]
  • [name]
  • [eventstart]
  • [eventend]

The SQL schema is as follows:

CREATE TABLE [dbo].[event] (
  [id]         INT          IDENTITY (1, 1) NOT NULL,
  [name]       VARCHAR (50) NULL,
  [eventstart] DATETIME     NOT NULL,
  [eventend]   DATETIME     NOT NULL,
  CONSTRAINT [PK_event] PRIMARY KEY CLUSTERED ([id] ASC)
);

You can use your existing database schema and map the data set fields using Data*Field properties.

C#

private void SetDataSourceAndBind()
{
  GanttControl.DataSource = GetData(GanttControl.StartDate, GanttControl.EndDate);

  GanttControl.DataStartField = "eventstart";
  GanttControl.DataEndField = "eventend";
  GanttControl.DataIdField = "id";
  GanttControl.DataTextField = "name";

  GanttControl.DataBind();
}

VB

Private Sub SetDataSourceAndBind()
  GanttControl.DataSource = GetData(GanttControl.StartDate, GanttControl.EndDate)

  GanttControl.DataStartField = "eventstart"
  GanttControl.DataEndField = "eventend"
  GanttControl.DataIdField = "id"
  GanttControl.DataTextField = "name"

  GanttControl.DataBind()
End Sub

Visible Date Range

gantt-asp.net-visible-range.png

You can define the visible range using StartDate and Days properties.

We are displaying the current month.

C#

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
      GanttControl.StartDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
      GanttControl.Days = DateTime.DaysInMonth(DateTime.Today.Year, DateTime.Today.Month);
      SetDataSourceAndBind();
  }
}

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
  If Not IsPostBack Then
    GanttControl.StartDate = New Date(Date.Today.Year, Date.Today.Month, 1)
    GanttControl.Days = Date.DaysInMonth(Date.Today.Year, Date.Today.Month)
    SetDataSourceAndBind()
  End If
End Sub

Adding Tasks to the Gantt Chart

gantt-asp.net-control-add-task.png

We will use a modal dialog to display a new task dialog.

<a href="javascript:add()" id="add">Add Task...</a>&nbsp;

<script type="text/javascript">
  function add() {
      var start = new DayPilot.Date().getDatePart();
      var end = start.addDays(1);
      modal().showUrl('New.aspx?start=' + start + '&end=' + end);
  }
  function modal() {
      var m = new DayPilot.Modal();
      m.closed = function () {
          var data = this.result;
          console.log(data);
          if (data == "OK") {
              GanttControl.commandCallBack("refresh");
          }
      };
      return m;
  }
</script>

It is a standalone page (New.aspx). It creates a new record in the database and updates the main page using the client side .closed callback.

C#

protected void ButtonOK_Click(object sender, EventArgs e)
{
  DateTime start = Convert.ToDateTime(TextBoxStart.Text);
  DateTime end = Convert.ToDateTime(TextBoxEnd.Text);

  DbInsertEvent(start, end, TextBoxName.Text);
  Modal.Close(this, "OK");
}

private void DbInsertEvent(DateTime start, DateTime end, string name)
{
  using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["daypilot"].ConnectionString))
  {
      con.Open();
      SqlCommand cmd = new SqlCommand("INSERT INTO [event] (eventstart, eventend, name) VALUES(@start, @end, @name)", con);
      cmd.Parameters.AddWithValue("start", start);
      cmd.Parameters.AddWithValue("end", end);
      cmd.Parameters.AddWithValue("name", name);
      cmd.ExecuteNonQuery();
  }
}

VB

Protected Sub ButtonOK_Click(ByVal sender As Object, ByVal e As EventArgs)
  Dim start As Date = Convert.ToDateTime(TextBoxStart.Text)
  Dim [end] As Date = Convert.ToDateTime(TextBoxEnd.Text)

  DbInsertEvent(start, [end], TextBoxName.Text)
  Modal.Close(Me, "OK")
End Sub

Private Sub DbInsertEvent(ByVal start As Date, ByVal [end] As Date, ByVal name As String)
  Using con As New SqlConnection(ConfigurationManager.ConnectionStrings("daypilot").ConnectionString)
    con.Open()
    Dim cmd As New SqlCommand("INSERT INTO [event] (eventstart, eventend, name) VALUES(@start, @end, @name)", con)
    cmd.Parameters.AddWithValue("start", start)
    cmd.Parameters.AddWithValue("end", [end])
    cmd.Parameters.AddWithValue("name", name)
    cmd.ExecuteNonQuery()
  End Using
End Sub

Drag and Drop Task Moving

gantt-asp.net-control-task-moving.png

You can enable drag and drop task moving using EventMoveHandling property.

ASPX

<DayPilot:DayPilotScheduler 
  runat="server" 
  ID="GanttControl"
  ViewType="Gantt"
  
  ...
  EventMoveHandling="Notify"
  OnEventMove="GanttControl_EventMove"
  ...
  />

The Gantt control will fire GanttControl_EventMove event handler when the moving is finished.

C#

protected void GanttControl_EventMove(object sender, EventMoveEventArgs e)
{
  DbUpdateEvent(e.Value, e.NewStart, e.NewEnd, e.Text);
}

private void DbUpdateEvent(string id, DateTime start, DateTime end, string name)
{
  using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["daypilot"].ConnectionString))
  {
      con.Open();
      SqlCommand cmd = new SqlCommand("UPDATE [event] SET eventstart = @start, eventend = @end, name = @name WHERE id = @id", con);
      cmd.Parameters.AddWithValue("id", id);
      cmd.Parameters.AddWithValue("start", start);
      cmd.Parameters.AddWithValue("end", end);
      cmd.Parameters.AddWithValue("name", name);
      cmd.ExecuteNonQuery();
  }
}

VB

Protected Sub GanttControl_EventMove(ByVal sender As Object, ByVal e As EventMoveEventArgs)
  DbUpdateEvent(e.Value, e.NewStart, e.NewEnd, e.Text)
End Sub

Private Sub DbUpdateEvent(ByVal id As String, ByVal start As Date, ByVal [end] As Date, ByVal name As String)
  Using con As New SqlConnection(ConfigurationManager.ConnectionStrings("daypilot").ConnectionString)
    con.Open()
    Dim cmd As New SqlCommand("UPDATE [event] SET eventstart = @start, eventend = @end, name = @name WHERE id = @id", con)
    cmd.Parameters.AddWithValue("id", id)
    cmd.Parameters.AddWithValue("start", start)
    cmd.Parameters.AddWithValue("end", [end])
    cmd.Parameters.AddWithValue("name", name)
    cmd.ExecuteNonQuery()
  End Using
End Sub

See also event moving [doc.daypilot.org].

We will also limit the drag and drop moving to the horizontal direction using BeforeEventRender:

C#

protected void GanttControl_BeforeEventRender(object sender, BeforeEventRenderEventArgs e)
{
  e.EventMoveVerticalEnabled = false;
}

VB

Protected Sub GanttControl_BeforeEventRender(ByVal sender As Object, ByVal e As BeforeEventRenderEventArgs)
  e.EventMoveVerticalEnabled = False
End Sub

Drag and Drop Task Resizing

gantt-asp.net-control-task-resizing.png

You can enable drag and drop task resizing using EventResizeHandling property.

.aspx

<DayPilot:DayPilotScheduler 
    runat="server" 
    ID="GanttControl"
    ViewType="Gantt"

    ...
    EventResizeHandling="Notify"
    OnEventResize="GanttControl_EventResize"
    ...
    />

The GanttControl_EventResize will update the database and refresh the Gantt chart to display the new duration in the row header.

C#

protected void GanttControl_EventResize(object sender, EventResizeEventArgs e)
{
  DbUpdateEvent(e.Value, e.NewStart, e.NewEnd, e.Text);

  // force client-side Gantt chart update (possible duration change)
  SetDataSourceAndBind();
  GanttControl.Update(CallBackUpdateType.Full);
}

VB

Protected Sub GanttControl_EventResize(ByVal sender As Object, ByVal e As EventResizeEventArgs)
  DbUpdateEvent(e.Value, e.NewStart, e.NewEnd, e.Text)

  ' force client-side update (possible duration change)
  SetDataSourceAndBind()
  GanttControl.Update(CallBackUpdateType.Full)
End Sub

Gantt Row Header Columns

gantt-asp.net-control-columns.png

Define the row header columns using HeaderColumns:

<DayPilot:DayPilotScheduler 
  runat="server" 
  ID="GanttControl"
  ViewType="Gantt"
  ...
  >
  <HeaderColumns>
      <DayPilot:RowHeaderColumn Title="Task" Width="120" />
      <DayPilot:RowHeaderColumn Title="Duration" Width="90" />
  </HeaderColumns>
</DayPilot:DayPilotScheduler>

The first column will automatically display the task name as defined in DataTextField property. The content of the additional columns can be defined using BeforeResHeaderRender event handler:

C#

protected void GanttControl_BeforeResHeaderRender(object sender, BeforeResHeaderRenderEventArgs e)
{
  DateTime start = (DateTime) e.DataItem["eventstart"];
  DateTime end = (DateTime) e.DataItem["eventend"];
  TimeSpan duration = end - start;
  e.Columns[0].Html = duration.ToString();
}

VB

Protected Sub GanttControl_BeforeResHeaderRender(ByVal sender As Object, ByVal e As BeforeResHeaderRenderEventArgs)
  Dim start As Date = CDate(e.DataItem("eventstart"))
  Dim [end] As Date = CDate(e.DataItem("eventend"))
  Dim duration As TimeSpan = [end].Subtract(start)
  e.Columns(0).Html = duration.ToString()
End Sub

Gantt PNG Export

gantt-asp.net-control-export.png

The Gantt control has a built-in support for image export (PNG, JPG and other formats).

The "Export to PNG" button will use the Export() method to return the exported image for download.

C#

private void ExportToPng()
{
  SetDataSourceAndBind();
  SetExportProperties();

  Response.Clear();
  Response.ContentType = "image/png";
  Response.AddHeader("content-disposition", "attachment;filename=gantt.png");
  MemoryStream img = GanttControl.Export(ImageFormat.Png);
  img.WriteTo(Response.OutputStream);
  Response.End();
}

VB

Private Sub ExportToPng()
  SetDataSourceAndBind()
  SetExportProperties()

  Response.Clear()
  Response.ContentType = "image/png"
  Response.AddHeader("content-disposition", "attachment;filename=gantt.png")
  Dim img As MemoryStream = GanttControl.Export(ImageFormat.Png)
  img.WriteTo(Response.OutputStream)
  Response.End()
End Sub

Gantt PDF Export

gantt-asp.net-control-pdf.png

We will use the PNG image export to insert the Gantt into a PDF file.

We will use the open-source PDFSharp library for handling the PDF export.

C#

private void ExportToPdf()
{   
  // create a new PDF document
  PdfDocument doc = new PdfDocument();
  doc.Info.Title = "Gantt Chart PDF Export";
  doc.Info.Author = "DayPilot";

  // add a page
  PdfPage page = doc.AddPage();

  // set PDF page properties (size and orientation)
  page.Size = (PageSize) Enum.Parse(typeof (PageSize), ListPageSize.SelectedValue);
  page.Orientation = (PageOrientation)Enum.Parse(typeof(PageOrientation), ListPageOrientation.SelectedValue);

  // create graphics object for PDF page modification
  XGraphics gfx = XGraphics.FromPdfPage(page);

  // write title
  XRect titleRect = new XRect(new XPoint(), gfx.PageSize);
  titleRect.Inflate(-10, -15);
  XFont font = new XFont("Tahoma", 14, XFontStyle.Bold);
  gfx.DrawString("DayPilot Gantt PDF Export", font, XBrushes.DarkGray, titleRect, XStringFormats.TopCenter);

  // create the Gantt image
  SetDataSourceAndBind();
  SetExportProperties();
  Bitmap bitmap = GanttControl.ExportBitmap();

  // add the image to the PDF page
  XImage image = XImage.FromGdiPlusImage(bitmap);
  XRect imageRect = GetPaddedRectForImage(gfx, image, 10);
  double y = 40;
  imageRect.Y = y;
  gfx.DrawImage(image, imageRect);

  // save the PDF file to MemoryStream
  MemoryStream mem = new MemoryStream();
  doc.Save(mem, false);

  // send the output stream to the browser
  Response.Clear();
  Response.ContentType = "application/pdf";
  Response.AddHeader("content-disposition", "attachment;filename=gantt.pdf");
  mem.WriteTo(Response.OutputStream);
  Response.End();
}

VB

Private Sub ExportToPdf()
  ' create a new PDF document
  Dim doc As New PdfDocument()
  doc.Info.Title = "Gantt Chart PDF Export"
  doc.Info.Author = "DayPilot"

  ' add a page
  Dim page_Renamed As PdfPage = doc.AddPage()

  ' set PDF page properties (size and orientation)
  page_Renamed.Size = DirectCast(System.Enum.Parse(GetType(PageSize), ListPageSize.SelectedValue), PageSize)
  page_Renamed.Orientation = DirectCast(System.Enum.Parse(GetType(PageOrientation), ListPageOrientation.SelectedValue), PageOrientation)

  ' create graphics object for PDF page modification
  Dim gfx As XGraphics = XGraphics.FromPdfPage(page_Renamed)

  ' write title
  Dim titleRect As New XRect(New XPoint(), gfx.PageSize)
  titleRect.Inflate(-10, -15)
  Dim font As New XFont("Tahoma", 14, XFontStyle.Bold)
  gfx.DrawString("DayPilot Gantt PDF Export", font, XBrushes.DarkGray, titleRect, XStringFormats.TopCenter)

  ' create Gantt image
  SetDataSourceAndBind()
  SetExportProperties()
  Dim bitmap As Bitmap = GanttControl.ExportBitmap()

  ' add the image to the PDF page
  Dim image As XImage = XImage.FromGdiPlusImage(bitmap)
  Dim imageRect As XRect = GetPaddedRectForImage(gfx, image, 10)
  Dim y As Double = 40
  imageRect.Y = y
  gfx.DrawImage(image, imageRect)

  ' save the PDF file to MemoryStream
  Dim mem As New MemoryStream()
  doc.Save(mem, False)

  ' send the output stream to the browser
  Response.Clear()
  Response.ContentType = "application/pdf"
  Response.AddHeader("content-disposition", "attachment;filename=gantt.pdf")
  mem.WriteTo(Response.OutputStream)
  Response.End()
End Sub