![]() |
Web Development »
Custom Controls »
General
Intermediate
Cool Calendar - Amazing Calendar without popupBy Gaurang DesaiThis 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. |
C#, Windows, .NET, ASP.NET, Visual Studio, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||

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.
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...!!!!!!!!!!!!!
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:
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());
}
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();
}
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.
//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());
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;
}
}
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.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 11 Oct 2004 Editor: Nishant Sivakumar |
Copyright 2004 by Gaurang Desai Everything else Copyright © CodeProject, 1999-2009 Web20 | Advertise on the Code Project |