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

ASP.NET 服务器控件中的泛型支持

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (15投票s)

2009 年 1 月 29 日

CPOL

4分钟阅读

viewsIcon

63509

downloadIcon

460

实现一个框架以支持 ASP.NET 服务器控件中的泛型,包括 ITemplate 容器的强类型。

ASP.NET 中的泛型?为什么?

任何构建过 WebForms 应用程序的人都无疑遇到过老旧的 Repeater 控件。作为 Web 编程的基石,它允许显示项目集合,并通过 EvalBind 方法暴露项目。

在底层,EvalBind 通过反射工作,以提供后期绑定支持。这也是您在引用属性时看到的,例如

<%# Container.DataItem.PropertyNameHere %>

原因在于 ASP.NET 允许透明的后期绑定,所以即使它认为 Container.DataItem 的类型是 'Object',评估也只会在运行时发生。这会导致各种问题,例如

  • 页面在属性更改时损坏,并且在编译时没有警告。
  • 性能降低,因为每个数据字段显示点都变成了一个反射调用。

但是,如果我们能告诉 ASP.NET 数据项的实际类型是什么,那么在站点启动/编译期间,我们将能够确保使用正确的类型,并且不会发生任何后期绑定。对所有页面、母版页和用户控件绑定标签进行编译时检查听起来不错?

如果这对您来说还不够,那么还有完整的智能感知支持呢?

StrongTypingIntellisense.png

ASP.NET 中的泛型?如何实现?

在启动和给定文件夹中页面首次点击时,ASP.NET 会对您的 ASPX 文件进行批处理编译。也就是说,将它们和其中的内容转换为实际要运行的 .NET 代码。之前 ASP.NET 泛型的问题在于 ASP.NET 没有语法支持来指定泛型类型,例如

<asp:Repeater ID="Repeater1" runat="Server" />

一切都很好,但是您不能说

<asp:GenericRepeater<String> ID="StringRepeater" />

因此,我们将不得不寻找一种变通方法。我最终采取的路线与另一个关于由Andrey Shchekin提供的类型化 Repeater 实现的教程非常相似。您可以在此处找到他的教程。其工作原理如下:

  • 我们使用分配给非泛型类的ControlBuilder子类来拦截页面的编译时构造。
  • ControlBuilderInit 方法通过读取非泛型控件的属性中的类型名称,将非泛型类型切换为真正的泛型类型。
  • 我们将真实类型包装在 TypeDelegator 中,该类会拦截对该类型的任何属性(例如模板化容器)的调用。
  • 每当 ASP.NET 请求类型的TemplateContainer属性时,我们就会向其返回一个动态构造的实例,该实例包含正确的强类型信息。

在示例代码中,我创建了一个“GenericRepeater”,用于显示强类型集合及其内部项。然后,我像这样将其与 <Object, Object> 子类化,以创建ObjectRepeater

[ControlBuilder(typeof(GenericTemplatedControlBuilder))]
[GenericControlType(typeof(GenericRepeater<,>), "CollectionTypeName", "ItemTypeName")]
public class ObjectRepeater : GenericRepeater<IEnumerable<Object>, Object>

ObjectRepeater 继承了真正的泛型列表器类型,并且属性告诉 ASP.NET 使用特殊的控件生成器而不是基类。然后 GenericTemplatedControlBuilder 查找 GenericControlType 属性,并使用两个类型参数激活“真正的” GenericRepeater 实例。

在泛型列表器实现内部,我们使用其他属性标记了各种 ITemplate 属性:

[GenericTemplateContainerParameter(typeof(IndexedDataContainer<,>), 0, 1)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate { get; set; }

GenericTemplateContainerParameter 声明告诉系统要实例化哪种类型的数据容器,以及要向下传递的泛型参数的索引。因此,由于 IndexedDataContainer 接受两个泛型参数,我们告诉它从 GenericRepeater 中获取泛型参数 0 (TCollectionType) 和 1 (TItemType) 作为相关参数。

当 ASP.NET 扫描控件的属性时,我们现在已经将控件切换为泛型控件,并指示我们的GenericControlPropertyDelegator将所有对 TemplateContainer 的调用重新映射到我们真正、泛型的模板容器。

最终产品

现在一切就绪,让我们定义一个使用该控件的简单 ASP.NET 页面:

StrongTypingRenderedOutput.png

好了,这就完成了,一个简单的 ASP.NET 泛型框架,允许您处理几乎任何泛型控件场景,并且不局限于 Repeater 的示例用途。

关于代码的说明

提供的示例代码实际上并没有实现任何回发支持,所以如果您需要添加一些代码来加载/保存视图状态中的数据等等,请不要惊讶。

此外,如果您要在自己的 Web 应用程序中引用此代码,请确保将 web.config 元素一起引入以注册 CFC 标签前缀。

© . All rights reserved.