美国和英国日期的ASP.NET 复合控件

分享于 

15分钟阅读

Web开发

  繁體
<--文章图像 --> !

Sample Image - datecontrol.jpg

<--将你的HTML其余部分添加到 --> !

介绍

在构建基于web的企业广泛系统时,我们经常遇到跨越英语语言的本地化类型问题。 我们在英语国家中遇到的最重要的数据问题是日期的输入。 应用程序在文本框中最初接受日期,在数据库中保存日期时产生了很多混乱。 ( 我们) 在美国,日期为 2003年04月5日/5/2003,这个日期是 2003年05月4日,一些英国用户在他们的电脑上设置了本地化设置,有些英国用户在他们的电脑上设置了本地化设置。

没有可以靠的方法确定用户真正使用 (mm/dd/yyy 或者 dd/mm/yyyy)的数据输入日期格式。 应用程序以dd-Mon-yyyy格式显示日期,该格式对用户是明确的。 用户发现类型笨重,并且附近的标签显示了适当的格式和附加培训对用户没有帮助。 这样就产生了一个基于列表框的日期控件的概念。 我选择在用户控件上创建一个复合控件,以便同时在多个应用程序中部署控件。

背景

复合控件必须实现 INamingContainer 接口,以便页上的多个控件实例具有唯一的名称。 另外,复合控件必须重写 CreateChildControls 方法以实例化子控件。 我选择实现 IPostBackDataHandler 来管理回发的控件状态,而不是挂钩到子控件的内置事件。 这主要是由于我的控制产生了一个值,但使用了三个控件。 如果用户更改了三个,则每个控件引发一个更改事件,并导致不必要的代码执行来维护控件的单个值。 IPostBackDataHandler 接口允许控件检查所有三个状态的状态,然后引发单个更改事件。

DateControl类

公用 属性

属性SelectCurrentDate使控件的值最初设置为当前日期的布尔值。
属性Value表示格式"mm/dd/yyyy". 中三个控件的值的字符串空值为空字符串。
属性YearsBack用于设置控件在年列表框中呈现的过去数的整数。

公用 事件

事件Change当任何列表框更改时发生。

要在页面上实例化控件,首先需要在页面上对它的进行 register:

<%@RegisterTagPrefix="custom"namespace="CustomControls"assembly="YourDLLName"%>

然后在HTML中:

<custom:datecontrolid="TheDate"runat="server"selectcurrentdate="true"></custom:datecontrol>

日期控制的工作方式

三个子控件的控件 consisits,但只公开一个值,该值是所有三个子控件的组合。 控件在重写 CreateChildControls 方法中非常简单。 每个列表框都采用适当的array 并将它的绑定。 日期和月份是 static,计算的年份为 array。 设置控件属性时,方法分割值并分别存储每个控件的值,以处理 postback 上的列表框,并将单个 Value 属性保存在ViewState中。 控件不关心数据的有效性,只关心格式。

publicstring Value 
{
 get 
 {
 string s = (string)ViewState["Value"];
 if(s == null)
 returnString.Empty;
 elsereturn s;
 } 
 set 
 {
 this.SetSelected(value);
 ViewState["Value"] = value;
 }
}privatevoid SetSelected(string When)
{
 string[] ResultList;
 Regex DateSplitter = new Regex("^d{1,2}/d{1,2}/d{4}$");
 if(DateSplitter.IsMatch(When)) //The date has a good format {
 char divider = '/';
 ResultList = When.Split(divider);
 SelectedMonth = Int32.Parse(ResultList[0]);
 SelectedDay = Int32.Parse(ResultList[1]);
 SelectedYear = Int32.Parse(ResultList[2]);
 }
 else//When must be empty or not recognizable, //so set the listboxes to the empty state {
 SelectedDay = -1;
 SelectedMonth = -1;
 SelectedYear = -1;
 }
}

当更改发生时,控件实现 IPostBackDataHandler 以管理控件的值更改,并在发生更改时重置值。 对于每个控件,它检查已经发布的selectedindex/值并将它的与从ViewState中恢复的selectedindex/值进行比较。 如果发生更改,则会更新控件的Value。 没有显式设置的列表框值为 -1,并且其值被移除的所有列表框也设置为。 这可能有点奇怪,但它将极大地帮助验证控件的有效性。 当 LoadPostData 返回 true 时,ASP.NET 调用 RaisePostDataChangedEvent,从而触发控件事件的Change

bool IPostBackDataHandler.LoadPostData(string postDataKey, 
 NameValueCollection postCollection) 
{
 int Day = 0;
 int Month = 0;
 int Year = 0;
 bool Changed = false;
 string MonthName;
 if(postCollection[this.UniqueID.ToString() + ":Day"]!= "")
 {
 Day = Int32.Parse(postCollection[this.UniqueID.ToString() + 
 ":Day"]);
 if (Day!= this.SelectedDay)
 {
 this.SelectedDay = Day;
 Changed = true;
 }
 }
 else {
 if(this.SelectedDay!= -1)
 {
 this.SelectedDay = -1;
 Changed = true;
 }
 }
 MonthName = postCollection[this.UniqueID.ToString() + ":Month"];
 if (MonthName!= "")
 {
 Month = Array.IndexOf(this.MonthArrayLong, MonthName);
 if(Month!= this.SelectedMonth)
 {
 this.SelectedMonth = Month;
 Changed = true;
 }
 }
 else {
 if(this.SelectedMonth!= -1)
 {
 this.SelectedMonth = -1;
 Changed = true;
 }
 }
 if(postCollection[this.UniqueID.ToString() + ":Year"]!= "")
 {
 Year = Int32.Parse(postCollection[this.UniqueID.ToString() 
 + ":Year"]);
 if (Year!= this.SelectedYear)
 {
 this.SelectedYear = Year;
 Changed = true;
 }
 }
 else {
 if(this.SelectedYear!= -1)
 {
 this.SelectedYear = -1;
 Changed = true;
 }
 }
 if(this.SelectedDay == -1 && this.SelectedMonth == -1 && 
 this.SelectedYear == -1)
 this.Value = "";
 else {
 string NewDate = this.SelectedDay.ToString() + "/";
 NewDate += this.SelectedMonth.ToString() + "/";
 NewDate += this.SelectedYear.ToString();
 this.Value = NewDate;
 }
 return Changed;
}

下一步:验证

正如你现在所注意到的,控件将返回像"4/-1/1975" 或者"-1/-1/2003". 这样的值,它不会返回 -1/-1/-1,,而是空字符串。 尽管我们使用的是普通的语音和书写语言,但是数据库日期字段非常挑剔,不能接受部分日期,甚至不接受 4/2003。 设置为 -1的单个值允许有效验证控件,并包含数据库日期字段无法接受的值,因此错误数据不能无意中输入数据库。 我没有将验证滚动到控件中,而是创建了一个验证器,以便与日期控制一起去。 通过自定义验证程序可以进行验证,但是我更喜欢自己部署客户端脚本。 DateControlValidatorBaseValidator 继承,只实现一个自定义属性。

DateControlValidator

公用 属性

属性Required当 true 发生错误时,boolean 会导致验证器检查所有三个字段是否为空。 当 false 验证器允许所有三个字段同时为空时。

定制的编写器比控件更具有挑战性。 它与to验证器无缝地工作,它们与上级和下级浏览器和 EnableClientScript 属性的行为有关。 JavaScript包含一个主要函数 DateControlIsValid,它检查 Required的值并相应地执行操作。 验证器发出自己的错误消息( 客户端和服务器),它忽略用户输入验证器的ErrorMessage 属性的内容。 这是由于可能出现多个不同的错误条件:

  • 未完成状态,例如 4/-1/2003,其中一个或者多个子控件未被选中。
  • 一个无效日期,如 2003年09月31日 ( 九月只有 30天)。
  • 需要 DateControl,但未选择任何子控件。

错误消息不能为用户提供足够的信息来区分这些问题,并容易地解决错误状况。

自定义编写器必须重写 EvaluateIsValid。 此外,为了支持客户端脚本的使用,验证器必须重写 AddAttributesToRenderOnPreRender。 验证器在发出给客户端的HTML上作为一个 <div> 呈现。 AddAttributesToRender 将必要的属性放入 <div>,以便when书写客户端验证例程能够在客户端验证时正确地查找 <div>。 可以在 WebUIValidation.js 文件夹heirarchy中找到Micrsoft编写的Javascript written。

protectedoverridevoid AddAttributesToRender(HtmlTextWriter writer)
{
 if(this.DetermineRenderUplevel() && this.EnableClientScript)
 {
 base.AddAttributesToRender(writer);
 writer.AddAttribute("controltovalidate", this.ControlToValidate);
 writer.AddAttribute("evaluationfunction", "DateControlIsValid");
 writer.AddAttribute("display", this.Display.ToString());
 writer.AddAttribute("style", "display:none");
 writer.AddAttribute("errormessage", this.ErrorMessage);
 if(ControlRequired)
 writer.AddAttribute("required", "true");
 else writer.AddAttribute("required", "false");
 }
}

OnPreRender 例程将定制的验证器的JavaScript文件钩子到HTML中,并将它的插入到Microsoft客户端验证程序中。 对于上级浏览器,当页面加载时,微软客户端验证函数会动态地与控件事件的HTML客户端钩子。 由于 DateControl 实际上是三个单独的HTML控件,因这里,例程无法识别到验证器验证函数的单个HTML控件。 实际上,如果验证器只绑定到三个控件中的一个,则DateControl将不能正确验证。 OnPreRender 例程发出一个自定义启动脚本,通过HTML页面上的DateValidator控件( <div> ),并允许它们消除of编写的行为。 微软提供一个完成同样事件的例程,但是这会使验证发生在 onpageload<body>的事件中。 要求立即开始空白的DateControls即使用户没有执行任何操作,也会立即报告错误情况。

protectedoverridevoid OnPreRender(EventArgs e)
{
 if(this.DetermineRenderUplevel() && this.EnableClientScript)
 {
 string EOL = Environment.NewLine;
 StringBuilder script = new StringBuilder();
 //Make sure the MS standard javascript file is includedthis.RegisterValidatorCommonScript();
 //Include our own file for the control script.Append("<script language="'javascript'"" + 
 "src='../include/date_control.js'></script>" + EOL);
 Page.RegisterClientScriptBlock("DateValidate", script.ToString());
 //Register this validator in the array of validators //on the page, and the array of //date controls on the pagestring element = "document.all["" + ClientID + ""]";
 Page.RegisterArrayDeclaration("Date_Controls", element);
 Page.RegisterArrayDeclaration("Page_Validators", element);
 //Because the date control is a composite control, //there is no single HTML control to //bind the evaluation function to. Therefore, //the MS javascript disables this validator.//The client script below re-enables the validator when //the page is submitted.  script.Remove(0, script.Length - 1);
 script.Append("<script language="'javascript'">");
 //This is the"Microsoft Preferred" method for enabling //a control, but it causes the//control to validate, causing an immediate //error condition when the field is required.// script.Append(sVbCrLf)// script.Append("ValidatorEnable(" & ClientID &", true);")// Instead I am setting the enabled property directly // of all registered date controls.// the EOLs are for human readablity only script.Append(EOL);
 script.Append("var x;");
 script.Append(EOL);
 script.Append("for (x = 0; x <Date_Controls.length; x++)");
 script.Append(EOL);
 script.Append("Date_Controls[x].enabled = true;");
 script.Append(EOL);
 script.Append("</script>");
 script.Append(EOL);
 Page.RegisterStartupScript("SetEnabled", script.ToString());
 }
}

未来的改进

DateControl 当然进一步进行,但这些更改并不是立即需要的,因这里它们尚未实现:

  • 支持数据绑定
  • 从WebControl继承
  • 设计器支持

.zip 文件中包含 DateControlDateControl 验证程序类的VB.Net 版本。


COM  控制  asp  asp-net  日期  dates  
相关文章