Click here to Skip to main content
15,885,546 members
Articles / Web Development / ASP.NET
Article

Cool Calendar - Amazing Calendar without popup

Rate me:
Please Sign up or sign in to vote.
4.07/5 (27 votes)
11 Oct 2004CPOL4 min read 289.4K   5.4K   96   58
This article will show you how to build a cool Calendar without out popups and PostBack events. You even don't have to write a single line of JavaScript code.

Sample Image - CoolCalendarImg.jpg

Introduction

I would like to thank Microsoft for shipping Calendar control in .NET that helps us to solve many problems, but it has many limitations and drawbacks. I have seen many articles just to improve this control. Many articles suggest Popup window. but I found that many users don't like popups. Second, we have to write JavaScript to deal with it. Second problem with Calendar control itself is it doesn't provide navigation by year. Imagine when you have to enter some date of 10 years back. Even for 2 years, user has to click at least 24 times and so 24 round trips. Even for same year but 10 months back, at least 10 round trips. I was annoyed with this fact and started thinking about a better solution. At last, I have listed out the requirement for the best calendar.

  • It should provide navigation for month as well as year.
  • It should provide combobox for month as well as year to select past dates.
  • It should not appear as popup.
  • It should occupy as little as possible space on the form.
  • It should assign value to control automatically, no JavaScript need to be written.

And here we go, I have tried my best to fulfill all the above requirements in this article.

Hope it will be useful to you.

Using the code

As I said, there is no need to write a single line of JavaScript code any more. Add a reference of CoolCalendar control in toolbox. Just drag the control on the page, assign the control (control to which, you want to assign the value) to CoolCalander, and enjoy...!!!!!!!!!!!!!

Points of Interest

Sounds interesting...!!! So, let's start exploring the magic behind this control. It is a composite control. It's a combination of Label(s), LinkButton(s) and a Calendar control. As it's a composite control, I have derived my class from Control class. First, list out the points that we have to keep in mind:

  • We need navigation button for previous, next month as well as year.
  • We need dropdown box for year as well as month, but it should display only when user clicks on month or year..!!
  • We need calendar, without postback event on each day.
  • Define the property which can take reference of the control to assign selected date.
  • Define the property to specify date format.
  • The control should occupy less space on the page.

Core of control

As this is a composite control, we need not implement/ override Render method. In fact, the main core function of the control is CreateChildControls. So, whenever the control is initialized, it will call this method after Init and before PreRender events. I have created all my child controls here. The code is self explanatory.

C#
protected override void CreateChildControls() {
    Calendar cal=new Calendar(); 

    //Default Diplay Current Month
    cal.VisibleDate=DateTime.Today; 
    //disable default heading
    cal.ShowTitle=false; 
    cal.BackColor=BackColor; 
    cal.ForeColor=ForeColor; 
    cal.Attributes.Add("style","z-index:0"); 

    // By Default control will be render as small text or small Image.
    // Onclick on this text/image only our calander will be displayed
    if(ViewState["visibility"]==null) 
        ViewState["visibility"]=false;
    if(Img=="")
      Controls.Add(new 
         LiteralControl(@"<div style='position:absolute;z-index:0' id='"+ 
         this.ID+@":Div1' onClick=""ShowCal('"+this.ID+
         @":Cal')"">Select Date</div><div " + 
         @"style='position:absolute;z-index:0;BACKGROUND-COLOR:"+ 
         HeadingBackColor.Name  +";visibility:hidden' id='"+
         this.ID+@":Cal' ><table cellpadding=0 " + 
         @"cellspacing=0><tr><td vAlign=""middle"" align=center>"));
    else
      Controls.Add(new 
         LiteralControl(@"<div style='position:absolute;z-index:0' id='"+ 
         this.ID+@":Div1' onClick=""ShowCal('"+this.ID+ 
         @":Cal')""><img src='"+ Img + 
         "'></img></div><div " + 
         "style='position:absolute;z-index:0;BACKGROUND-COLOR:"+ 
         HeadingBackColor.Name +" ;visibility:hidden' id='"+this.ID+ 
         @":Cal' ><table cellpadding=0 " + 
         @"cellspacing=0><tr><td vAlign=""middle"" align=center>"));
    //<div style='position:absolute;left:15px;z-index:0'>Month</div>
    //<div style='position:absolute;z-index:0'>Year</div>

    DropDownList cboyear = new DropDownList();
    DropDownList cboMonth= new DropDownList();
    Label  lblMonth=new Label();
    Label lblYear=new Label();
    lblMonth.Text = cal.VisibleDate.ToString("MMMM").PadRight(12,' ')+",";
    lblYear.Text=cal.VisibleDate.ToString("yyyy");

    for(int i=2050;i>1049;i--)
        cboyear.Items.Add(i.ToString());   

    cboMonth.Items.Add("January");  
    cboMonth.Items.Add("February");  
    cboMonth.Items.Add("March");  
    cboMonth.Items.Add("April");  
    cboMonth.Items.Add("May");  
    cboMonth.Items.Add("Jun");  
    cboMonth.Items.Add("July");  
    cboMonth.Items.Add("Auguest");  
    cboMonth.Items.Add("September");  
    cboMonth.Items.Add("October");  
    cboMonth.Items.Add("November");  
    cboMonth.Items.Add("December");              

    cboMonth.AutoPostBack=true; 
    cboyear.AutoPostBack =true; 
    cboMonth.SelectedIndexChanged += new 
            EventHandler(cboMonth_SelectedIndexChanged);      
    cboyear.SelectedIndexChanged += new 
            EventHandler(cboyear_SelectedIndexChanged);             

    LinkButton  lblFirst=new LinkButton();
    LinkButton lblPrev=new LinkButton(); 
    LinkButton lblNext=new LinkButton(); 
    LinkButton lblLast=new LinkButton(); 

    lblFirst.Font.Name="Webdings";
    lblPrev.Font.Name="Webdings";
    lblNext.Font.Name="Webdings";
    lblLast.Font.Name="Webdings";

    lblFirst.Attributes.Add("style","TEXT-DECORATION: none;COLOR: "+ 
                                                  HeadingForeColor.Name);   
    lblPrev.Attributes.Add("style","TEXT-DECORATION: none;COLOR: "+ 
                                                  HeadingForeColor.Name); 
    lblNext.Attributes.Add("style","TEXT-DECORATION: none;COLOR: "+ 
                                                  HeadingForeColor.Name); 
    lblLast.Attributes.Add("style","TEXT-DECORATION: none;COLOR: "+ 
                                                  HeadingForeColor.Name); 

    //Tool tips to the Navigation Button
    lblFirst.Attributes.Add("title","Previous Year");   
    lblPrev.Attributes.Add("title","Previous Month");   
    lblNext.Attributes.Add("title","Next Month");   
    lblLast.Attributes.Add("title","Next Year");   

    lblPrev.Text="7";
    lblFirst.Text="9";
    lblNext.Text ="8";
    lblLast.Text=":"; 

    lblPrev.Click+=new EventHandler(lblPrev_Click);
    lblFirst.Click+=new EventHandler(lblFirst_Click);
    lblNext.Click+=new EventHandler(lblNext_Click);
    lblLast.Click+=new EventHandler(lblLast_Click);

    lblMonth.Attributes.Add("style","FONT-WEIGHT: bold; COLOR: "+ 
                                            HeadingForeColor.Name);    
    lblYear.Attributes.Add("style","FONT-WEIGHT: bold; COLOR:"+ 
                                            HeadingForeColor.Name);    

    Controls.Add(lblFirst); 
    Controls.Add(lblPrev); 
    //Controls.Add(cboMonth);
    //Controls.Add(cboyear);
    Controls.Add(lblMonth);
    Controls.Add(lblYear);

    // Hide Month and year dropdown box by defauld will be displayed
    // when user will click on month or year label.
    Controls.Add(new LiteralControl(@"<span style='width:"+ 
        200 +"px;background-color:white;position:absolute;left:10px;" + 
        "align:center;visibility:hidden;z-index:0' id='"+ 
        this.ID+ ":Month'>"));  
    Controls.Add(cboMonth);
    Controls.Add(cboyear);
    Controls.Add(new LiteralControl(@"</span>"));  
    Controls.Add(lblNext);
    Controls.Add(lblLast);
    lblMonth.Attributes.Add("OnClick","ShowCal('"+this.ID+@":Month')");   

    Controls.Add(new LiteralControl("</td></tr><tr><td>"));

    Controls.Add(cal);
    cal.DayRender+=new DayRenderEventHandler(cal_DayRender);
    Controls.Add(new LiteralControl("</td></tr></table></div>"));

}

We also need to register JavaScript to show / hide Calender control on user request. That we have to do on PreRender event. The code for PreRender is as follows:

C#
protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender (e);
    if((bool)ViewState["visibility"]==true)  
    {
        if(Img=="")
            ((LiteralControl)Controls[0]).Text= 
                 @"<div style='position:absolute;z-index:0' id='"+
                 this.ID+@":Div1' onClick=""ShowCal('"+this.ID+
                 @":Cal')"">Select Date</div><div style='position:" + 
                 @"absolute;z-index:0;BACKGROUND-COLOR:"+HeadingBackColor.Name + 
                 ";visibility:visible' id='"+this.ID+@":Cal' ><table " + 
                 @"cellpadding=0 cellspacing=0><tr>" + 
                 @"<td vAlign=""middle"" align=center>";
        else
            ((LiteralControl)Controls[0]).Text= 
                 @"<div style='position:absolute;z-index:0' id='"+this.ID+ 
                 @":Div1' onClick=""ShowCal('"+this.ID+@":Cal')""><img src='"+ 
                 Img +"'></img></div><div style='position:" + 
                 @"absolute;z-index:0;BACKGROUND-COLOR: "+HeadingBackColor.Name +
                 ";visibility:visible' id='"+this.ID+@":Cal' ><table " + 
                 @"cellpadding=0 cellspacing=0><tr>" + 
                 @"<td vAlign=""middle"" align=center>";
    }

    System.Text.StringBuilder strJavaScript=new System.Text.StringBuilder();

    //Code to emite Javascript that will display Calander.
    strJavaScript.Append(@"<script language='JavaScript'>");

    strJavaScript.Append(@"function ShowCal(doc){alert(doc);" + 
        @" document.all[doc].style.visibility='visible';}</script>");

    Page.RegisterClientScriptBlock("CoolCalJavaScript", 
                                    strJavaScript.ToString());  

    //Code to emite Javascript that will assign Value to given control
    strJavaScript.Remove(0,strJavaScript.Length);

    strJavaScript.Append(@"<script language='JavaScript'>");

    strJavaScript.Append(@"function AssignValue(ctrl,val,doc)" + 
           @"{ document.all[ctrl].value=val;document.all[doc]" + 
           @".style.visibility='hidden';}</script>");

    Page.RegisterClientScriptBlock("CoolCalJavaScript1", 
                                    strJavaScript.ToString());
}

Navigation Button

We need four navigation buttons. Navigation to previous year, previous month, next year, and next month. We need Click event to be trapped on server side as we want to change the visible date of the calendar according to button click.

C#
/// <SUMMARY>
/// To Navigate to Previous Year
/// </SUMMARY>
private void lblPrev_Click(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    cal.VisibleDate = cal.VisibleDate.AddMonths(-1);
    ViewState["visibility"]=true;     
    setLabel();
}

/// <SUMMARY>
/// To navigate to preveious Month
/// </SUMMARY>
private void lblFirst_Click(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    cal.VisibleDate = cal.VisibleDate.AddYears(-1);
    ViewState["visibility"]=true;     
    setLabel();
}

/// <SUMMARY>
/// To Navigate to Next Month
/// </SUMMARY>
private void lblNext_Click(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    cal.VisibleDate = cal.VisibleDate.AddMonths(1);
    ViewState["visibility"]=true;     
    setLabel();
}

/// <SUMMARY>
/// To Navigate to Next Year
/// </SUMMARY>
private void lblLast_Click(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    cal.VisibleDate = cal.VisibleDate.AddYears(1);
    ViewState["visibility"]=true;     
    setLabel();
}

Similarly, we need to navigate to selected month and selected year, when month and year changes.

C#
/// When Month is changed through Dropdown box
/// </SUMMARY>
private void cboyear_SelectedIndexChanged(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    DropDownList cboYear=((DropDownList)Controls[7]);
    cal.VisibleDate =Convert.ToDateTime(cal.VisibleDate.ToString("MM") + 
                                  "/15/" + cboYear.SelectedItem.Value );
    ViewState["visibility"]=true; 
    setLabel();
}

/// <SUMMARY>
/// When Year is changed through Dropdown box
/// </SUMMARY>
private void cboMonth_SelectedIndexChanged(object sender, EventArgs e)
{
    Calendar cal=((Calendar)Controls[12]);
    DropDownList cboMonth=((DropDownList)Controls[6]);
    cal.VisibleDate =Convert.ToDateTime(cboMonth.SelectedItem.Value  + 
                           "/15/" + cal.VisibleDate.ToString("yyyy"));
    ViewState["visibility"]=true;         
    setLabel();
}

Calendar control without PostBack

We don't want our calendar to have round trip when user selects a date. As we should assign selected date to assigned control using JavaScript, we need JavaScript code as well as we need to modify calendar control to have days as hyperlink calling that JavaScript.

JavaScript

C#
//Code to emite Javascript that will assign Value to given control
strJavaScript.Remove(0,strJavaScript.Length);
strJavaScript.Append(@"<script language='JavaScript'>");
strJavaScript.Append(@"function AssignValue(ctrl,val,doc)" + 
      @"{ document.all[ctrl].value=val;document.all[doc]." + 
      @"style.visibility='hidden';}</script>");
Page.RegisterClientScriptBlock("CoolCalJavaScript1",strJavaScript.ToString());

Link Creation

We have to trap DayRender event and modify the contents for the day. First of all, we should remove all existing controls and then add link which will call our JavaScript when day is selected.

C#
/// <SUMMARY>
/// Modify Style of Date Displayed on the Calender to link,
/// also remove autopost back to JavaScript
/// </SUMMARY>
private void cal_DayRender(object sender, DayRenderEventArgs e)
{
    // Clear the link from this day
    e.Cell.Controls.Clear();
    if(!e.Day.IsOtherMonth)   
    {

        // Add the custom link
        System.Web.UI.HtmlControls.HtmlGenericControl Link = 
             new System.Web.UI.HtmlControls.HtmlGenericControl();
        Link.TagName = "a";
        Link.InnerText = e.Day.DayNumberText;

        Link.Attributes.Add("href","javascript:AssignValue('"+ 
                  ControlToAssign +"','"+e.Day.Date.ToString(Format) 
                  +"','"+this.ID+@":Cal')");
        Link.Attributes.Add("style","color:white;TEXT-DECORATION: none");

        // Now add our custom link to the page
        e.Cell.Controls.Add(Link);
    }
    else
    {
        // Add the custom link
        System.Web.UI.HtmlControls.HtmlGenericControl Link = 
              new System.Web.UI.HtmlControls.HtmlGenericControl();
        Link.TagName = "a";
        Link.InnerText = e.Day.DayNumberText;

        Link.Attributes.Add("href","#");
        Link.Attributes.Add("style","color:white;TEXT-DECORATION: none");
    }

    if(e.Day.IsToday)
    {
        e.Cell.BackColor = System.Drawing.Color.LightGray;
    }
}

Final Touch

We need to define properties like BackgroundColor, ForeColor, HeadingColor, HeadingForeColor, VisibleDate, DateFormat, CurrentDateColor, ImgPath (to display initial image), Text (initial text), and the most important AssignToControl. Due to time constraints, I am not able to implement all but I have implemented the important one. You can get this in source code submitted with this article.

History

  • Created on 10th October, 2004.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Currently he is working as Senior Soft. Engineer at Cognizant Technology Solution.He is involved in various project activities like System Architecture, Design, and Development. He is fond of conduction training for various technologies. He has have worked on various language and platforms. He is Microsoft and Oracle Certified professional. He is spending quantity and quality time in .Net world. He had also spoiled his hand with java, too.
If work is not demanding, he spends good time with his wife, Purvi.He
blogs
at WebDevs.com.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 418578210-Jan-11 10:37
Member 418578210-Jan-11 10:37 
GeneralMy vote of 5 Pin
sabithpaul6-Jan-11 1:03
sabithpaul6-Jan-11 1:03 
Generalurgent Pin
pramod kumar patil15-Apr-08 19:14
pramod kumar patil15-Apr-08 19:14 
QuestionJavascript error Pin
Parthasarathy Mandayam5-Dec-07 4:34
Parthasarathy Mandayam5-Dec-07 4:34 
I'm trying to use the control in the edittemplate of a gridview but when I give the "Controltoassign" property it's not finding it. It gives javascript error.

Line 38
document.all[...] is null or not an object.
My textbox control is named "TextBox2" and the calendar should post back to this but it doesn't seem to be able to find the control. I've given Controltoassign="TextBox2".
So why isn't it working?

Certified VB6, SQL 7 and ASP developer

GeneralAssigning properties Pin
Parthasarathy Mandayam4-Dec-07 8:44
Parthasarathy Mandayam4-Dec-07 8:44 
QuestionI am getting serious error. Am I alone? Pin
Novu6-Feb-07 16:42
Novu6-Feb-07 16:42 
AnswerRe: I am getting serious error. Am I alone? Pin
fuxiurong_200129-Mar-07 22:59
fuxiurong_200129-Mar-07 22:59 
GeneralDayPilot - Outlook-like calendar/scheduling control for ASP.NET (open-source) Pin
Dan Letecky28-Jun-06 10:54
Dan Letecky28-Jun-06 10:54 
GeneralCool Calendar Control Pin
satyen lulla24-Apr-06 23:22
satyen lulla24-Apr-06 23:22 
GeneralViewState Pin
mitsemaj13-Dec-05 15:39
mitsemaj13-Dec-05 15:39 
GeneralRe: ViewState Pin
mitsemaj13-Dec-05 15:43
mitsemaj13-Dec-05 15:43 
GeneralControlToAssign does not contains the proper control id Pin
Mehfuz Hossain11-Sep-05 19:14
Mehfuz Hossain11-Sep-05 19:14 
GeneralRe: ControlToAssign does not contains the proper control id Pin
syao16820-Dec-05 10:20
syao16820-Dec-05 10:20 
GeneralRe: ControlToAssign does not contains the proper control id Pin
Mehfuz Hossain20-Dec-05 17:47
Mehfuz Hossain20-Dec-05 17:47 
Generalcan't download the updated code from personal site Pin
margiex6-Jul-05 4:42
margiex6-Jul-05 4:42 
GeneralCommCtrl missing Pin
Ezra27-Jun-05 7:36
Ezra27-Jun-05 7:36 
GeneralRe: CommCtrl missing Pin
Gaurang Desai27-Jun-05 17:58
Gaurang Desai27-Jun-05 17:58 
GeneralRe: CommCtrl missing Pin
Ezra28-Jun-05 5:44
Ezra28-Jun-05 5:44 
GeneralFile or assembly name CommCtrl, or one of its dependencies, was not found. Pin
Talis92122-Feb-05 8:50
Talis92122-Feb-05 8:50 
GeneralRe: File or assembly name CommCtrl, or one of its dependencies, was not found. Pin
Talis92122-Feb-05 9:46
Talis92122-Feb-05 9:46 
GeneralRe: File or assembly name CommCtrl, or one of its dependencies, was not found. Pin
RichAlger19-Aug-05 5:56
RichAlger19-Aug-05 5:56 
Generalmaybe dumb qutione Pin
Anonymous3-Feb-05 8:32
Anonymous3-Feb-05 8:32 
GeneralLatest Code Base Pin
Member 42486016-Dec-04 5:51
Member 42486016-Dec-04 5:51 
GeneralA new bug! Pin
Derrick Lin17-Nov-04 22:03
Derrick Lin17-Nov-04 22:03 
GeneralRe: A new bug! Pin
Gaurang Desai17-Nov-04 23:46
Gaurang Desai17-Nov-04 23:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.