支持语言和不同日期格式的日期控件






4.14/5 (10投票s)
2005年5月13日
4分钟阅读

99576

713
使用 .NET 的 System.Globalization 类来检索外国语言的月份名称,以及 DateTimeFormatInfo 来确保您的日期格式正确。

引言
互联网上有很多日期控件,包括 CodeProject 上的许多。然而,很少有控件能够利用 .NET 的 System.Globalization 类来检索外语月份的名称。从南非荷兰语到越南语的月份名称,无需了解所说的语言即可实现。此外,我还增加了处理任何我选择的日期格式的可能性。此控件可以很好地处理美国或欧洲的日期格式。该控件还支持 Visual Studio 设计器。
背景
System.Globalization 命名空间包含定义与区域设置相关信息的类,包括语言、国家/地区、使用的日历以及日期、货币和数字的格式模式。
我使用了 CodeProject 上的 David Truxall 的控件 作为该控件的模板,并添加了以下功能:
属性
| 属性 | 默认值 | 描述 | 
| YearsForward | 0 | (可选)一个整数,用于设置控件从当前年份开始显示的未来年份数。 | 
| YearsBack | 0 | (可选)一个整数,用于设置控件从当前年份开始显示的过去年份数。 | 
| 文化 | (可选)一个 string,与 Culture Name 相同,用于显示月份名称。例如,en-gb es-ES。有关完整列表,请参阅 MSDN。如果未设置,则使用服务器的区域代码。- 这不会影响控件的值。 | |
| MonthFormat | MMMM | (可选)一个 string,用于设置控件中的月份文本。例如 MMM、MMMM、MM。 - 这不会影响控件的值。 | 
| DateFormat | 默认使用服务器上的格式 | 一个 string,用于设置输出日期值格式。例如,dd/MM/yyyy MM-dd-yyyy yyyyMMdd | 
| 值 | 一个用于设置 date的值。* 请注意,如果您使用 dd/MM/yyyy,请确保值设置为双位数,例如 02/04/2005 是有效的,而 2/4/2005 是无效的。 | |
| SelectCurrentDate | true | 一个布尔值,用于在未指定值时将值设置为当前日期。 | 
使用示例
<%@ Register TagPrefix="cc1" 
  Namespace="i386.UI" Assembly="i386.UI" %>
德语,显示短月份文本,未来 10 年,比当前年份早 2 年
<cc1:dropdowndatetime  id="DropDownDateTime1" runat="server" YearsForward="2" 
   YearsBack="10" Culture="de-DE"   SelectCurrentDate="False" MonthFormat="MMM">
</cc1:dropdowndatetime>
使用 date 进行美国日期格式化
<cc1:dropdowndatetime id="Dropdowndatetime4" runat="server" 
  dateFormat="MM/dd/yyyy" Value="11/02/2005">
</cc1:dropdowndatetime>
YearsForward 和 YearsBack 属性可以为负值,这会控制范围和顺序。
| YearsForward | YearsBack | 显示的年份 | 
| -11 | 20 | 1985 ... 1994 | 
| 11 | -20 | 1994 ... 1985 | 
| 11 | 20 | 1985 ... 2016 | 
控件的工作原理
首先,DateFormat 默认设置为服务器当前使用的格式,除非设置了 DateFormat 属性。
private string _DateFormat= 
      System.Threading.Thread.CurrentThread.
             CurrentCulture.DateTimeFormat.ShortDatePattern;        
public string Value 
{
    get 
    {
        string s = (string)ViewState["Value"];
        if(s == null) return String.Empty;
        else return s;
    }                
    set 
    {
        this.SetSelected(value);
        ViewState["Value"] = value;
    }
}
private void SetSelected(string StoredValue)
{
    DateTimeFormatInfo formatInfo = 
                         new DateTimeFormatInfo();
    formatInfo.FullDateTimePattern = DateFormat;
    DateTime dt = DateTime.ParseExact(StoredValue,
                                DateFormat,formatInfo);    
    SelectedDay  = dt.Day;
    SelectedMonth = dt.Month;
    SelectedYear = dt.Year;    
}
该控件实现 IPostBackDataHandler 来管理控件值在 PostBack 时发生更改时的重置。这与 David Truxall 的相同,不同之处在于构建 postCollection 的 Value 属性。
bool IPostBackDataHandler.LoadPostData(string postDataKey, 
     System.Collections.Specialized.NameValueCollection postCollection)
{
    ....
    ....                
    if(this.SelectedDay == -1 && this.SelectedMonth == -1 && 
                                         this.SelectedYear == -1)
        this.Value = "";
    else
    {
        // Build a DateTime string based on the DateFormat
        DateTimeFormatInfo formatInfo = new DateTimeFormatInfo();
        formatInfo.FullDateTimePattern = DateFormat;
        DateTime dt = new DateTime(this.SelectedYear, 
                           this.SelectedMonth, this.SelectedDay);
        this.Value = dt.ToString(DateFormat,formatInfo);
    }
}
构建子控件
当 SelectCurrentDate 设置为 true(默认)并且控件的 Value 为空时,我们将控件设置为当前日期。我们使用 DateTimeFormatInfo 和 DateFormat 将日期正确格式化为 string 以用于 Value 属性。如果 SelectCurrentDate 设置为 false,则控件设置为年份下拉框中列出的第一年的 1 月 1 日。我们使用 JavaScript 来限制闰年的天数,并保持 Value 输入框的更新。Value 由客户端和服务器值保持最新。
protected override void CreateChildControls()
{
    DateTime dt = DateTime.Today;
    // Use CurrentDate if Value is empty and 
    // SelectCurrentDate is true.
    if (SelectCurrentDate && this.Value=="")
    {
        // Use Current Date
        SelectedDay  = dt.Day;
        SelectedMonth = dt.Month;
        SelectedYear = dt.Year;
        DateTimeFormatInfo formatInfo = 
                               new DateTimeFormatInfo();
        formatInfo.FullDateTimePattern = DateFormat;
        this.Value = dt.ToString(DateFormat,formatInfo);
    }
    // Days
    DropDownList ddlboxDay = new DropDownList();
    ddlboxDay.ID ="Day";
    ddlboxDay.Attributes.Add("onchange",
      "ChangeOptionDays('" + this.ClientID +"',
                              '" + DateFormat + "');");
    for (int nDay = 1; nDay<32; nDay++)
                  ddlboxDay.Items.Add(nDay.ToString());
    
    // So we can restore the Culture later
    CultureInfo ExistingCulture = 
      System.Threading.Thread.CurrentThread.CurrentCulture; 
    // Culture - changes language of months    
    if (Culture!=null)    
      System.Threading.Thread.CurrentThread.CurrentCulture = 
                  new System.Globalization.CultureInfo(Culture);    
    // Months
    DropDownList ddlboxMonth = new DropDownList();
    ddlboxMonth.ID ="Month";
    ddlboxMonth.Attributes.Add("onchange",
      "ChangeOptionDays('" + this.ClientID +"',
                             '" + DateFormat + "');");
    for ( int nMonth=1; nMonth<=12; nMonth++)
    { 
        DateTime MonthDate = new DateTime(2000,nMonth,1);
        ddlboxMonth.Items.Add(new 
              ListItem(MonthDate.ToString(this.MonthFormat),
                                            nMonth.ToString()));
    }
     // Restore 
    System.Threading.Thread.CurrentThread.CurrentCulture = 
                                              ExistingCulture;
    // Years (Forward and Back properties)    
    DropDownList ddlboxYear = new DropDownList();
    ddlboxYear.ID ="Year";
    ddlboxYear.Attributes.Add("onchange",
               "ChangeOptionDays('" + this.ClientID +"',
                                      '" + DateFormat + "');");
    ddlboxYear.ID ="Year";
      if (YearsBack>=0)
      {
        for (int nYear = -YearsBack; nYear<=YearsForward; nYear++)
        {
            int ddlYear =dt.Year+nYear;
            ddlboxYear.Items.Add(ddlYear.ToString());
        }
      }
      else
      {
        for (int nYear = YearsForward; nYear<=-YearsBack; nYear++)
        {
            int ddlYear =dt.Year-nYear;
            ddlboxYear.Items.Add(ddlYear.ToString());
        }
      }
    // Select the DropDownList for the year
    if (SelectedYear>0)
    {
        // See if the year is in the list.
        if (ddlboxYear.Items.FindByValue(SelectedYear.ToString())!=null) 
        {
           ddlboxYear.Items.FindByValue(SelectedYear.ToString()).Selected = 
                                                                        true;
        }
    }
    if (SelectedMonth>0)    
      ddlboxMonth.Items.FindByValue(SelectedMonth.ToString()).Selected = true;
    if (SelectedDay>0)    
      ddlboxDay.Items.FindByValue(SelectedDay.ToString()).Selected = true;
    // Add Child Control
    Controls.Add(ddlboxDay);
    Controls.Add(ddlboxMonth);
    Controls.Add(ddlboxYear);
调试模式
用于检查控件的运行情况。DebugMode 输出 Value、ViewStates、CultureInfo 和 DateFormat。要启用 DebugMode,请使用 DebugMode="true"。
<cc1:dropdowndatetime DebugMode=true id="DropDownDateTime1" runat="server">
</cc1:dropdowndatetime>

浏览器兼容性
这只是我测试过的浏览器列表中的一小部分。我的电脑上没有安装 Netscape。
| 浏览器 | 操作系统下 | |
| Safari 1.0.3 | Mac System 9+ | 完全支持 | 
| Netscape 4.77 | Mac System 10.2.8 | 否 | 
| Opera 6.0.3 | Mac System 10.2.8 | 完全支持 | 
| Internet Explorer 5.1, 5.2 | Mac System 9+ | 完全支持 | 
| Firefox 1.x | Windows XP | 完全支持 | 
| Internet Explorer 4.0+ | Windows XP | 完全支持 | 
| Netscape ?? | Windows | ?? | 
Internet Explorer 3.0 需要使用服务器验证。如果某个浏览器无法正常工作,请告知我。
JavaScript 支持
从调试模式中,您可能会注意到隐藏的输入框。此输入框可以在 PostBack 到服务器之前在 JavaScript 中使用。
更新、版本和错误修复
- 2005 年 5 月 18 日,版本 0.2- 添加并修复了 YearsForward和YearsBack属性的负值。
- 支持旧版 JavaScript 浏览器。
 
- 添加并修复了 
未来增强功能
- 支持和测试使用服务器验证的旧版、无 JavaScript 或禁用 JavaScript 的浏览器。
- EnableSetDate属性尚未完成。此功能通过复选框启用/禁用日期框,并允许空白日期。
- 日期和闰年的服务器验证。源代码中包含一个 DateTimeValidator。
- 另一个功能是支持 Value的年份超出YearsForward和YearsBack属性范围。
- 时间支持。
欢迎评论或自行添加功能。如果您对该页面的评分较低,请在下方的论坛中留言。
