[转]利用ASP.NET 2.0创建自定义Web控件(2)

简介:
 
如何生成的?

  Render() 方法基本上控制着 WebControl 的整个输出。默认情况下,Render() 方法实际上会依次调用 RenderBeginTag()、RenderContents() 以及 RenderEndTag()。尽管在 ASP.NET 1.x 中调用结构并未变化,但由于该呈现模型,修改这些调用的影响却发生了变化。

您可以覆盖 Render() 方法来发出您想要的任何内容。换句话说,您可能已经跳过了覆盖 TagKey 属性、AttributestoRender 属性和 RenderContents() 方法,并且仅使 Render() 编写“text”。但是,这种做法可能会严重影响自适应呈现。如果重写 Render() 来直接发出最终输出,您会绕过内置到 WebControl 类中的大多数自适应呈现特性。

自适应呈现模型和各种适配器的作用是:截获对各种标记方法的调用并转换特定设备的输出。在 MailLink 的特定示例中,几乎所有的标记语言都支持用于 链接的相同语法。但是,其他标记通常在不同的标记语言中会有截然不同的转换。如果我们为这样的标记使用了 Render(),我们的控件将只能在某些浏览器上使用,而适配器无法更改该行为。通过设计控件以使用自适应元素而不是使用 Render(),您可以让 ASP.NET 框架有机会根据浏览器提供在浏览器上的呈现服务。

使用自定义控件

自定义控件能够以很多方法包括在 Web 应用程序中。标准方法是将自定义控件编译到一个程序集中,然后在使用该控件的所有 Web 应用程序中添加一个对该程序集的引用。

使用 EmailLink

为了使用 EmailLink 控件,您需要: 

1.将 MyControls 项目编译到一个程序集中。 


图 3. 编译包含 EmailLink 控件的 MyControls 命名空间


2.在新的 Web 项目中添加一个对已编译程序集的引用。


图 4. 编译应用程序并添加一个引用


在正确添加引用之后,自定义控件应该出现在工具箱中的“MyControls Components”下。


图 5. 工具箱中的 EmailLink


MyControls 程序集中的所有组件都使用默认的齿轮图标,因为我们未曾在每个控件上设置特定的图标。设置图标如同在该控件类上调整图标属性那样简单。

  页面上的控件

在添加了对包含控件的程序集的引用之后,您可以将 MailLink 控件拖动到设计器表面并像使用任何其他 ASP.NET 服务器控件那样使用它。


图 6. MailLink 自定义控件


图 6 展示了 MailLink 控件的设计器视图。请注意,Properties 窗口公开了预期的 Email 和 Text 元素,它们可以用于配置控件。通过将自定义控件编译到可重复使用的程序集中,MailLink 控件可以被很多 Web 应用程序重复使用。

创建复合服务器控件

诸如 Login 和 GridView 这些可靠的控件是由很多基本控件组成的。在 ASP.NET 1.x 中,您必须通过艰苦的工作将嵌套标记和元素添加到自定义控件中来开发复合控件。在 ASP.NET 2.0 中,您可以通过扩展 System.Web.UI.WebControls.CompositeControl 类来构建复杂的复合控件。CompositeControl 类提供了将多个控件的输出合并到单个统一的控件中所必需的框架。

管理复合控件比管理基本自定义控件稍微困难一些,因为复合控件需要一些自定义布局的信息。复合控件将它们的呈现和事件处理任务委托给构成控件。子组件的所有关联的适配器类也会被自动应用。这样,如果您具有适当的适配器,复合控件将会在任何目标浏览器类型或设备上正确地呈现。

创建复合控件

创建复合控件的初始过程与创建自定义服务器控件的初始过程相似。但是,该过程还涉及了更多的步骤。在以下示例中,我们将创建一个由 Label 和 TextBox 组成的简单的复合 AgeCollector 控件,它旨在收集生日的信息。

复合控件类应该通过从 CompositeControl 继承开始。

public class AgeCollector : CompositeControl{}


定义属性

对于我们的简单控件,我们必须为标签 (Prompt) 和文本框 (DateOfBirth) 创建属性。

[Bindable(true), Category("Appearance"),
DefaultValue("Please enter your date of birth:"),
Description("Text to prompt user with.")
Localizable(true)]
public virtual String Prompt { 
get 

string s = (string)ViewState["Prompt"];
return (s == null) ? String.Empty : s;
}
set {
ViewState["Prompt"] = value;
}
}


再一次,我们使用特性为属性提供说明和默认值。我们选择了使提示可以进行本地化,以便该控件无论何时都可以用于要求进行国际化的应用程序中。实际的提示可以绑定到包含语言特定文本的资源文件。

还必须定义 DateOfBirth 属性。但是,我们不是使用 String,而是使用 DateTime 数据类型来正确地存储日期。

[Bindable(true), Category("Appearance"),
DefaultValue(""),
Description("Date of Birth Input area")]
public virtual DateTime DateOfBirth {
get
{
bject o = ViewState["DateOfBirth"];
return (o == null) ? DateTime.Now : (DateTime)o;
}
set { 
ViewState["DateOfBirth"] = value;
}
}


CreateChildControls 方法

我们的复合控件由一个标签和一个文本框组成。我们无法使用简单控件的技术来显示这两个标记,除非使用强制方式和 Render() 方法。因为我们希望利用自适应呈现并显示我们的两个控件,所以我们需要覆盖内置到 CompositeControl 类中的 CreateChildControls() 方法。这种方法使我们可以定义控件,并将我们的复合控件的属性传递到要显示的单个控件中。

protected override void CreateChildControls() {
//Create and load the label
Label lab1 = new Label();
lab1.Text = Prompt;
lab1.ForeColor = this.ForeColor;
this.Controls.Add(lab1);

//Add a line break between the label and text box
Literal lit = new Literal();
lit.Text = "";
this.Controls.Add(lit);

//Add the Textbox
TextBox tb = new TextBox();
tb.ID = "tb1";
tb.Text = DateOfBirth.ToString();
this.Controls.Add(tb);

//call the parent method
base.CreateChildControls();
}


请注意,我们必须初始化每个控件、分配所有属性,然后将控件添加到内置到 CompositeControl 类中的 Controls 集合。我们还使用了 Literal 对象将换行符置于标签和控件之间。Literal 对象是非常简单的控件,您可以使用它在功能元素之间插入原始 HTML。

请注意,我们还对基本方法进行了调用,以便确保我们的复合控件具有内置到 CompositeControl 基类中的任何其他功能。尤其是,基本方法会强制 ASP.NET 将 Controls 集合的所有元素添加到控件树中。如果我们忽略这个调用,或者将其置于我们方法的顶部,那么复合控件将不会正确地生成。

完整的 AgeCollector

当我们的 AgeCollector 控件生成时,ASP.NET 将在每个子控件上实际调用适当的方法,并将结果合并到复合控件的输出中。换句话说,如果我们已正确地设计了简单控件,那么该复合控件就只是一个容器。自适应呈现模型将会自动应用到每个子控件中。但是,实际的 CompositeControl 将不会被修改,因为它不包含需要更改的任何控件。

以下是另一个实例,其中使用的适当方法 (CreateChildControls()) 利用了自适应呈现模型,而不是简单地在 WebControl 上重载 Render() 方法。由于自适应呈现模型和 CompositeControl 的特性,ASP.NET 2.0 节省了我们的开发时间、减少了代码行数并减少了很多的测试烦恼。只要我们知道元素控件可通过特定适配器正确地生成,CompositeControl 将会通过该适配器正确地生成。

如果我们将控件拖动到 ASP.NET 页面上并查看属性,我们将会看到具有 Prompt 和 DateOfBirth 属性的单个控件。


图 7. AgeCollector 使用


请注意,如果我们将复合控件的 ForeColor 更改为红色,我们实际上更改了 Label 的 ForeColor。但是,我们尚未链接某些其他属性。例如,我们无法更改 DateOfBirth 字段的 ForeColor。换句话说,当您构建一个复合控件时,您始终需要考虑应该公开哪些子控件属性。

  添加控件行为

到目前为止,我们设计的两个控件都是简单、静态的控件。也就是说,这些控件不会完成利用普通的内置控件或简单用户控件 (.ascx) 无法完成的任何操作。构建自定义服务器控件的主要原因之一就是要提供使用现有控件集无法执行的新功能。

事件模型

在 Web 窗体页面中,与服务器控件关联的事件由客户端引发并由 Web 服务器处理。对于在客户机上由服务器控件引发的事件,ASP.NET 2.0 事件模型收集有关请求的信息,并使用 HTTP Post 将详细信息传递到服务器。服务器上的 Page Framework 对该公告作出解释以确定发生的事件,然后调用适当的处理程序方法。


图 8. 典型的服务器控件事件


ASP.NET 2.0 可处理几乎所有捕获、传输和解释事件的方法。详细信息对于开发人员来说是隐藏的,开发人员只需要关心服务器上的处理程序方法的实现。 

大多数服务器事件要求一个到服务器的往返以便进行处理,因此支持有限数量的单击类型事件。出于性能原因,不支持鼠标悬停和其他内部事件。

回发事件

ASP.NET 2.0 中的很多服务器控件都生成回发事件。回发事件将页面传递到服务器以便进行处理。这是一个非常昂贵的操作,因为它要求页面通过网络进行传递。

回发模型自从 ASP.NET 1.x 就没有进行过显著更改。为了创建一个可处理回发的控件,您的控件必须实现 IPostBackDataHandler 接口,它定义了两个方法: 

1) LoadPostData — 该方法处理您控件的回发数据。 

2) RaisePostDataChangedEvent — 该事件通知应用程序由于处理回发数据,该控件的状态已经更改。 

PostDataChangedEvent 调用引发的事件必须在该控件内部定义。然后,用户可以在开发过程中编写实际的事件方法。

非回发事件

某些服务器控件支持非回发事件。此类事件会更改控件的状态,但并不要求立即进行处理。这些事件由控件缓存,而不是立即传递到服务器以进行处理。例如,ListBox 控件可能包含很多元素。如果用户选择一个不同的元素,那么控件将在不通知服务器的情况下显示适当的更改并记住其新状态。在张贴包含 ListBox 的窗体之后,ListBox 控件将提交事件(选定的项)。

非回发事件的默认行为可以通过设置 AutoPostBack 属性进行更改。如果 AutoPostBack 设置为 true,那么通常由客户端缓存的事件发送信号通知服务器立即进行处理。启用 AutoPostBack 的控件要求客户机允许运行脚本。

ASP.NET 2.0 并未以任何明显的方式更改该模型。

回调和带外请求

标准的 Web 协议设计用于同步通讯。每个请求接收响应的速度与服务器生成数据的速度同样快。但是,很多任务都需要带外 请求,例如同一时间访问第三方资源。这些请求未处于浏览器和 Web 服务器之间的标准通讯带区内,因此被认为是带外请求。

ASP.NET 1.x 中的带外

进行带外数据请求的要求提示众多开发人员可以创造性地使用可用资源来获得所需的功能。例如,通过使用 ActiveX 组件和 JavaScript,开发人员能够进行外部 HTTP 调用而无需完全回发到服务器。下面的 JavaScript 示例说明了可以与 ASP.NET 1.x 一起使用的带外 HTTP 请求。

function RetrieveGoogleFrontPage() {
var XmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0");
XmlHttp.Open("GET", "http://www.fakedomain.com", false);
XmlHttp.Send();
return XmlHttp.responseText;
}


这种机制的一个缺点就是 XmlHttp.responseText 包含该请求的完整结果。开发人员将必须编写只返回商业数据的特殊页面,否则响应会由于不必要的标记而非常庞大。

ASP.NET 2.0 中的带外

ASP.NET 2.0 概括了 XmlHttp 对象的使用并提供了内置的回调功能。新系统的核心有两个关键项:System.Web.UI.ICallbackEventHandler 和 Page.GetCallbackEventReference 方法。

Page.GetCallbackEventReference 方法及其重载用于指定将参与回调事件的 JavaScript 方法。

public string GetCallbackEventReference(
Control control, 
string argument, 
string clientCallback, 
string context
);


上述代码显示了 GetCallBackEventReference 所需的最小参数集,这些参数将在下面进行详细说明。 

Control — control 参数确定实现 RaiseCallbackEvent 方法的 ICallbackEventHandler。 

Argument — argument 字符串包含客户端脚本。评估该脚本的结果将作为 eventArgument 参数传递到 RaiseCallbackEvent。 

ClientCallback — clientCallback 参数包含客户端事件处理程序的名称,该处理程序将接收成功服务器事件的结果。 

Context — context 参数包含一个客户端脚本。评估该脚本的结果将传递到客户端事件处理程序,该处理程序在 clientCallback 参数中指定为 context 参数。 

CallbackEventHandler 和 GetCallbackEventReference 方法相结合在客户端和服务器之间产生异步通讯。

  回调示例

以下 Web 页使用回调机制查询服务器以获得其当前时间。该页面弹出一个 JavaScript 警告,在无需完整页面回发的情况下显示当前时间。

<%@ Page Language="C#" CompileWith="Default3.aspx.cs" ClassName="Default3_aspx" %>
<%@ Register TagPrefix="cc1" Namespace="MyControls" Assembly="WebControlLibrary3" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<script language="javascript">
function GetServerTime() {
var message = '';
var context = '';
<%=CallBack%>
}
function ShowServerTime(timeMessage, context) {
alert('The time on the server is:\n' + timeMessage);
}
function OnError(message, context) {
alert('An unhandled exception has occurred:\n' + message);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<cc1:timesnap id="TimeSnap1" runat="server"> </cc1:timesnap>
<input type="button" value="GetTime" onclick="GetServerTime();" /> 
</div>
</form>
</body>
</html>

上述页面源代码包含三个关键 JavaScript 函数:GetServerTime()、ShowServerTime() 和 OnError()。这些 JavaScript 函数与页面的 GetCallbackEventReference 带外请求相关联。

public partial class Default3_aspx {
public string CallBack;
void Page_Load(object sender, EventArgs e) {
CallBack = this.GetCallbackEventReference(TimeSnap1,"message","ShowServerTime","context","OnError");
}
}

GetCallbackEventReference 方法需要为其第一个参数实现 ICallbackEventHandler 接口的对象。通过实现 RaiseCallbackEvent() 方法,TimeSnap 自定义服务器控件符合接口要求。

public class TimeSnap : WebControl, ICallbackEventHandler
{
...
public string RaiseCallbackEvent(string eventArgument) {
// Uncomment next line to test error handler
// throw new ApplicationException(// "Some unhandled exception");
return DateTime.Now.ToLocalTime().ToShortTimeString();
}
}
}

TimeSnap.RaiseCallbackEvent() 方法仅返回 string 格式的当前时间。 


图 9. 回调请求输出

图 9 说明了按下 GetTime 按钮的结果。向服务器发出带外请求,从而产生显示服务器上当前时间的“Alert”窗口。发出这个请求不需要回发,因此控件的最初生成时间不会改变。

使用设计器

在前面的示例中,我们已经使用了几个标准的特性来规定自定义控件的属性将与设计器 (Visual Studio) 进行交互的方式。我们为各种控件属性分配了特性以定义属性将在其中出现的类别、定义属性是否应该具有一个默认值、定义属性的说明应该是什么样子以及属性是否应该为 bindable。在 ASP.NET 1.x 中,附加的设计器类 使您可以创建用于编辑属性的新对话框、自动将属性值从 String 转换为其他数据类型(反之亦然),并显示只在运行时生成的控件的占位符数据。

设计器类有助于将控件开发分成两个阶段。第一,您必须开发自定义控件。第二,您必须决定开发人员将如何与设计环境内的控件进行交互。设计器类通过在每个自定义控件的顶部充当装饰师来完成第二个任务。换句话说,如果您要开发很多自定义控件,您可以创建一个标准的可重复使用的设计器集,并通过特性简单地将设计器应用到每个自定义控件中。

ASP.NET 2.0 为设计器模型提供了几项增强功能: 

1) 新的复合控件设计器 — CompositeControlDesiger 类完全识别复合控件,并且提供支持父子控件关系的功能。 

2) 新的数据绑定控件设计器 — DataBoundControlDesigner 为 Databound 控件提供了很多新功能。您可以使用该设计器来提供模拟数据,或者在设计期间自动连接到活 datasource。 

3) 增强的备用设计时区域支持 — 新的 DesignerRegion 类及其子类提供了一种非常灵活的机制以便显示控件。您可以使用 DesignerRegion 来设置控件的选项卡式视图。您还可以使用 EditableDesignerRegion 控件为控件创建新的模板。 

4) 增强的模板支持 — 现在,设计器类提供了更简洁的机制以便将新的模板添加到控件中。模板化控件是一种将控件逻辑和控件显示分开的控件。显示通过模板进行定义,而逻辑在实际控件中进行编码。 

5) 增强的任务支持 — 现在,设计器可以合并设计时的任务。最常见的任务将可在视图之间切换。但是,其他任务可以包括控件的自动配置或资源文件的自动创建。任务可以在设计时控件上显示为菜单(与允许您配置 GridView 控件的菜单相似)。 

6) 增强的事件支持 — 设计器中的事件模型已经进行了改进。现在,您可以创建事件来响应在不同区域中的单击或对各种任务的单击。使用设计器时,只要用户在特定区域上单击就可以使控件切换视图、自动生成代码或更改配置。 

ASP.NET 2.0 具有一个经过显著改进的设计器模型,它可以使专业控件开发人员的工作更加简单。如果您只是为自己使用而构建一个单个的控件,该设计器就大材小用了。但是,如果您要为分发而构建一个控件,您可以使用新的设计器来全面地自定义 Visual Studio 2005 中控件的行为。

小结

尽管 ASP.NET 2.0 包含了一个内容丰富的扩展控件集,但开发人员通常有很多理由来创建自定义控件。由于 ASP.NET 2.0 中的增强功能,创建自定义控件的过程要比在 ASP.NET 1.x 中更快、更容易。新的 CompositeControl 基类完全利用自适应呈现模型,并为创建复杂的控件提供了一个简单、易于使用的容器。如果您的控件需要回发或回调功能,ASP.NET 2.0 简化了处理客户端脚本文件和开发带外请求的过程。最后,在您开发控件后,您可以使用各种设计器类来完全配置控件在可视化设计器(例如 Visual Studio 2005)内的行为。

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!



















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/archive/2009/04/08/1432000.html ,如需转载请自行联系原作者


相关文章
|
21天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
81 3
|
3月前
|
编解码 前端开发 JavaScript
.NET_web前端框架_layui_栅格布局
【8月更文挑战第27天】
45 4
|
3月前
|
安全 前端开发 API
【Azure 应用服务】Azure Web App 服务默认支持一些 Weak TLS Ciphers Suite,是否有办法自定义修改呢?
【Azure 应用服务】Azure Web App 服务默认支持一些 Weak TLS Ciphers Suite,是否有办法自定义修改呢?
|
7天前
|
前端开发 开发者
WEB自定义页面请求响应
Web组件支持在应用拦截到页面请求后自定义响应请求能力。开发者通过onInterceptRequest()接口来实现自定义资源请求响应 。自定义请求能力可以用于开发者自定义Web页面响应、自定义文件资源响应等场景。
13 0
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
86 3
|
18天前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
2月前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
54 5
|
2月前
|
开发框架 JavaScript 前端开发
|
3月前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
48 0