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类的属性
| 属 性 | 类 型 | 说 明 |
| Date | DateTime | 由Day表示的日期。只读 |
| DayNumberText | String | 该日期的日编号的等效字符串。只读 |
| IsOtherMonth | Boolean | 指示该日期是否显示当前月份以外的月份。只读 |
| IsSelectable | Boolean | 指示该日期是否可以被选择。非只读 |
| IsSelected | Boolean | 指示该日期是否被选择 |
| IsToday | Boolean | 指示该日期是否是今天 |
| IsWeekend | Boolean | 指示该日期是否是周末 |
如果这个日期既在当前月又是周末,则为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中高亮显示的代码。