2 个月日历 Yahoo WebControl
带有很多选项的 2 个月日历 Yahoo WebControl。

引言
我最近为一家著名的法国网球公司工作,他们有一个特定的网站应用程序需求。比赛在五月底开始,六月中旬结束。我的客户想同时看到五月和六月的日历来选择日期。对于某些用户类别,他希望允许他们选择最多日期(例如:在整个比赛期间选择 3 天,适用于 A 类用户)。并且禁止他们预订某些特殊日期(因此,可以在日历上设置禁止日期)。还有一个重要的点是,可以在日期范围内选择这些日期,有开始日期和结束日期(日历视图必须以第一个月的日期初始化)。当时很难找到一个具备所有这些选项并且外观良好的 2 个月日历小部件。我发现了一个几乎具备所有选项的:优秀的 Yahoo 日历 (http://developer.yahoo.com/yui/calendar/)。我在这里下载了带有日历组件的 JavaScript 开源框架 http://developer.yahoo.com/yui/2/。我看到基本功能都存在,但缺少两个功能:设置禁止日期和设置最大日期数。于是我深入分析了那个庞大的“calendar.js”文件,因为代码设计非常出色,非常干净,并且注释得很好,所以我成功地在这个 JavaScript 文件中实现了这两个功能。;)
在这篇文章中,我不会解释我对calendar.js的修改,而是解释实现 Web 控件的有趣要点。
背景
在本节中,我将解释如何使用该控件及其属性。
代码
实例化一个日历很容易
//server side code :
YuiExtCalendar.MinDateSelectable = new DateTime(2011, 6, 1);
YuiExtCalendar.MaxDateSelectable = new DateTime(2011, 10, 1);
YuiExtCalendar.InitialSelectedDates = new List()
{ new DateTime(2011, 6, 24), new DateTime(2011, 6, 25) } ;
YuiExtCalendar.ForbiddenDates = new List()
{ new DateTime(2011, 7, 10), new DateTime(2011, 7, 11) };
YuiExtCalendar.NumberOfSelectableDates = 5;
YuiExtCalendar.HeaderTitle = "Title of Calendar :";
YuiExtCalendar.DataBind();
<!--client side code-->
<cc1:YuiExtCalendar ID="YuiExtCalendar"
Mode="Flat"
runat="server" />
Mode
:Mode
属性是一个枚举,有 3 种可能的值。Flat 值显示日历,就像任何经典的控件一样。CollapsibleCalendarWithDateSummury
值会在您单击具有 ImageCalendarUrl
属性的图片时显示日历。因此,如果您使用 CollapsibleCalendarWithDateSummury
或 CollapsibleCalendarWithNoDateSummury
,则必须提供 ImageCalendarUrl
属性。CollapsibleCalendarWithDateSummury
会生成一个选定日期的迷你摘要,在关闭日历时很有用。CollapsibleCalendarWithNoDateSummury
值与 CollapsibleCalendarWithDateSummury
完全相同,只是没有摘要。
MinDateSelectable
和 MaxDateSelectable
是 dateTime
属性,必须一起设置。它们允许您在日历上限制日期选择范围。例如,如果您希望您的日历允许在 19/09/2012
和 09/12/2012
之间选择日期。(您必须在服务器端设置 MinDateSelectable = 19/09/2012
和 MaxDateSelectable = 09/12/2012
。)
InitialSelectedDates
属性是一个 List
,它允许您在页面加载时选择一些 datetime
。在服务器端设置此变量。
ForbiddenDates
属性是一个 List
,它允许您设置一个禁止日期的列表:这些日期将不可选。您可以在 calendar.css 中自定义这些禁止日期的 UI 样式:/*Forbidden Style*/ .yui-skin-sam .yui-calendar td.calcell.forbidden{ ##customize_here## }
。在服务器端设置此变量。
NumberOfSelectableDates
是一个整数属性,允许您控制已选日期的数量,例如:如果您希望一个用户最多在日历上选择 4 个日期,请将此变量设置为 4
。在服务器端或客户端设置此变量。
HeaderTitle
是一个 string
属性,允许您设置日历标题栏的标题。在服务器端或客户端设置此变量。
ImageCalendarUrl
是一个 string
属性。在 CollapsibleCalendarWithNoDateSummury
或 CollapsibleCalendarWithNoDateSummury
模式下,它是 Image
的 URL。我建议在客户端设置此变量。
SelectedValues
属性是一个 List
,它允许您在按钮发生回发时获取用户选择的日期。
如果您单击日历上显示的两个月中的一个,您可以选择您想看到的月份。
Using the Code
我选择继承 CompositeControl
类来实现这个日历控件。为什么?因为当您想要生成分层 HTML 标签时,子控件的 ID 会自动命名为:“ControlId_childrenControlId_subchildrenControlId_subsubchildrenControlId
”等等……这是一个很有用的属性,我主要在 JavaScript 中使用它(替换 _ID_
)。当您在 Defaut.aspx 页面上测试程序时,请查看客户端生成的 HTML 代码源代码。CreateChildControls
是一个重写的方法,允许我生成 HTML。OnPreRender
是一个重写的方法,允许我将 JavaScript 文件引用和 CSS 文件引用(如果需要)追加到头部,并生成 JavaScript 来构建和显示日历。每个 JavaScript 文件、CSS 文件和图片文件必须在 AssemblyInfo.cs 中注册,并且也都在 Loading 目录中作为“嵌入式资源”,需要动态加载。我如何动态读取位于日历程序集内的文件?我使用了 Assembly.GetExecutingAssembly().GetManifestResourceStream()
方法来读取文件的内容。
//Build CalendarLoader.js Constructor
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();
Calendar
有几个按钮(右导航、左导航、关闭按钮等)……所有这些按钮都绘制在一个文件中(设计很奇怪),这个文件是 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
)替换它们。
//Add style for registering sprite.png
//We extract all css lines from calendar.css whose contain url(sprite.png)
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")
.Append(" .yui-skin-sam .yui-calcontainer .calclose{background:url
(\"[SPRITE_IMG_URL]\") no-repeat 0 -300px;width:25px;
height:15px;top:.4em;right:.4em;cursor:pointer;} \r\n")
.Append(" .yui-skin-sam .yui-calendar .calnavleft{background:url
(\"[SPRITE_IMG_URL]\") no-repeat 0 -450px;width:25px;height:15px;
top:0;bottom:0;left:-10px;margin-left:.4em;cursor:pointer;} \r\n")
.Append(" .yui-skin-sam .yui-calendar .calnavright{background:url
(\"[SPRITE_IMG_URL]\") no-repeat 0 -500px;width:25px;height:15px;
top:0;bottom:0;right:-10px;margin-right:.4em;cursor:pointer;} \r\n")
.Append(" .yui-skin-sam .yui-calendar a.calnav:hover{background:url
(\"[SPRITE_IMG_URL]\") repeat-x 0 0;border-color:#A0A0A0;cursor:pointer;} \r\n")
.Append(" .yui-skin-sam .yui-calcontainer .yui-cal-nav .yui-cal-nav-btn
{border:1px solid #808080;background:url(\"[SPRITE_IMG_URL]\")
repeat-x 0 0;background-color:#ccc;margin:auto .15em;} \r\n")
.Append(" .yui-skin-sam .yui-calcontainer .yui-cal-nav
.yui-cal-nav-btn.yui-default{border:1px solid #304369;
background-color:#426fd9;background:url
(\"[SPRITE_IMG_URL]\") repeat-x 0 -1400px;} \r\n");
stylesExtracted = stylesExtracted.Replace("[SPRITE_IMG_URL]", urlImageSprite);
最后一点是,如果我们在同一个页面上放置多个日历,多次包含 JS 文件和 CSS 文件库是没有意义的。所以我使用了 Page.Items
集合,它在 HttpRequest
的生命周期中是持久的。
结果:如果您在同一页面上放置多个日历,我已经处理好了,脚本只会加载一次。
关注点
我改进了我对 ASP.NET 组件中 JavaScript 封装的知识。我希望在一家以设计 ASP.NET 工具箱组件为业务的公司工作。
历史
我在许多情况下多次测试了这个组件。它的行为看起来是正确的。如果您有任何问题,请随时提出。