定制化日历控件的实现

在开发一个网站应用程序时,客户提出了一个具体需求:需要一个能够同时显示5月和6月的日历,并在5月底到6月中旬的时间段内选择日期。对于某些用户类别,他们希望允许这些用户在比赛期间最多选择3天的日期。同时,需要禁止用户选择某些特殊日期。最后,日历视图必须以月初的日期初始化。虽然市场上有许多日历控件,但很难找到一个同时满足这些需求且外观良好的控件。经过一番搜索,找到了Yahoo的日历控件,它几乎满足了所有需求,但缺少设置禁止日期和设置最大日期数量的功能。因此,深入分析了其JavaScript代码,并成功地添加了这两个功能。

在本篇文章中,将不会详细解释对calendar.js文件所做的更改,而是介绍实现这个Web控件时遇到的一些有趣点。

在本节中,将解释如何使用这个控件以及它的属性。

代码

要实例化一个日历,过程非常简单。以下是服务器端的代码示例:

YuiExtCalendar.MinDateSelectable = new DateTime(2011, 6, 1); YuiExtCalendar.MaxDateSelectable = new DateTime(2011, 10, 1); YuiExtCalendar.InitialSelectedDates = new List<DateTime>() { new DateTime(2011, 6, 24), new DateTime(2011, 6, 25) }; YuiExtCalendar.ForbiddenDates = new List<DateTime>() { new DateTime(2011, 7, 10), new DateTime(2011, 7, 11) }; YuiExtCalendar.NumberOfSelectableDates = 5; YuiExtCalendar.HeaderTitle = "日历标题:"; YuiExtCalendar.DataBind();

以下是客户端的代码示例:

<cc1:YuiExtCalendar ID="YuiExtCalendar" Mode="Flat" runat="server" />

Mode属性是一个枚举值,有3种可能的值。Flat值显示一个像传统控件一样的日历。CollapsibleCalendarWithDateSummary值显示一个日历,当点击带有ImageCalendarUrl属性的图片时。如果使用CollapsibleCalendarWithDateSummaryCollapsibleCalendarWithNoDateSummary,必须提供ImageCalendarUrl属性。CollapsibleCalendarWithDateSummary生成一个选择的日期的迷摘要,这在关闭日历时很有用。CollapsibleCalendarWithNoDateSummary值与CollapsibleCalendarWithDateSummary完全相同,只是没有摘要。

MinDateSelectableMaxDateSelectabledateTime属性,必须一起设置。它允许在日历上限制日期选择的区间。例如,如果希望日历允许在2012年9月19日和2012年12月9日之间选择日期(必须设置MinDateSelectable = 2012-09-19MaxDateSelectable = 2012-12-09)。在服务器端设置这个变量。

InitialSelectedDates属性是一个List<datetime>,它允许在页面加载时选择一些日期。在服务器端设置这个变量。

ForbiddenDates属性是一个List<datetime>,它允许设置一个禁止日期列表:这些日期将不可选择。可以在calendar.css中自定义这些禁止日期的UI样式:

/*禁止样式*/ .yui-skin-sam .yui-calendar td.calcell.forbidden{ ##在这里自定义## }

在服务器端设置这个变量。

NumberOfSelectableDates是一个整型属性,它允许控制选择的日期数量,例如:如果希望一个用户在日历中最多选择4个日期,将这个变量设置为4。在服务器端或客户端设置这个变量。

HeaderTitle是一个字符串属性,它允许设置日历头部的标题。在服务器端或客户端设置这个变量。

ImageCalendarUrl是一个字符串属性。它是CollapsibleCalendarWithNoDateSummaryCollapsibleCalendarWithNoDateSummary模式中的图片URL。建议在客户端设置这个变量。

SelectedValues属性是一个列表,它允许在按钮发生回发时获取用户选择的日期。

如果点击日历上写的两个月中的任何一个,可以选择希望查看的月份:

使用代码

选择实现日历控件,继承CompositeControl类。为什么?因为当想生成层次化的HTML标签时,子控件的ID会自动命名为'ControlId_childrenControlId_subchildrenControlId_subsubchildrenControlId'等...这是一个有趣的属性,主要在JavaScript中使用它(替换_ID_)。当在Defaut.aspx页面上测试程序时,查看生成的客户端HTML代码源。

CreateChildControls是一个覆盖方法,它允许生成HTML。

OnPreRender是一个覆盖方法,它允许附加到头部JavaScript文件引用和CSS文件引用,以及生成JavaScript以构建和显示日历。

每个JavaScript文件、CSS文件和图片文件都必须在AssemblyInfo.cs中注册,每个文件都是"Embedded Resources"中的一个加载目录,必须动态加载。如何动态读取日历程序集中的文件?使用了Assembly.GetExecutingAssembly().GetManifestResourceStream()方法来读取文件的内容。

System.IO.Stream streamScriptLoader = Assembly.GetExecutingAssembly().GetManifestResourceStream("YuiExtAspNetCalendar.Loading.calendarLoader.js"); System.IO.StreamReader readerScriptLoader = new System.IO.StreamReader(streamScriptLoader); string scriptLoader = readerScriptLoader.ReadToEnd(); readerScriptLoader.Close();

日历有几个按钮(右导航、左导航、关闭按钮等)。所有这些按钮都画在一个文件中(奇怪的设计),这个文件是sprite.png。仅仅把sprite.png放在目录中是不够的,ASP.NET在执行期间无法找到它。要在程序集中找到它,ASP.NET需要获取AssemblyUrl,可以通过Page.ClientScript.GetWebResourceUrl()方法获取它。

string urlImageSprite = Page.ClientScript.GetWebResourceUrl(this.GetType(), "YuiExtAspNetCalendar.resources.sprite.png");

结果:因为calendar.css需要这个sprite.png,提取了所有包含background:url('sprite.png')的CSS行,在服务器端,用正确的URL(AssemblyUrl)替换:

string urlImageSprite = Page.ClientScript.GetWebResourceUrl(this.GetType(), "YuiExtAspNetCalendar.resources.sprite.png"); StringBuilder stylesExtracted = new StringBuilder(); stylesExtracted.Append("\r\n .yui-skin-sam .yui-calcontainer .title{background:url(\"[SPRITE_IMG_URL]\") repeat-x 0 0; border-bottom:1px solid #ccc;font:100% sans-serif;color:#000; font-weight:bold;height:auto;padding:.4em; margin:0 -10px 10px -10px;top:0;left:0;text-align:left;} \r\n"); // ... 更多样式 stylesExtracted = stylesExtracted.Replace("[SPRITE_IMG_URL]", urlImageSprite);

最后一点是,如果在同一页面上放置了几个日历,重复包含JS文件和CSS文件库是没有用的。所以使用了Page.Items集合,它是HttpRequest生命周期的持久性。

结果:如果在同一页面上放置了几个日历,做了处理,只加载一次脚本。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485