SYSK 227: Monthly Calendar control in ASP.NET

Did you ever need a calendar control that allows you to color-code certain days based on your business logic and display details on mouse-over event? Here is an example of what I’m talking about:

So, perhaps the yellow color means “standard” appointments, and red indicates very important events… You decide based on your business rules and needs. This control also raises OnDateClick event so you can, for example, display additional information. The OnDateClick event is only registered for dates with info, e.g. in the above example the only clickable days are Friday the 3rd and Sunday the 12th.

 

Interested? Here is the code… with my standard disclaimer: use at your own risk, has not been rigorously tested, etc.

 

 

1. Create a class library

2. Add the following class:

 

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace Web.UI.CustomControls

{

    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),

        AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]

    public class MonthlyCalendar : System.Web.UI.WebControls.Table, INamingContainer

    {

        [Serializable]

        public delegate void DateClickHandler(MonthlyCalendar source, DateTime date);

        private static readonly object EventDateClicked = new object();

        public MonthlyCalendar()

        {

            // Set defaults

            base.BorderStyle = BorderStyle.Solid;

            base.CellPadding = 0;

            base.CellSpacing = 0;

            base.BorderColor = System.Drawing.Color.DarkGray;

            base.BorderWidth = 1;

        }

        public event DateClickHandler OnDateClick

        {

            add { Events.AddHandler(EventDateClicked, value); }

            remove { Events.RemoveHandler(EventDateClicked, value); }

        }

                  

        protected override void CreateChildControls()

        {

            Controls.Clear();

            base.CreateChildControls();

           

            // disply title

            DateTime currentDate = new DateTime(Year, (int) Month, 1);

            base.Caption = "<p style=\"padding-bottom: 4px\"><b>" + currentDate.ToString("MMMM yyyy") + "</b></p>";

            base.Rows.Clear();

            // Add header

            TableHeaderRow hr = new TableHeaderRow();

            for (short i = 0; i < 7; i++)

            {

                TableHeaderCell c = new TableHeaderCell();

                c.Text = string.Format("&nbsp;{0}&nbsp;", ((DayOfWeek)i).ToString().Substring(0, 3));

                hr.Cells.Add(c);

            }

            base.Rows.Add(hr);

            // Add weeks

            DateTime endDate = currentDate.AddMonths(1);

            TableRow row = null;

            while (currentDate < endDate)

            {

              int dayOfWeek = (int)currentDate.DayOfWeek;

                if (row == null || dayOfWeek == (int)DayOfWeek.Sunday)

                {

                    // Create a row

                    row = new TableRow();

                    for (int i = 0; i < 7; i++)

                    {

                        row.Cells.Add(new TableCell());

                    }

                    base.Rows.Add(row);

                }

                // Add text, back/forecolor

                TableCell currentCell = row.Cells[dayOfWeek];

                currentCell.Text = string.Format("&nbsp;{0}&nbsp;", currentDate.Day);

                // bool found = false;

                foreach (SeletedPeriod period in Periods)

                {

                    if (currentDate.Day >= period.StartDay && currentDate.Day <= period.EndDay)

                    {

                        currentCell.BackColor = period.BackColor;

                        currentCell.ForeColor = period.ForeColor;

                        currentCell.ToolTip = period.Message;

                        currentCell.Attributes["onclick"] = string.Format("javascript:{0};", Page.ClientScript.GetPostBackEventReference(this, string.Format("SelectDate({0})", currentDate.Day)));

                        break;

        }

                }

                // Next day

                currentDate = currentDate.AddDays(1);

            }

        }

        public int Year

        {

            get { return ViewState["Year"] == null ? 0 : (int) ViewState["Year"]; }

            set { ViewState["Year"] = value; }

        }

        public Month Month

        {

            get { return ViewState["Month"] == null ? Month.Undefined : (Month)ViewState["Month"]; }

            set { ViewState["Month"] = value; }

        }

        private System.Collections.Generic.List<SeletedPeriod> Periods

        {

            get { return ViewState["Periods"] == null ? new System.Collections.Generic.List<SeletedPeriod>() : (System.Collections.Generic.List<SeletedPeriod>)ViewState["Periods"]; }

            set { ViewState["Periods"] = value; }

        }

        public void Add(SeletedPeriod period)

        {

            System.Collections.Generic.List<SeletedPeriod> data = Periods;

            data.Add(period);

            Periods = data; // save in ViewState

        }

        public void AddRange(SeletedPeriod[] periods)

        {

            System.Collections.Generic.List<SeletedPeriod> data = Periods;

            data.AddRange(periods);

        Periods = data; // save in ViewState

        }

        public void AddRange(System.Collections.Generic.List<SeletedPeriod> periods)

        {

            System.Collections.Generic.List<SeletedPeriod> data = Periods;

            data.AddRange(periods);

            Periods = data; // save in ViewState

        }

        protected override void RaisePostBackEvent(string eventArgument)

        {

            this.EnsureChildControls();

            DateClickHandler handler = (DateClickHandler)Events[EventDateClicked];

            if (handler != null)

            {

                string eventArg = System.Web.HttpContext.Current.Request["__EVENTARGUMENT"];

                if (eventArg != null)

                {

                    string searchFor = "SelectDate(";

                    int startPos = eventArg.IndexOf(searchFor);

                    if (startPos != -1)

                    {

                        int endPos = eventArg.IndexOf(')', startPos);

                        if (endPos != -1 && endPos > startPos)

                        {

                            string clickedDate = eventArg.Substring(startPos + searchFor.Length, endPos - startPos - searchFor.Length);

                            // Raise event

                         handler(this, new DateTime(Year, (int) Month, int.Parse(clickedDate)));

                        }

                    }

                }

            }

        }

    }

    [Serializable]

    public class SeletedPeriod

    {

        private int _startDay;

        private int _endDay;

        private string _message;

        private System.Drawing.Color _backColor;

        private System.Drawing.Color _foreColor;

        public SeletedPeriod(int startDay, int endDay, string message, System.Drawing.Color backColor, System.Drawing.Color foreColor)

        {

            _startDay = startDay;

            _endDay = endDay;

            _message = message;

            _backColor = backColor;

            _foreColor = foreColor;

        }

        public int StartDay

        {

            get { return _startDay; }

            set { _startDay = value; }

        }

        public int EndDay

        {

            get { return _endDay; }

            set { _endDay = value; }

        }

        public string Message

        {

            get { return _message; }

            set { _message = value; }

        }

        public System.Drawing.Color BackColor

        {

            get { return _backColor; }

            set { _backColor = value; }

        }

      public System.Drawing.Color ForeColor

        {

            get { return _foreColor; }

            set { _foreColor = value; }

        }

    }

    public enum Month : short

    {

        Undefined = 0,

        January = 1,

        February = 2,

        March = 3,

        April = 4,

        May = 5,

        June = 6,

        July = 7,

        August = 8,

        September = 9,

        October = 10,

        November = 11,

        December = 12

    }

}

 

3. On .aspx page, add the following line

<%@ Register Assembly="Web.UI.CustomControls" Namespace="Web.UI.CustomControls" TagPrefix="cc1" %>

 

and add the actual control:

<cc1:monthlycalendar id="MonthlyCalendar1" runat="server" ></cc1:monthlycalendar>

<asp:Label ID="SelectedDate" runat="server"></asp:Label>

 

4. On Page_Load, add your initialization code. E.g.

if (Page.IsPostBack == false)

{

            MonthlyCalendar1.Month = Web.UI.CustomControls.Month.February;

            MonthlyCalendar1.Year = 2006;

            MonthlyCalendar1.Add(new Web.UI.CustomControls.SeletedPeriod(3, 3, string.Format("Meeting with Mike @ 11:30 am\r\nHoustons restaurant"), System.Drawing.Color.Yellow, System.Drawing.Color.Black));

            MonthlyCalendar1.Add(new Web.UI.CustomControls.SeletedPeriod(12, 12, string.Format("Performance review with boss\r\n9:00 am"), System.Drawing.Color.Red, System.Drawing.Color.White));

}

MonthlyCalendar1.OnDateClick += new Web.UI.CustomControls.MonthlyCalendar.DateClickHandler(MonthlyCalendar_OnDateClick);

 

 

When a date (appointment) is clicked, it’ll raise an event… Here is your delegate:

void MonthlyCalendar_OnDateClick(Web.UI.CustomControls.MonthlyCalendar source, DateTime selectedDay)

{

        SelectedDate.Text = selectedDay.ToShortDateString();

}

 

Enjoy!