DateTimePicker Web 控件






4.60/5 (39投票s)
2004年4月24日
10分钟阅读

844297

42849
利用 ASP.NET 日历控件制作的 DateTimePicker 控件。
使用 DateTimePicker 进行日期选择的示例
引言
DateTimePicker
Web 控件类似于 Windows DateTimePicker
控件。该控件公开了两个属性:SelectedDate
和 FormatType
。SelectedDate
表示用户选择的日期,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"
来启用页面跟踪信息,从而生成控件树信息。上图表示页面上存在的各种元素的层次结构。将 TextBox
和 Calendar
控件添加到 Controls
集合使它们成为控件树层次结构中复合控件的子级。将组成控件添加到控件树可确保单个控件的事件得到适当处理。我们无需为它们的事件处理采取额外的措施。我们所需要做的就是为我们想要捕获的事件添加事件处理程序。CreateChildControls
也是为您可能希望从子控件接收的事件添加事件处理程序的最佳位置,如代码所示。但所有这些还有更多内容。CreateChildControls
何时执行的问题对于成功的事件处理至关重要。这就是 INamingContainer
发挥作用的地方。
INamingContainer
的必要性
几乎所有复合控件都实现此接口。实现很简单,如所示
public class DateTimePicker : System.Web.UI.WebControls.WebControl.INamingContainer
INamingContainer
是一个不包含任何方法的标记(空)接口。当控件实现此接口时,ASP.NET 页面框架会在该控件下创建一个新的命名范围。这确保了子控件在控件的层次树中具有唯一的 ID。这在所示的控件树图中很明显。控件实例的名称是 dtpicker
,TextBox
的名称是 dtpicker_ctl0
,Calendar
控件的名称是 dtpicker_ctl0
。由于页面上不可能有其他名为 dtpicker
的控件,因此我们可以确定 TextBox
和 Calendar
的名称是唯一的。当页面上有控件的多个实例时,此功能很重要。需要注意的一点是,此分层命名方案在事件路由中很重要。如果我们为子控件提供自己的 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
控件是一个复合控件,包含两个服务器端组件:TextBox
和 Calendar
控件。它还包括一个*客户端按钮*控件。文本框和按钮放在一个表格中,日历放在另一个表格中。需要另一个表格的原因是我们需要在按钮点击和焦点更改时更改其可见性。为了通过 JavaScript 实现这一点,我们需要通过 ID 引用它。我们无法为 Calendar
控件分配 ID,因为那样就无法进行事件处理。因此,我们将其放在表格中,并更改外部表格的可见性。所有控件一次性呈现,但 Calendar
(实际上是外部表格)的可见性根据以下情况而变化
- 当包含
DateTimePicker
的页面首次加载时,Calendar
不可见。 - 当页面因日期更改事件而导致回发后加载时,
Calendar
不可见。 - 当页面因月份更改事件而导致回发后加载时,
Calendar
可见。
当日历在页面加载时可见时,我们需要将其 z-Index
属性设置为某个较高的值以实现 3D 效果(日历显示在其重叠的控件上方)。但为此,我们需要一个具有 OnLoad
事件的控件。由于我们之前没有使用过这样的控件,我只是简单地生成了一个虚拟 IFrame
并在 IFRAME
的 OnLoad
事件中执行内务工作。还会生成一个隐藏输入控件,它指定 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
类。
需要重写的成员包括 GetDesignTimeHtml
和 AllowResize
。下面显示了类实现的一部分
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 添加到工具箱。可以在属性窗口中设置 SelectedDate
和 FormatType
属性。
如果您不使用设计视图,则在 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”>
上面的行假设控件的 TagPrefix
是 dtp
。如果 TagPrefix
是 cc1
,则 HTML
标签更改为
<HTML xmlns:cc1=”DTPicker”>
DTPicker
是模式文件的 targetNamespace
。
下载部分提供了一个示例 aspx 页面和代码隐藏页面,作为控件用法的示例。