Calendar控件编程 Programming the Calendar Control Calendar控件提供了3个事件,它们并不是从其他控件类继承。通过为事件提供事件处理程序,可以看到日历是如何运行的。这些事件是: l SelectionChanged l DayRender l VisibleMonthChanged 下面详细描述这些事件。 SelectionChanged事件 当用户在Calender控件中选择一天、一周或整个月份时,将触发SelectionChanged事件。以编程方式选择时,并不触发该事件。该事件处理程序传递一个EventArgs类型参数。 接下来的Calendar-SelectionChanged事件说明了如何处理SelectionChanged事件。当选择一个新日期时,它将显示今天的日期、选中的日期及选中的天数。 为了创建该示例,可将上一个示例Calendar-Styles复制为新网站,并命名为Calendar- SelectionChanged。在设计视图中选择Calendar控件,并双击属性窗口的事件图标(闪 电图标),为SelectionChanged事件添加具有默认名称的事件处理程序。 这样,将在内容文件的Calendar控件声明中添加一个OnSelectionChanged属性。同时,打开代码隐藏文件,代码中已经包含了事件处理程序的框架。可以在该方法框架中输入自己的代码。在Calendar1_SelectionChanged方法中输入示例5-18中高亮显示的代码,以及高亮显示的lblCountUpdate辅助方法。 示例5-18:Default.aspx.cs from Calendar-SelectionChanged using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Calendar1_SelectionChanged(object sender, EventArgs e) { lblTodaysDate.Text = "Today's Date is " + Calendar1.TodaysDate.ToShortDateString( ); if (Calendar1.SelectedDate != DateTime.MinValue) lblSelected.Text = "The date selected is " + Calendar1.SelectedDate.ToShortDateString( ); lblCountUpdate( ); } private void lblCountUpdate( ) { lblCount.Text = "Count of Days Selected: " + Calendar1.SelectedDates.Count.ToString( ); } } 还必须在页面的底部添加3个Label控件以便显示日历信息。示例5-19列出了内容文件,其中忽略了Calendar控件的样式属性,因为它们与上一个示例相同。从上一个示例修改的代码已经进行了高亮显示。 示例5-19:从Calendar-SelectionChanged节选的default.aspx.cs(Calendar样式属性未显示) <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W "http://www./TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www./1999/xhtml" > <head runat="server"> <title>Calendar</title> </head> <body> <form id="form1" runat="server"> <div> <h1>Calendar Control</h1> <h2>SelectionChanged</h2> <asp:Calendar ID="Calendar1" runat="server" OnSelectionChanged="Calendar1_SelectionChanged"> </asp:Calendar> <br/> <asp:Label id="lblCount" runat="server" /> <br/> <asp:Label id="lblTodaysDate" runat="server" /> <br/> <asp:Label id="lblSelected" runat="server" /> </div> </form> </body> </html> 运行页面并选择一个日期后,将显示图5-17所示的结果。 在示例5-19中,可以看到该示例为Calendar添加了OnSelectionChanged事件处理程序。该处理程序指向代码隐藏文件中的Calendar1_SelectionChanged方法,如示例5-18所示。在Calendar控件之后添加了3个Label控件。第一个Lable控件lblCount,用于显示选择的天数。另外两个Label控件,lblTodaysDate和lblSelected,分别用于显示今天的日期和当前选定的日期。 在SelectionChanged事件处理方法中,这3个Label控件都设置了Text属性。在示例5-18中,可以看到lblTodaysDate由Calendar控件的TodaysDate属性填充,使用的代码如下所示: lblTodaysDate.Text = "Today's Date is " + Calendar1.TodaysDate.ToShortDateString( ); 图5-17:选择日期后的Calendar-SelectionChanged Calendar控件的ID为Calendar1,该控件的TodaysDate属性返回System.DateTime类型的值。为了给Text属性(String类型)赋值,必须把DateTime转换为String。该转换由ToShortDateString方法实现。 DateTime具有多个将DateTime对象转换为其他格式的方法,包括表5-18所显示的方法。 表5-18 DateTime转换到字符串的方法
尽管不是ASP.NET特有的,DateTime对于获取日期和时间信息是非常有用的。该结构的一些只读属性在表5-19中列出。 表5-19 DateTime的只读属性
lblSelected由下面的代码填充: if (Calendar1.SelectedDate != DateTime.MinValue) lblSelected.Text = "The date selected is " + Calendar1.SelectedDate.ToShortDateString( ); 为了检查是否选择一日期,可以检测当前选择日期Calendar1.SelectedDate是否等于DateTime.MinValue。DateTime.MinValue是一个表示DateTime最小可能值的常量。如果没有选择一个日期SelectedDate属性,则默认为DateTime.MinValue。MinValue的值为12:00:00 AM, 如果用户选择了一个日期,lblSelected的Text属性将被设置为SelectedDate属性值的字符串。 Label控件lblCount显示选择的天数。SelectionChanged事件处理程序调用lblCountUpdate方法,该方法设置lblCount的Text属性。为设置该控件,必须确定选择了多少个日期。Calendar控件有一个SelectedDates属性,它返回一个SelectedDates集合。SelectedDates集合是一个表示Calendar控件所有选定的DateTime对象的集合。Count是SelectedDatesCollection对象的一个属性,它返回一个包含集合中日期数量 的整型值。由于Count属性是一个整型值,所以必须使用ToString方法把它转换为一个字符串,才可以给Text属性赋值。 Calendar1.SelectedDates.Count.ToString( ) 尽管SelectedDates(选定日期的集合)和SelectedDate(单个选定的日期)都包含DateTime对象,但只存储Date值,时间值被设置null(在C#语言中)。 SelectedDates集合中的日期是按日期升序排列的。更新SelectedDates集合时,SelectedDate属性自动更新为包含SelectedDates集合的第一个对象。 用户单击月份标题两边的月份导航控件会在月份之间导航。用户也可以单击一天以选中它,单击星期选择控件选定一个星期,单击月份选择控件选中整个月份。 然而,可以给用户更多的灵活性。为此,必须添加几个控件和方法。将当前示例Calendar-SelectionChanged复制到一个新网站,并命名为Calendar-MoreSelections。 为使用户可以直接导航到一年中的任意月份,可添加一个包含一年中所有月份的DropDownList和一个标签为TGIF的按钮,该按钮选中当前显示月份所有的星期5。 Calendar控件还可以让用户选择一定范围内的日期。您可能希望用户可以使用标准的Windows方法,按住Ctrl或Shift键选择日期的,但这样不行。不过在页面上放几个控件用于选定开始日期和结束日期。在Calendar-MoreSelections中,将添加两个TextBox控件用于输入一定范围内的开始日期和结束日期,以及一个Button控件来强制选择该范围内的日期。 实现该功能的内容文件在示例5-20中列出。同样,为了简洁,所有与Calendar的样式相关的属性都被省略,它们与5-17示例中的相同。从上一个示例修改的代码(它们表示添加的控件)被高亮显示。 示例5-20:Calendar-MoreSelections的Default.aspx,省略Calendar的样式属性 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W "http://www./TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www./1999/xhtml" > <head runat="server"> <title>Calendar</title> </head> <body> <form id="form1" runat="server"> <div> <h1>Calendar Control</h1> <h2>More Selections</h2> <asp:Calendar ID="Calendar1" runat="server" OnSelectionChanged="Calendar1_SelectionChanged"> </asp:Calendar> <br/> <asp:Label id="lblCount" runat="server" /> <br/> <asp:Label id="lblTodaysDate" runat="server" /> <br/> <asp:Label id="lblSelected" runat="server" /> <br/> <table> <tr> <td> Select a month: </td> <td> <asp:DropDownList id= "ddl" runat="server" AutoPostBack="true" onSelectedIndexChanged = "ddl_SelectedIndexChanged"> <asp:ListItem text="January" value="1" /> <asp:ListItem text="February" value="2" /> <asp:ListItem text="March" value="3" /> <asp:ListItem text="April" value="4" /> <asp:ListItem text="May" value="5" /> <asp:ListItem text="June" value="6" /> <asp:ListItem text="July" value="7" /> <asp:ListItem text="August" value="8" /> <asp:ListItem text="September" value="9" /> <asp:ListItem text="October" value="10" /> <asp:ListItem text="November" value="11" /> <asp:ListItem text="December" value="12" /> </asp:DropDownList> </td> <td> <asp:Button id="btnTgif" runat="server" text="TGIF" onClick="btnTgif_Click"/> </td> </tr> <tr> <td colspan="2"> </td> </tr> <tr> <td colspan="2"><b>Day Range</b></td> </tr> <tr> <td>Starting Day</td> <td>Ending Day</td> </tr> <tr> <td> <asp:TextBox id= "txtStart" runat="server" Width="25" MaxLength="2" /> </td> <td> <asp:TextBox id= "txtEnd" runat="server" Width="25" MaxLength="2" /> </td> <td> <asp:Button id="btnRange" runat="server" text="Apply" onClick="btnRange_Click" /> </td> </tr> </table> </div> </form> </body> </html> 示例5-21列出了该示例完整的代码隐藏文件。从上一个示例中修改的代码被高亮显示。 示例5-21:Calendar-MoreSelections的Default.aspx.cs using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { /// <summary> /// 初始化Calendar1的 VisibleDate属性和ddl的选定项 /// 并显示相关信息 /// property /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { Calendar1.VisibleDate = Calendar1.TodaysDate; ddl.SelectedIndex = Calendar1.VisibleDate.Month - 1; } lblTodaysDate.Text = "Today's Date is " + Calendar1.TodaysDate.ToShortDateString( ); } protected void Calendar1_SelectionChanged(object sender, EventArgs e) { lblSelectedUpdate( ); lblCountUpdate( ); txtClear( ); } private void lblSelectedUpdate( ) { if (Calendar1.SelectedDate != DateTime.MinValue) lblSelected.Text = "The date selected is " + Calendar1.SelectedDate.ToShortDateString( ); } private void lblCountUpdate( ) { lblCount.Text = "Count of Days Selected: " + Calendar1.SelectedDates.Count.ToString( ); } protected void ddl_SelectedIndexChanged(Object sender, EventArgs e) { Calendar1.SelectedDates.Clear( ); lblSelectedUpdate( ); lblCountUpdate( ); Calendar1.VisibleDate = new DateTime(Calendar1.VisibleDate.Year, Int32.Parse(ddl.SelectedItem.Value), 1); txtClear( ); } protected void btnTgif_Click(Object sender, EventArgs e) { int currentMonth = Calendar1.VisibleDate.Month; int currentYear = Calendar1.VisibleDate.Year; Calendar1.SelectedDates.Clear( ); for (int i = 1; i <= System.DateTime.DaysInMonth(currentYear, currentMonth); i++) { DateTime date = new DateTime(currentYear, currentMonth, i); if (date.DayOfWeek == DayOfWeek.Friday) Calendar1.SelectedDates.Add(date); } lblSelectedUpdate( ); lblCountUpdate( ); txtClear( ); } protected void btnRange_Click(Object sender, EventArgs e) { int currentMonth = Calendar1.VisibleDate.Month; int currentYear = Calendar1.VisibleDate.Year; DateTime StartDate = new DateTime(currentYear, currentMonth, Int32.Parse(txtStart.Text)); DateTime EndDate = new DateTime(currentYear, currentMonth, Int32.Parse(txtEnd.Text)); Calendar1.SelectedDates.Clear( ); Calendar1.SelectedDates.SelectRange(StartDate, EndDate); lblSelectedUpdate( ); lblCountUpdate( ); } private void txtClear( ) { txtStart.Text = ""; txtEnd.Text = ""; } } 运行Calendar-MoreSelections,并选择一个日期范围后的结果如图5-18所示。 所有选择控件都位于静态HTML表格中,这样可以控制页面布局。
DropDownList控件的ListItem对象包含了作为Text属性的月份名称和作为Value属性的月份数值。 Calendar1_SelectionChanged的大部分代码被重构到一个单独的方法lblSelect- edUpdate中,该方法更新Label控件lblSelected的Text属性。这个方法随后被Calendar1_SelectionChanged方法及其他几个方法调用。此外,还有一个辅助方法txtClear,该方法用于清空Start和End Day文本框。
图5-18:选择一个日期范围后的Calendar-MoreSelections ddl_SelectedIndexChanged事件处理方法首先清空SelectedDates集合: Calendar1.SelectedDates.Clear( ); 调用lblSelectedUpdate方法以清空包含第一个选定日期的Label控件,调用lbl- SelectedUpdate方法清空包含选定天数信息的Label控件。然后,Calendar控件的VisibleDate属性设置为新选定月份的第一天: Calendar1.VisibleDate = new DateTime(Calendar1.VisibleDate.Year, Int32.Parse(ddl.SelectedItem.Value), 1); VisibleDate属性的类型为DateTime,它初始化了一个新DateTime对象。DateTime结构和.NET Framework中的许多对象一样,使用一个重载的构造函数。该对象可以有不止一个构造函数,每个构造函数都由不同的参数类型或不同的参数个数区分。 因此,需要实例化一个包含该日期的DateTime对象。它需要3个整型参数:year、month和day。第一个参数是Calendar1.VisibleDate.Year,最后一个参数是1,它本身就是一个整型值。month参数来自DropDownList控件选定项的Value属性。Value属性是个字符串,尽管它包含的字符类似于一个整型值,但并非整型值。因此,它必须被转换为整型值,它使用下面的代码: Int32.Parse(ddl.SelectedItem.Value)
TGIF按钮被命名为btnTgif,并拥有一个Click事件的事件处理程序btnTgif_Click。该方法遍历当前可见月份的每一天,并检测是否是星期五。如果是,则将该日期添加到SelectedDates集合中。 首先,btnTgif_Click方法获取当前可见月的月份和年份,它使用Calendar控件的VisibleDate属性(该属性是一个DateTime对象)来获取这个DateTime对象的Month和Year属性: int currentMonth = Calendar1.VisibleDate.Month; int currentYear = Calendar1.VisibleDate.Year; 然后,清空当前所有选择的日期: Calendar1.SelectedDates.Clear( ); 现在,开始遍历,for循环的限定部分由DateTime对象的DaysInMonth属性决定该月份中的天数。使用的月份由currentYear和currentMonth两个参数指定: System.DateTime.DaysInMonth(currentYear, currentMonth) 在for循环中,DateTime类型的变量date被赋值为月份中的每一天。同样,该DateTime对象由year、month和day几个参数实例化。关键的问题来了,“这一天是星期五吗?”,如果是,则把该日期添加到SelectedDates中: DateTime date = new DateTime(currentYear, currentMonth, i); if (date.DayOfWeek == DayOfWeek.Friday) cal.SelectedDates.Add(date); 最后,遍历月份的每一天后,调用lblSelectedUpdate方法更新显示第一个选择日期的标签,调用lblCountUpdate方法更新显示选中天数的标签,调用txtClear清空开始和结束日期文本框。 注意代码隐藏文件中的Page_Load方法。如代码中的注释所解释的,它让每一次单击TGIF按钮时,即使在月份变化之前,页面都能正常运行。如果没有Page_Load事件处理程序,则页面只有在月份被更改一次后,才能正常使用TGIF按钮。btnTgif_Click法使用VisibleDate属性设置当前月份和年份变量。如果在页面初始加载时没有初始化该属性,那么赋值到这些变量的值将与可见月份不符。 此外,更新显示今天日期的Label控件lblTodaysDate的代码,由SelectionChanged方法移到了Page_Load方法中,这样,将使lblTodaysDate更敏感,即每次都能正确显示信息。 选择日期范围的控件与选择月份的控件位于同一个静态HTML表格中。其中有两个文本框,txtStart用于开始日期,txtEnd用于结束日期。在这个示例中,TextBox控件
的Width和MaxLength属性提供了对用户输入的限制。在产品应用程序中,可能需要添加验证控件(在第8章描述),以避免用户输入无效字符或超过一定数量的字符而得到讨厌的错误信息。 CalendarMoreSelections中提供的选择日期范围的用户界面很明显是有限制的,因为,不能跨多个月。可以很容易地提供3个独立的控件:一个用于开始日期、一个用于结束日期、一个用于日期范围。如果在月份更改后不再次应用选择,将无法选定日期范围,因为VisibleMonthChanged并未捕获事件(参考本章后面的“VisibleMonthChanged事件”一节)。 txtClear辅助方法用于清空日期范围选择文本框。该方法在其他方法中被调用。 应用按钮命名为btnRange,Click事件处理程序为btnRange_Click。在btnRange_Click中,将当前月份和年份赋值给整型变量: int currentMonth = Calendar1.VisibleDate.Month; int currentYear = Calendar1.VisibleDate.Year; 声明两个DateTime变量来保存开始日期和结束日期: DateTime StartDate = new DateTime(currentYear, currentMonth, Int32.Parse(txtStart.Text)); DateTime EndDate = new DateTime(currentYear, currentMonth, Int32.Parse(txtEnd.Text)); 与前面描述的月份DropDownList类似,DateTime需要year,month和day。已经有了整型值的year和month,只需要day了。通过把指定文本框的文本转换为整型值可以得到day。 这个方法获取了DateTime类型的开始日期和结束日期之后,将清除所有当前选择的日期并使用SelectedDatesCollection类的SelectRange方法添加日期范围到Selec- tedDates集合中: Calendar1.SelectedDates.Clear( ); Calendar1.SelectedDates.SelectRange(StartDate, EndDate); SelectRange方法需要两个参数:开始日期和结束日期。
DayRender事件 Calendar控件不直接支持日期绑定。然而,可以修改单个日期单元格的内容和格式。这样可从数据库中获取数据,以便进行一些处理后把它们置于指定的单元格中。 在Calendar控件呈现到客户端浏览器之前,将组成创建该控件的所有组件。随着创建每个单元格,将引发DayRender事件。可以捕获该事件。 DayRender事件处理程序接收两个DayRenderEventArgs类型的参数。该对象有两个属性,它们可以用编程方式读取: Cell 表示要呈现的单元格的表格单元格对象。 Day 表示呈现在单元格中日期的CalendarDay对象。 下一个示例Calendar-Events将说明DayRender事件(下面VisibleMonthChanged事件一节将构建同一个示例)。所有的周末将显示不同的背景颜色, 将上一个示例Calendar-MoreSelections复制为新网站,并命名为Calendar-Events。在这一示例中将捕获DayRender事件。只需要修改两个地方。 首先,在设计视图中选择Canlendar1,单击属性窗口中的事件图标(闪电图标),并双击DayRender旁边的文本框。在内容文件的Calendar1控件声明中添加如下属性: OnDayRender="Calendar1_DayRender" 该操作同时也将在代码隐藏文件中创建默认的事件处理程序,并将光标置于此以准备输入代码。在该代码框架中输入示例5-22中高亮显示的代码。 示例5-22:Calendar-Events的DayRender事件处理程序 protected void Calendar1_DayRender(object sender, DayRenderEventArgs e) { // 注意这将会覆盖WeekendDayStyle if (!e.Day.IsOtherMonth && e.Day.IsWeekend) e.Cell.BackColor = System.Drawing.Color.LightGreen; // 在单元格中显示“Happy New Year!” if (e.Day.Date.Month == 1 && e.Day.Date.Day == 1) e.Cell.Controls.Add(new LiteralControl("<br/>Happy New Year!")); }
Calendar1_DayRender先把周末的颜色设为LightGreen。该控件有一个Weekend- DayStyle属性,它把周末的颜色设为LavenderBlush。DayRender方法将覆盖Weekend- DayStyle。(它们之间的差别在纸版图书中可能看不到,但在浏览器中运行该页面时可看到这种差别)。 该事件处理程序传递两个参数: void DayRender(Object sender, DayRenderEventArgs e) DayRenderEventArgs包含Day和Cell的属性。Day用于检测是否在当前月份及是否是周末: (!e.Day.IsOtherMonth && e.Day.IsWeekend) Day是一个CalendarDay类型属性。表5-20中列出了CalendarDay类的属性(除Is- Selectable属性之外都是只读的)。 表5-20 CalendarDay类的属性
如果这个日期既在当前月又是周末,则为Cell.BackColor属性设置一个颜色: e.Cell.BackColor=System.Drawing.Color.LightGreen; Calendar1_DayRender方法然后检测选定的日期是否是元旦。同样,使用DayRender- EventArgs对象的Day属性检测日期是否是 if (e.Day.Date.Month == 1 && e.Day.Date.Day == 1) 如果是 e.Cell.Controls.Add(new LiteralControl("<br/>Happy New Year!")); 牢记一点,与所有的ASP.NET服务器控件一样,真正发送到浏览器的是HTML。因此,日历以HTML表格的方式呈现到浏览器。日历上每个可选择的组件都有与之关联的超链接标签,以及完成回发工作的JavaScript(当光标位于可单击的元素上时可以证实这
一点:当链接可以单击时,浏览器的状态行将显示要执行的JavaScript函数的名称)。使用LiteralControl控件在HTML单元格中按原样插入文本。查看浏览器源文件中的代码段可以看到下面的内容: <td align="Center" style="color:Black;background-color:White; font-family:Arial;width:12%;"> <a href="javascript:_ _doPostBack('cal','selectDay7')" style="color:Black"> 1 </a> <br/>Happy New Year! </td> 图5-19:Calendar-Events显示了执行DayRender事件的结果 VisibleMonthChanged事件 Calendar控件还提供了一个事件VisibleMonthChanged以确定用户是否更改了月份。通过扩展当前示例Calendar-Events可以捕获该事件。 使用与添加Calendar1的DayRender事件相同的方式,添加VisibleMonthChanged事件的事件处理程序。这将为内容文件的Calendar1声明添加如下属性: OnVisibleMonthChanged="Calendar1_VisibleMonthChanged">
在代码隐藏文件中,创建默认事件处理程序代码结构,并将光标置于此以准备输入代码。在这个代码结构中输入示例5-23中高亮显示的代码。 示例5-23:Calendar-Events中的VisibleMonthChanged事件处理程序 protected void Calendar1_VisibleMonthChanged(object sender, MonthChangedEventArgs e) { if ((e.NewDate.Year > e.PreviousDate.Year) || ((e.NewDate.Year == e.PreviousDate.Year) && (e.NewDate.Month > e.PreviousDate.Month))) lblMonthChanged.Text = "My future's so bright..."; else lblMonthChanged.Text = "Back to the future!"; Calendar1.SelectedDates.Clear( ); lblSelectedUpdate( ); lblCountUpdate( ); txtClear( ); } 还需要在内容文件中的Calendar控件前面添加一个Label控件,并命名为lblMon- thChanged: <asp:Label id="lblMonthChanged" runat="server" /> Calendar1_VisibleMonthChanged事件处理程序接收一个MonthChangedEventArgs类型的参数。该参数包含两个从程序读取的属性: NewDate 表示Calendar当前显示的月份。 PreviousDate 表示Calendar以前显示的月份。 在Calendar1_VisibleMonthChanged方法中,检测这些值哪一个更早。根据检测结果,两个字符串之一将赋给lblMonthChanged的Text属性。 最后,使用如下代码清除从日历选择的日期、更新日历下面的文本字符串、清除日期范围编辑框的内容: Calendar1.SelectedDates.Clear( ) lblSelectedUpdate( ) lblCountUpdate( ) txtClear( ) 运行Calendar-Events并导航到一个月份后,结果如图5-20所示。
图5-20:触发VisibleMonthChanged事件后的VisibleMonthChanged |
|