Back to
swartzentruber.net
 

Code Examples in VB.NET (Framework 1.1)

Code Samples Home


cachemanager

createlinkspagewithxml

hideorshowaddnew

lookupusercontrolwithevents

lookupusercontrolwithevents2

looplistboxforselected

tourcalendarinternational

tourcalendarwithdetail

tourcalendarwithdetail2

tourcalendarwithdetail3

usercontrol_sessionview

usertablestoarraylist_via_sqldmo

Tour Calendar With Detail Section - Part 2 - The Calendar Page
Summary & Comments

Although I'm glad for a Calendar control that is included with ASP.NET, it is certainly barebones in some ways.  I have no doubt that for some applications, you will want to extend this control or maybe buy a more robust one from a control developer.

First of all, there is no databinding with this control.  You have to do everything on the DayRender event which fires for each day as the calendar HTML is built.  While this isn't hard to do, you do need to worry about database hits, especially in any kind of high traffic scenerio.  You don't want to be hitting the database every time an individual day is rendered.  There are various ways to manage this.  I decided to use a datatable and some caching to minimize database roundtrips.  You may want to use something else, like an array or some other collection.  You may want to build a "Show" class and build a collection of "Show" objects.  Or you might want to throw the data out to XML once a day and read that into a datatable.  Lots of options to experiment with.

Once I have my datatable of show dates for the current month, I then do a select on my datatable for the current day that is rendering and see if I have any matches.  If so, I write them out to a literal control for that day.  I'm not completely happy with this method, as it seems rather inefficient, but it will have to do for now.

Originally I was going to have a "Detail" link for each show and render the show details to a label or repeater on my calendar page.  The way I planned to do this was to create an href back to the page with the ItinID on the querystring.  Keep in mind you are limited in what controls you can add to a particular day as it renders.  You can't use controls that fire events, which severely limits your options.

From the help:

Note   Since the DayRender event is raised while the Calendar control is being rendered, you cannot add a control that can also raise an event, such as LinkButton. You can only add static controls, such as System.Web.UI.LiteralControl, Label, Image, and HyperLink.

Okay, that sucks.  Unfortunately, when you pass a querystring back to the page, certain events don't fire.  Plus you have to keep track of the month you want to display yourself and reinitialize your data.  It gets to be quite the hassle.  So I took the easy (or easier) way out and decided to render the details in an IFRAME.  I may go back to my original idea in a followup article, since I understand stuff a bit better now and maybe I can get everything to cooperate.

So let's get the data loaded up first.  In part 3, I'll explain getting the show details to render.

ASPX Code
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="ItinSample.aspx.vb" Inherits="swartzentrubernet.ItinSample" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
</HEAD>
<body MARGINWIDTH="0" MARGINHEIGHT="0">
<form id="Form1" method="post" runat="server">
<center>
<table BORDER="1" CELLPADDING="10">
<tr bgcolor="#99cc99" bordercolor="#cc6600">
<td>
<asp:Label ID="lblMsg" Runat="server" CssClass="Alert"></asp:Label>
<table border="0" cellpadding="4">
<tr valign="top">
<td><asp:Calendar OnVisibleMonthChanged="ChangeMonth" OnDayRender="Calendar1_DayRender" DayStyle-Height="75" DayStyle-Width="75" DayStyle-verticalalign="Top" DayStyle-Font-Size="8" NextPrevFormat="FullMonth" SelectionMode="Day" TitleStyle-Font-Bold="True" TitleStyle-Font-Name="Verdana" TitleStyle-Font-Size="14" BackColor="white" BorderColor="#FF6633" BorderStyle="Dashed" CellPadding="2" CellSpacing="2" Runat="server" id="Calendar1" OtherMonthDayStyle-ForeColor="#C0C0C0" DayStyle-BorderStyle="Solid" DayStyle-BorderWidth="1" TodayDayStyle-ForeColor="Red" Height="600" Width="750"></asp:Calendar></td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</form>
</body>
</HTML>

You can see this is a simple page, mainly just setting the calendar properties.  There are 2 events we need to handle for the basic calendar page.

  • OnVisibleMonthChanged - This event is triggered when the user clicks the previous or next month on your calendar.
  • OnDayRender - This is the event that fires as each calendar day is rendered to the browser.  It will fire once for each day displayed.  Keep in mind it may display a few days from the month prior and the month after your current month.

The other thing I did was set the SelectionMode property to Day, which means you can only select a day at a time rather than a range.  One enhancement would be to let the user select a series of days and render all the shows during that time frame.

VB.NET Codebehind Code
Option Strict On
Option Explicit On

Imports System
Imports System.Text
Imports System.Drawing
Imports System.Data
Imports System.Data.SqlClient
Imports System.Web.UI.WebControls
Imports System.Web.UI
Imports System.Web.Caching

Public Class ItinSample
    Inherits System.Web.UI.Page
    Protected WithEvents Calendar1 As System.Web.UI.WebControls.Calendar
    Private cnString As String = ConfigurationSettings.AppSettings("test")
    Private dtDates As DataTable
    Protected WithEvents lblMsg As System.Web.UI.WebControls.Label
    Private curMonth As DateTime
    Private noDatesForMonth As Boolean = False

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Put user code to initialize the page here

        If Not Page.IsPostBack Then
            curMonth = Convert.ToDateTime(DateTime.Today.Month.ToString & "/1/" & DateTime.Today.Year.ToString)
            Calendar1.VisibleDate = curMonth
            LoadCacheForMonth(curMonth)
        End If

    End Sub

    Protected Sub ChangeMonth(ByVal sender As Object, ByVal e As MonthChangedEventArgs)

        curMonth = e.NewDate.Date
        LoadCacheForMonth(curMonth)

    End Sub

    Private Sub LoadCacheForMonth(ByVal curMonth As DateTime)

        ' loads dataset for a particular month into cache

        Dim iMonth As Integer = curMonth.Month
        Dim iYear As Integer = curMonth.Year
        Dim cacheName As String = iMonth.ToString & iYear.ToString & "Dates"

        If Cache(cacheName) Is Nothing Then
            dtDates = DatesForMonth(curMonth)
            Cache.Insert(cacheName, dtDates, Nothing, Cache.NoAbsoluteExpiration, _
                        TimeSpan.FromHours(3))
        Else
            dtDates = CType(Cache(cacheName), DataTable)
        End If

    End Sub

    Private Function DatesForMonth(ByVal Month As DateTime) As DataTable

        Dim cn As SqlConnection = New SqlConnection(cnString)
        Dim cmd As SqlDataAdapter = New SqlDataAdapter("GetDatesForMonth", cn)
        cmd.SelectCommand.CommandType = CommandType.StoredProcedure
        Dim dt As New DataTable()

        Try
            AppendSQLParameter(cmd.SelectCommand, "@startDate", SqlDbType.DateTime, ParameterDirection.Input, 8, Month)
            cmd.Fill(dt)
            cmd.Dispose()
            cn.Close()
        Catch x As SqlException
            lblMsg.Text = x.Message
        Finally
            cn = Nothing
        End Try

        Return dt

    End Function


Protected Sub Calendar1_DayRender(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DayRenderEventArgs) Handles Calendar1.DayRender

        If noDatesForMonth Then Exit Sub

        ' may have clicked selected date again
        If curMonth.Year = 1 Then
            curMonth = Convert.ToDateTime(Calendar1.SelectedDate.Month.ToString & "/1/" & Calendar1.SelectedDate.Year.ToString)
        End If

        Dim matchRows() As DataRow
        Dim myMonth As Integer = curMonth.Month
        LoadCacheForMonth(curMonth)
        If ((dtDates Is Nothing) OrElse (dtDates.Rows.Count = 0)) Then
            ' we have no cached datatable or the datatable is empty
            ' no dates to render for this month, shortcircuit any further processing
            noDatesForMonth = True
            Exit Sub
        End If

        Dim curDay As Integer
        Dim dr As DataRow
        Dim dayText As StringBuilder = New StringBuilder()
        Dim eventDay As Boolean
        Dim c As TableCell = e.Cell

        If e.Day.Date.Month = myMonth Then
            curDay = e.Day.Date.Day
            matchRows = dtDates.Select("ShowDay=" & curDay.ToString)
            For Each dr In matchRows
                eventDay = True
                dayText.Append("<br><b>")
                dayText.Append(Convert.ToString(dr.Item("Venue")))
                dayText.Append("</b>")
            Next
            If eventDay Then
                e.Cell.BackColor = Color.Pink
                c.Controls.Add(New LiteralControl(dayText.ToString))
                dayText = New StringBuilder()
            End If
        End If

    End Sub
End Class
AppendSqlParameter (part of my data access class called DataHelper)
Public Sub AppendSQLParameter(ByRef cmd As SqlCommand, _
                ByVal strParamName As String, _
                ByVal strDataType As SqlDbType, _
                ByVal strDirection As ParameterDirection, _
                ByVal lngSize As Integer, _
                ByVal varValue As Object)

        Dim param As SqlParameter

        If lngSize > 0 Then
            param = New SqlParameter(strParamName, strDataType, lngSize)
        Else
            param = New SqlParameter(strParamName, strDataType)
        End If
        param.Direction = strDirection
        If Not strDirection = ParameterDirection.Output Then
            If Not ((varValue Is System.DBNull.Value) Or (varValue Is Nothing)) Then
                param.Value = varValue
            Else
                param.Value = System.DBNull.Value
            End If
        End If

        cmd.Parameters.Add(param)

    End Sub

A couple of points here. I am explicitly caching my datatables for each month and year so that I only have to hit the database once in a while.  These datatables will stay cached for up to 3 hours, depending on how busy the site is.  You can obviously adjust these to your liking.

In my ChangeMonth sub, I simply get the new month from the event args and populate my datatable for that month if necessary.

In my DatesForMonth sub, I'm executing my stored procedure.  The datahelper class that is referenced is simply a custom class I use for data access.  In this case I have a simplified parameter builder that I use a lot.

My Calender1_DayRender is where the database information populates my calender.  If I have no information, I set the noDatesForMonth boolean so I don't waste time as the rest of the calendar renders.  This is a simplistic example where I'm just writing out the Venue for the day and changing the color of the Day cell to pink to highlight it.  There are lots of other things you can do.

Now let's look at how to give the user a way to look at the show details in Part 3

Tour Calendar With Detail Part 3

© 1999-2008 swartzentruber.net