65.9K
CodeProject 正在变化。 阅读更多。
Home

DateTimePicker Web 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (39投票s)

2004年4月24日

10分钟阅读

viewsIcon

844297

downloadIcon

42849

利用 ASP.NET 日历控件制作的 DateTimePicker 控件。

Sample Image - together.gif

使用 DateTimePicker 进行日期选择的示例

引言

DateTimePicker Web 控件类似于 Windows DateTimePicker 控件。该控件公开了两个属性:SelectedDateFormatTypeSelectedDate 表示用户选择的日期,FormatType 表示日期字符串应显示的格式。提供的演示展示了该控件的用法。

背景

在继续讨论 DateTimePicker 之前,我想与您分享一些我在开发 ASP.NET 复合服务器控件时发现的重要事项

CreateChildControls 的必要性

此复合控件像大多数其他复合控件一样,派生自 System.Web.UI.WebControls.WebControl。在开发复合控件时,很少不需要重写 CreateChildControls 函数

cal.VisibleMonthChanged +=new MonthChangedEventHandler(month_changed);
cal.SelectionChanged +=new System.EventHandler(date_changed);

Controls.Add(dArea);
Controls.Add(cal);
base.CreateChildControls();

通常,复合控件使用的单个控件应添加到其 Controls 集合中。这会将这些组成控件置于 ASP.NET 框架为页面生成的控件树中的自定义控件之下。控件树不过是 ASP.NET 为页面创建的 DOM 结构的等效项。下图说明了包含 DateTimePicker 控件的页面的控件树。

控件树结构

您可以通过在 Page 指令中添加 trace="true" 来启用页面跟踪信息,从而生成控件树信息。上图表示页面上存在的各种元素的层次结构。将 TextBoxCalendar 控件添加到 Controls 集合使它们成为控件树层次结构中复合控件的子级。将组成控件添加到控件树可确保单个控件的事件得到适当处理。我们无需为它们的事件处理采取额外的措施。我们所需要做的就是为我们想要捕获的事件添加事件处理程序。CreateChildControls 也是为您可能希望从子控件接收的事件添加事件处理程序的最佳位置,如代码所示。但所有这些还有更多内容。CreateChildControls 何时执行的问题对于成功的事件处理至关重要。这就是 INamingContainer 发挥作用的地方。

INamingContainer 的必要性

几乎所有复合控件都实现此接口。实现很简单,如所示

public class DateTimePicker : System.Web.UI.WebControls.WebControl.INamingContainer

INamingContainer 是一个不包含任何方法的标记(空)接口。当控件实现此接口时,ASP.NET 页面框架会在该控件下创建一个新的命名范围。这确保了子控件在控件的层次树中具有唯一的 ID。这在所示的控件树图中很明显。控件实例的名称是 dtpickerTextBox 的名称是 dtpicker_ctl0Calendar 控件的名称是 dtpicker_ctl0。由于页面上不可能有其他名为 dtpicker 的控件,因此我们可以确定 TextBoxCalendar 的名称是唯一的。当页面上有控件的多个实例时,此功能很重要。需要注意的一点是,此分层命名方案在事件路由中很重要。如果我们为子控件提供自己的 ID,那么我们将无法处理它们的事件。

e.g.  cal.ID="mycalendar";

INamingContainer 还控制复合控件的 CreateChildControls 方法何时被调用。考虑没有 INamingContainer 接口实现的复合控件的 ASP.NET 页面执行周期

LoadPostData->OnLoad->RaisePostDataChanged->
Handle events->OnPrerender->CreateChildControls->
SaveViewState->Render->Dispose

现在,将上面与实现 INamingContainer 时的以下内容进行比较

OnInit-> LoadViewState->LoadPostData->RaisePostDataChanged->
CreateChildControls->OnLoad->Handle events->OnPrerender->
SaveViewState->Render->Dispose

ASP.NET 页面执行周期表示从 IIS 接收到“aspx”页面请求到将页面呈现到浏览器之间的各个步骤。此周期在每次请求页面时都会发生,即,首次加载页面以及所有后续回发时都会发生。从上面的两个序列中,我们看到如果我们需要处理组成控件的事件,INamingContainer 很重要。在第一个序列的事件处理阶段,子控件不是控件树的一部分,因此事件被忽略。(只有在事件处理期间存在于控件树中的控件才符合事件处理的条件。)因此,我们看到命名方案和控件树结构是事件处理的核心,而实现 INamingContainer 保证了一切顺利。

ViewState 的必要性

正如我之前讨论的,ASP.NET 页面执行周期在每次页面请求到达服务器时都会执行。由于每次都从头创建控件实例,因此上次呈现页面时属性的值应保存在某个地方。这正是通过 ViewState 属性实现的。ViewState 是一个字典对象,它存储名称-值对。当页面呈现时,页面上每个控件的 Viewstate 会以 <input type=hidden> 的形式集体呈现。您可以在浏览器的“查看源代码”中看到此输入控件。当页面回发时,输入字段中的数据用于重新创建 ViewState。这在上面所示的页面执行周期中很明显。在 OnInit 阶段,创建控件实例(构造),然后在 LoadViewState 阶段,为每个控件提供其以前的状态,该状态以隐藏控件的形式保存在页面中。因此,ViewState 帮助我们创建持久存储的效果。我们所需要做的就是实现属性,使其返回 ViewState 中的值并写入 ViewState,如下所示

public string SelectedDate
{
get
{
return (string)ViewState["selecteddate"];
}
set
{
ViewState["selecteddate"]=value;
}
}
public string FormatType
{
get
{
return (string)ViewState["format"];
}
set
{
string val=(string)value;
    
ViewState["format"]=val.ToUpper();
}

SelectedDate 必须以“mm/dd/yy”格式输入。FormatType 可以是“long”或“short”。如果未指定 SelectedDate 或格式错误,则将今天的日期作为默认值。

DateTimePicker 的工作原理

DateTimePicker 控件是一个复合控件,包含两个服务器端组件:TextBoxCalendar 控件。它还包括一个*客户端按钮*控件。文本框和按钮放在一个表格中,日历放在另一个表格中。需要另一个表格的原因是我们需要在按钮点击和焦点更改时更改其可见性。为了通过 JavaScript 实现这一点,我们需要通过 ID 引用它。我们无法为 Calendar 控件分配 ID,因为那样就无法进行事件处理。因此,我们将其放在表格中,并更改外部表格的可见性。所有控件一次性呈现,但 Calendar(实际上是外部表格)的可见性根据以下情况而变化

  1. 当包含 DateTimePicker 的页面首次加载时,Calendar 不可见。
  2. 当页面因日期更改事件而导致回发后加载时,Calendar 不可见。
  3. 当页面因月份更改事件而导致回发后加载时,Calendar 可见。

当日历在页面加载时可见时,我们需要将其 z-Index 属性设置为某个较高的值以实现 3D 效果(日历显示在其重叠的控件上方)。但为此,我们需要一个具有 OnLoad 事件的控件。由于我们之前没有使用过这样的控件,我只是简单地生成了一个虚拟 IFrame 并在 IFRAMEOnLoad 事件中执行内务工作。还会生成一个隐藏输入控件,它指定 Calendar 是否可见。此控件还有助于处理后退按钮。我们不会深入研究 JavaScript 函数的细节。您可以在 Render 方法中找到这些函数。我已经尽力注释了它们,希望足以让您了解这些控件的工作原理。您现在可能已经意识到为什么我在控件中重载了 Render 方法,因为我必须输出一些额外的控件及其支持的 JavaScript 函数。如果您的复合控件中只有子服务器控件需要呈现,则不需要 render 方法。在这种情况下需要记住的一点是,页面上可能存在一个控件的多个实例。因此,在 Render 方法中创建的 HTML 组件名称应该是唯一的

通过将 this.UniqueID 附加到函数和组件名称来生成名称,从而实现这一点。这里是这样做的

string uniqueID=this.UniqueID;
string spanID="main"+uniqueID;
string btnID=uniqueID+"btn";
string tdID=uniqueID+"td;
string hidID=uniqueID+"_a1;

this.UniqueID 表示您的控件在其使用的页面中被赋予的名称。由于没有两个控件可以具有相同的名称组件,因此名称必然是唯一的。

实现设计时支持

实现设计时支持涉及编写一个新类,该类重写 System.ComponentModel.Design.ControlDesigner 类。

需要重写的成员包括 GetDesignTimeHtmlAllowResize。下面显示了类实现的一部分

public class MyDesigner:ControlDesigner
{
  string width;
  public override bool AllowResize
  {
    get
    {
       return true;
    }
  }
  public  override string GetDesignTimeHtml()
  {
    string designTimeHtml;
    DateTimePicker controlDesigned=(DateTimePicker)this.Component;
    string dateDesignTime=controlDesigned.SelectedDate;
    string format=controlDesigned.FormatType;
    //some more
    ...
  }

此函数由设计器调用,用于获取在设计视图中表示您的控件的“HTML”。当在设计视图中将控件放置在页面上以及每次移动或调整控件大小时都会调用此函数。设计器会为该函数返回的 HTML 生成视觉形式。component 属性返回对正在设计的控件实例的引用。可能有一些属性的值我们需要在设计时呈现。这些值可以通过 component 属性指向的实例获取。

如果您的控件支持调整大小,AllowResize 方法只返回 true。为了通知设计器 MyDesigner 是此控件的设计器类,只需将以下属性添加到 DateTimePicker

[Designer("DTPicker.MyDesigner")]
public class DateTimePicker :

实现 IntelliSense 支持

IntelliSense 支持在不使用设计视图创建 aspx 页面时非常有用,即,您正在键入 .aspx 页面的全部内容。

下图说明了此控件的 intellisense 支持

IntelliSense 支持是通过编写一个模式文件来实现的,该文件定义了您的控件可以拥有的属性以及可以出现在其开始和结束标签内的各种元素。(这里没有元素)。我不会详细介绍如何编写模式文件。您可以在网上找到足够的示例。唯一需要记住的是,模式中公开的 targetNamespace 应该与控件的命名空间相同。

DLL 中的命名空间和模式文件的 targetNamespace 都是 DTPicker。此控件的模式文件在下载部分提供。

下一步是将模式文件复制到以下文件夹

C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml.

这是所有服务器控件模式所在的位置。

关于示例

下载中的 DateTimePickerControl 代码是控件的项目文件夹。下载后,编译项目以生成 DateTimePicker DLL。如果您从设计视图中使用控件,只需将 DLL 添加到工具箱。可以在属性窗口中设置 SelectedDateFormatType 属性。

如果您不使用设计视图,则在 ASP.NET 项目中添加对 DateTimePicker DLL 的引用。现在,添加以下 Register 指令。

<%@ Register TagPrefix="dtp" Namespace="DTPicker" Assembly="DTPicker" %>

当您在设计视图中添加控件的第一个实例时,此指令会自动添加到页面中。但是,设计器生成的此指令中的 TagPrefix 将是“cc1”、“cc2”等值。

控件的名称是 DateTimePicker,因此以下标签代表我们的控件。

<dtp:DateTimePicker>

在设计视图中,您可能会看到名称 <cc1:DateTimePicker> 或类似名称,具体取决于 TagPrefix

实现 IntelliSense 支持涉及向包含控件的页面的 <HTML> 标签添加以下属性。

<HTML xmlns:dtp=”DTPicker”>

上面的行假设控件的 TagPrefixdtp。如果 TagPrefixcc1,则 HTML 标签更改为

<HTML xmlns:cc1=”DTPicker”>

DTPicker 是模式文件的 targetNamespace

下载部分提供了一个示例 aspx 页面和代码隐藏页面,作为控件用法的示例。

© . All rights reserved.