Skip to main content
Email Password   helpLost your password?

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.

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:

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.

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:

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.

/// <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.

/// 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

//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.

/// <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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalurgent Pin
pramod kumar patil
20:14 15 Apr '08  
QuestionJavascript error Pin
Parthasarathy Mandayam
5:34 5 Dec '07  
GeneralAssigning properties Pin
Parthasarathy Mandayam
9:44 4 Dec '07  
QuestionI am getting serious error. Am I alone? Pin
Novu
17:42 6 Feb '07  
AnswerRe: I am getting serious error. Am I alone? Pin
fuxiurong_2001
23:59 29 Mar '07  
GeneralDayPilot - Outlook-like calendar/scheduling control for ASP.NET (open-source) Pin
Dan Letecky
11:54 28 Jun '06  
GeneralCool Calendar Control Pin
satyen lulla
0:22 25 Apr '06  
GeneralViewState Pin
mitsemaj
16:39 13 Dec '05  
GeneralRe: ViewState Pin
mitsemaj
16:43 13 Dec '05  
GeneralControlToAssign does not contains the proper control id Pin
Mehfuz Hossain
20:14 11 Sep '05  
GeneralRe: ControlToAssign does not contains the proper control id Pin
syao168
11:20 20 Dec '05  
GeneralRe: ControlToAssign does not contains the proper control id Pin
Mehfuz Hossain
18:47 20 Dec '05  
Generalcan't download the updated code from personal site Pin
margiex
5:42 6 Jul '05  
GeneralCommCtrl missing Pin
Ezra
8:36 27 Jun '05  
GeneralRe: CommCtrl missing Pin
Gaurang Desai
18:58 27 Jun '05  
GeneralRe: CommCtrl missing Pin
Ezra
6:44 28 Jun '05  
GeneralFile or assembly name CommCtrl, or one of its dependencies, was not found. Pin
Talis921
9:50 22 Feb '05  
GeneralRe: File or assembly name CommCtrl, or one of its dependencies, was not found. Pin
Talis921
10:46 22 Feb '05  
GeneralRe: File or assembly name CommCtrl, or one of its dependencies, was not found. Pin
RichAlger
6:56 19 Aug '05  
Generalmaybe dumb qutione Pin
Anonymous
9:32 3 Feb '05  
GeneralLatest Code Base Pin
jagdishkab
6:51 16 Dec '04  
GeneralA new bug! Pin
Derrick Lin
23:03 17 Nov '04  
GeneralRe: A new bug! Pin
Gaurang Desai
0:46 18 Nov '04  
GeneralRe: A new bug! Pin
Derrick Lin
15:28 18 Nov '04  
GeneralRe: A new bug! Pin
ScottHiett
5:26 23 Nov '04  


Last Updated 11 Oct 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009