A Not-so-hidden Agenda

Create a Table-based Calendar with Links to Events

asp:Q&A

LANGUAGES: VB.NET | C#

ASP.NET VERSIONS: 1.x

 

A Not-so-hidden Agenda

Create a Table-based Calendar with Links to Events

 

By Josef Finsel

 

Q. I have a Web site that promotes events. I want an easy way to create a table-based calendar that has links to the events; something like the existing calendar control that looks more like a monthly appointment calendar. Is this easy to do?

 

A. This is a great question. The short answer is yes; the longer answer gives us an opportunity to look at creating a class and using the System.Date data type and DataTables to create a reusable tool. To begin, let s add a new class, called CalendarTable, to a Web site. Although I m only going to hit the high points in this article, you can download and explore the full source code (with C# and VB.NET examples); see the end of the article for details.

 

We ll start by creating four private variables: minDate, maxDate, calendarInformation, and strMonths. The first two will store the first and last date of the month, calendarInformation is a DataTable to store the date events, and strMonths is an array of month names. Next, we need to define what the class will contain. We need to be able to set the year and the month, so we ll expose a property for those. We need a way to add items to the calendar and a way to return the complete table. These are the items the class will make public. Any function or method that is used solely within the class will be private.

 

With those details defined, we need to look at the New method. This is called whenever the class is instantiated and needs to do two things. First it needs to set the values of minDate and maxDate and then it needs to define the columns in calendarInformation. By default, we ll assume that the calendar, until reset by the Month or Year property, is for the current month and year. Setting the value of the two dates is fairly simple, given the date manipulation built into .NET:

 

minDate = Now.Month & "/1/" & Now.Year

maxDate = minDate.AddDays(minDate.DaysInMonth(minDate.Year, _

                         minDate.Month) - 1)

 

The first line sets the date to the first of the month. The second uses the built-in AddDays method to add the number of days in the month less one to get to the last day of the month. This built-in functionality means never having to worry about leap years or trying to remember if April has 30 or 31 days. But we also need this same functionality in the Month and Year properties, so those two lines of code go into their own method.

 

It is most likely, however, that the year and month are not going to be the current year and month, so it s just as easy to overload the method to allow it to take a Month and Year parameter, which is shown in the code in Figure 1.

 

'Set the minDate and maxDate

Private Sub SetDate(ByVal iMonth As Int16, _

                   ByVal iYear As Int16)

 minDate = iMonth & "/1/" & iYear

 maxDate = minDate.AddDays(minDate.DaysInMonth(minDate.Year, _

                            minDate.Month) - 1)

End Sub

Private Sub SetColumns()

 calendarInformation.Columns.Add("Date")

 calendarInformation.Columns.Add("Output")

End Sub

'If no parameters are specified default to current month/year

Private Sub New()

 SetDate(Now.Month, Now.Year)

 SetColumns()

End Sub

'Define the month / year on New

Public Sub New(ByVal iMonth As Int16, ByVal iYear As Int16)

 SetDate(iMonth, iYear)

 SetColumns()

End Sub

Figure 1: The initialization routines for the class.

 

Figure 1 also shows SetColumns, which creates two columns in our DataTable: one for the date and one for whatever is supposed to be output on that date. Although I did consider building a more complex data structure with a URL and anchor text, I decided that keeping it as simple as possible provided more flexibility, allowing anything to be displayed on the date, from a link to a graphic to text.

 

Now that we have the basic housekeeping out of the way, let s look at the first of the two main public methods in this class, AddAppointment (shown in Figure 2) and RenderTable (shown in Listing One). AddAppointment has two parameters, the date and what is supposed to be output on the date. The first thing we do is check to make sure the date is for the month, and raise an error if it isn t. This error code would need to be handled in the page calling the method. Then it s as easy as creating a new DataRow, populating the columns, and saving it to our DataTable.

 

'Add an event to the calendar

Public Sub AddAppointment(ByVal CalendarDate As Date, _

                         ByVal EntryOutput As String)

 If CalendarDate < minDate Or CalendarDate > maxDate Then

   Err.Raise(5003, "UserCalendar", CalendarDate.ToShortDateString _

     & " is not between " & minDate.ToShortDateString & " and " _

     & maxDate.ToShortDateString & ".")

 End If

 'Create the new row and write it to the table

 Dim rowInfo As DataRow = calendarInformation.NewRow()

 rowInfo.Item("Date") = CalendarDate.ToShortDateString

 rowInfo.Item("Output") = EntryOutput

 calendarInformation.Rows.Add(rowInfo)

 calendarInformation.AcceptChanges()

End Sub

Figure 2: Adding an appointment to the calendar.

 

Finally, we output the actual table in RenderTable (again, see Listing One). We begin by creating a seven-column table with a header element that spans all seven columns. In this we ll put the month and year. Next we ll loop through the days between minDate and maxDate, inclusive. The first task is determining where to begin and end each table row. Fortunately, the System.Date data type has an enumerator that tells us what day of the week the date the variable contains. Hence, whenever the date is on a Sunday we need to start a new row, and whenever it s a Saturday we need to close the row. However, because very few months actually begin on Sunday, we may need to add blank space at the beginning of the month. To do that, we ll use a single <TD> element with a column span for the correct number of columns, which would be the enumerator. Now that we ve taken care of that, we can output the actual date s data. To that end we call OutputOneDay.

This private method returns all of the data that makes up a cell of the table. It begins by defining the <TD> tag and then printing the day, centered within the cell, followed by an <HR> tag to put a nice line below the day. Next it looks to see if there is any data in calendarInformation and adds it. If there are no events for the date, it adds a <BR> tag to expand the height of the cell. Without this, the rows will look really strange. Finally, it closes the cell and returns the string to RenderTable.

 

To wrap up the RenderTable method, we need to determine if the day is a Saturday, in which case we need to close the row and then increment the date. After we have gone through the entire month, we determine if we need to add another column-spanning blank cell to close out the month. The use of a non-breaking space (&nbsp;) makes the column show up with borders.

 

OK, we ve got the class built; now it s time to test it. TestCalendar.aspx is a very simple Web page with a single, Literal Web control in it. In the page load event we can instantiate our new class and add appointments to it:

 

Dim userCalendar As CalendarTable = _

 New CalendarTable(7, 2005)

userCalendar.AddAppointment("7/4/2005", _

 "<a href=http://www.july4th.org target=_blank>" & _

 "Boston's Celebration!</a>")

userCalendar.AddAppointment("7/1/2005", _

 "<a href=http://www.aspnetpro.com target=_blank>" & _

 "asp.netPro July Edition!</a>")

userCalendar.AddAppointment("7/30/2005", _

 "<img src=dingbar.gif />")

Literal1.Text = userCalendar.RenderTable()

 

As you can see from the above code, I ve added two URLs and one image to the calendar. Then I set the Literal Web control s text to the output of the RenderTable method and it displays a table that looks very much like a calendar. Although this is fully functional code, there are still some things that could be done to improve it. Some of these, such as adding functionality to set the background color of the table or days that occur in the past, are related to how the calendar looks. Others, such as creating another overloaded New method that takes the same parameters as AddAppointment and sets the month and date based on the date passed in, make the class more usable. It s also very easy to take this class and turn it into a Web-user control. All of this I leave for you to explore.

 

That wraps up this month s column. Remember, this is your column; it runs on the questions you ask, so send your ASP.NET questions to [email protected] and I ll help you get the answers you need.

 

C# and VB.NET code examples are available for download.

 

Josef Finsel is a software consultant with Strategic Data Solutions (http://www.sds-consulting.com). He has published a number of VB and SQL Server-related articles and is currently working with VS 2005. He s also author of The Handbook for Reluctant Database Administrators (Apress, 2001).

 

Begin Listing One Rendering the table

'Output the entire month

Public Function RenderTable()

 Dim outputDate As Date = minDate

 'Start by creating the table with the header

 Dim strOutput As StringBuilder = New _

   StringBuilder("<TABLE border=1 width=""100%"">")

 strOutput.Append("<TR><TH COLSPAN=7>")

 strOutput.Append(strMonths(minDate.Month - 1))

 strOutput.Append(", ")

 strOutput.Append(minDate.Year)

 strOutput.Append("</TH></TR>")

 'Now work through the month

 Do While outputDate <= maxDate

   'On Sunday or the first, we need to start a new row

   If outputDate.DayOfWeek = DayOfWeek.Sunday Or _

    outputDate.Day = 1 Then

     strOutput.Append("<TR>")

   End If

   'If the first is not a Sunday, we need filler

   'to line the days up

   If outputDate.Day = 1 And outputDate.DayOfWeek _

     <> DayOfWeek.Sunday Then

     strOutput.Append("<TD colspan=" & _

            outputDate.DayOfWeek & ">&nbsp;</TD>")

   End If

   'Call the code to get events

   strOutput.Append(OutputOneDay(outputDate))

   'On Saturday, close the row

   If outputDate.DayOfWeek = DayOfWeek.Saturday Then

     strOutput.Append("</TR>")

   End If

   'Important: Increment the date or infinite

   'loops commence

   outputDate = outputDate.AddDays(1)

 Loop

 'If last day of month isn't on a Saturday, clean up

 'rest of table

 If maxDate.DayOfWeek <> DayOfWeek.Saturday Then

   strOutput.Append("<TD colspan=" & (DayOfWeek.Saturday - _

     maxDate.DayOfWeek) & ">&nbsp;</TD>")

 End If

 'Close the table

 strOutput.Append("</TABLE>")

 Return strOutput.ToString()

End Function

'Output events for a day

Private Function OutputOneDay(ByVal outputDate _

                             As Date) As String

 Dim strOutput As StringBuilder = New _

   StringBuilder("<TD width=""14%"" valign=top>")

 'Align numeric day in center over a line

 strOutput.Append("<div style=""text-align: center;"">")

 strOutput.Append(outputDate.Day.ToString())

 strOutput.Append("</div><hr>")

 Dim rowInfo() As DataRow

 rowInfo = calendarInformation.Select("Date='" & _

    outputDate.ToShortDateString & "'")

 If rowInfo.GetLength(0) > 0 Then 'Events exist

   For i As Integer = rowInfo.GetLowerBound(0)

    To rowInfo.GetUpperBound(0)

     strOutput.Append(rowInfo(i)("Output"))

   Next

 Else

   'No data, print a blank line to make it look good

   strOutput.Append("<BR>")

 End If

 'Close the column

 strOutput.Append("</TD>")

 Return strOutput.ToString()

End Function

End Listing One

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish