Introduction
There are several implementations of this control on the web. I based mine on Paul Kilmer's but made many changes after I could not get it to work; many of them were changing the HTML controls for actual ASP.NET controls, as this would give me a better control in the code behind of my control, and you will see this in the code.
Creating the Control
First, you need to create a new control, in this case, I called it ctlCalendar
. In this control, you will need to add a TextBox
(TextBox1
) from the Web Forms toolbox, a Button
(Button1
) from the HTML toolbar, a Panel
(Panel1
) from the Web Forms toolbox, and finally inside this Panel
, a Calendar
control (Calendar1
) from the Web Forms toolbox. Notice that I used Button1
from the HTML toolbar because I need it not post back when the user clicks it.
This is how your control's HTML would look like:
<asp:textbox id="TextBox1" runat="server"></asp:textbox>
<input type="button" id="Button1" runat="server" value="..."><br>
<asp:Panel id="pnlCalendar" runat="server"
style="POSITION: absolute">
<asp:calendar id="Calendar1" runat="server" CellPadding="4"
BorderColor="#999999" Font-Names="Verdana" Font-Size="8pt"
Height="180px" ForeColor="Black" DayNameFormat="FirstLetter"
Width="200px" BackColor="White">
<TodayDayStyle ForeColor="Black" BackColor="#CCCCCC"></TodayDayStyle>
<SelectorStyle BackColor="#CCCCCC"></SelectorStyle>
<NextPrevStyle VerticalAlign="Bottom"></NextPrevStyle>
<DayHeaderStyle Font-Size="7pt" Font-Bold="True" BackColor="#CCCCCC">
</DayHeaderStyle>
<SelectedDayStyle Font-Bold="True" ForeColor="White" BackColor="#666666">
</SelectedDayStyle>
<TitleStyle Font-Bold="True" BorderColor="Black" BackColor="#999999">
</TitleStyle>
<WeekendDayStyle BackColor="LightSteelBlue"></WeekendDayStyle>
<OtherMonthDayStyle ForeColor="#808080"></OtherMonthDayStyle>
</asp:calendar>
</asp:Panel>
Creating the functionality
Now, we will use two functions to create the functionality; the first one is the Page_Load
on which we initialize our controls and Calendar1_SelectionChanged
.
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack)
{
this.TextBox1.Text = System.DateTime.Now.ToShortDateString();
this.pnlCalendar.Attributes.Add("style",
"DISPLAY: none; POSITION: absolute");
}
else
{
string id = Page.Request.Form["__EVENTTARGET"].Substring(0,
Page.Request.Form["__EVENTTARGET"].IndexOf(":"));
if (id != this.ID)
{
this.pnlCalendar.Attributes.Add("style",
"DISPLAY: none; POSITION: absolute");
}
else
{
this.pnlCalendar.Attributes.Add("style","POSITION: absolute");
}
}
Page.RegisterClientScriptBlock("Script_Panel" + this.ID,
"<script> function On"+this.ID+"Click() { if(" +
this.ID + "_pnlCalendar.style.display == \"none\") "
+ this.ID + "_pnlCalendar.style.display = \"\"; else "
+ this.ID+"_pnlCalendar.style.display = \"none\"; } </script>");
this.Button1.Attributes.Add("OnClick","On"+this.ID+"Click()");}
In the Page_Load
function, the first thing I do is initiate the Text
property of the TextBox
with the current date, just not to leave it blank. Then comes a little trick that I learned while making this article, when you change the month in the calendar, it does a post back, and if we have many calendars, we need to know which control exactly did the post back so we won�t hide that specific calendar; and that�s where we will access the __EVENTTARGET
hidden field that will tell us which calendar is in action.
string id = Page.Request.Form["__EVENTTARGET"].Substring(0,
Page.Request.Form["__EVENTTARGET"].IndexOf(":"));
Then I just compare if it�s not the calendar I am working with, I�ll hide it or else just leave it with an absolute position.
if (id != this.ID)
{
this.TextBox1.Text = System.DateTime.Now.ToShortDateString();
this.pnlCalendar.Attributes.Add("style",
"DISPLAY: none; POSITION: absolute");
}
else
{
this.pnlCalendar.Attributes.Add("style","POSITION: absolute");
}
In the next lines, I register a client script, this is the actual JavaScript that is going to show the calendar when they click the button, it looks a bit complex so I will explain it in depth.
When you add multiple controls to the same form, each control gets its own name (in this case, ctlCalendar
) followed by a number (for the first control, it will be CtlCalendar1
). Knowing this, I wrote a line that will create a JavaScript function for each control created, using the this.ID
property:
Page.RegisterClientScriptBlock("Script_" + this.ID,
"<script> function On"+this.ID+"Click() { if("+this.ID+
"_pnlCalendar.style.display == \"none\") "+this.ID+
"_pnlCalendar.style.display = \"\"; else "+this.ID+
"_pnlCalendar.style.display = \"none\"; } </script>");
And then added that function to the Button
itself.
this.Button1.Attributes.Add("OnClick","On"+this.ID+"Click()");
Also, in order to access the Panel
's actual properties, you need to know your Panel
's name. This name is composed when generated as the control's name (ctlCalendar1
) and then the Panel
's name (Panel1
) separated with an underscore (ctlCalendar1_Panel1
for the first ctlCalendar
added to the page).
Let's see the JavaScript code generated for the first Calendar in the page:
<Script>
function OnCtlCalendar1Click()
{
if(CtlCalendar1_pnlCalendar.style.display == "none")
CtlCalendar1_pnlCalendar.style.display = "";
else
CtlCalendar1_pnlCalendar.style.display = "none";
}
</script>
Here, we see how the function has its own name (OnCtlCalendar1Click()
) and we also generated the final name of the Panel
(CtlCalendar1_pnlCalendar
) in order to make it invisible with the display
property. The following controls you add will take consecutive numbers and so will their functions, allowing you to use as many controls as you need.