将属性扩展程序提供程序与 ASP.NET 2.0一起使用

分享于 

16分钟阅读

Web开发

  繁體

介绍

在 Windows 窗体环境中广泛使用属性扩展程序提供程序,其中的技术充分支持. NET 框架和 Visual Studio IDE的支持。 相反,ASP.NET 开发人员并不总是完全欣赏技术的好处。 本文演示了如何利用 ASP.NET 自定义控件项目中的属性扩展器,并提出 ASP.NET 2.0和 Visual Studio 2005设计时的问题。

用户界面需求的常见情况是调用Web窗体上显示的控件以公开特定的自定义属性集。 例如某些控件可以根据当前用户凭据禁用自身,或者显示某种交互式上下文敏感帮助。 通常,web开发人员通过在各种情况下使用属性扩展器提供程序来实现自定义控件的库。

扩展程序提供程序向其他控件添加新属性。 作为属性提供程序的组件或者控件负责所提供属性的行为。

Windows 表单 Visual Studio的世界为属性扩展程序提供全面支持,包括内置CodeDom序列化,从而使得实现简单简单。 另一方面,自定义web控件和组件公开扩展程序提供者的开发人员被迫定制自己的序列化逻辑代码。 在大多数情况下,这种方法与非视觉设计组件相比较好,直到 Visual Studio 2005从开发人员中删除组件和CodeDom序列。

背景

CodeProject已经发布了关于这里主题的许多文章:

  • 在studio的可视化 ASP.NET 中,通过 Wouter Vugt修复 IExtenderProvider。 本文展示了如何通过使用CodeDom序列化来克服扩展属性的序列化困难。 由于 ASP.NET 框架不再支持CodeDom序列化,这里方法将不使用 ASP.NET 2.0.
  • ASP.NET 中的扩展程序提供程序组件: 一个IExtenderProvider实现,由 Frank Robijn。 Frank使用提供程序本身的'buffer'属性演示了更简单的替代方案,它负责存储'客户端'控件的属性值。 这种方法虽然有用且非常简单,但它的局限性和缺乏优雅开发人员在完成解决方案中所期望的。

在本文中,我将在大量介绍属性扩展器概念,并在使用 2.0和 Visual Studio 2005时分享如何处理特定属性的问题。

属性扩展程序剖析

扩展程序提供程序组件可以向容器中的其他组件提供属性。 在某种意义上,这些其他组件充当扩展程序提供者的客户端。 一个众所周知的例子是 ErrorProvider 组件。 将 ErrorProvider 控件添加到窗体时,所有其他控件都将 Error 属性添加到它的属性的List。

IExtenderProvider接口

IExtenderProvider 接口允许设计环境查询方法 CanExtend 以标识容器中能够接收扩展程序属性的所有对象。

publicbool CanExtend(object extendee)
{
 return extendee is TextBox;
}

ProvidePropertyAttribute

此类级别属性定义属性扩展程序向其他组件提供的属性的NAME。 以属性标记的类必须以 Set<name> <名称> ( 如果需要) 和 Get<name> <名称> 方法的形式提供属性代码的实现。

[ProvideProperty("Validation", typeof(TextBox))]publicclass ValidatorControl
...public TextValidator GetValidation(TextBox textBox)
{.. .

可以看到属性扩展程序概念相当简单,并且在向项目中的组件添加新功能时允许很大程度的灵活性。 接下来,我们将介绍如何为 ASP.NET 应用程序构建属性扩展器。

ASP.NET 中的属性扩展程序

为了更好地说明这个相当复杂的主题,所有概念和技术都将在示例项目 MutlifieldValidator ( source附在本文中)的上下文中讨论。 它已经在 C# 2.0开发,Visual Studio 2005 β 2. 在. NET 1.1和 Visual Studio 2003中不能使用泛型和它的他新框架特性的编码时,这个项目也可以轻松移植。

多字段验证程序概述

MutlifieldValidator解决了验证文本输入的问题,具有与实际理论练习相关的实际开发任务的强大目的。 在这个问题域的选择中,读者可以轻松地将这个解决方案与现有的'out-of-the-box'ASP.NET 验证器控件( 比如可用性和。

假定以下需求定义将我们在技术上的偏好提升到属性扩展程序:

  • 表单上的每个文本框都应该有一个单独指定的正则表达式,并且当正则表达式不匹配时显示错误消息。
  • 标准验证器控件不是一个更好的选项,因为这个应用程序中有限的实际地产。

就像我们所要求的,属性扩展程序控件必须为每个文本框提供两个属性- ErrorMessageExpression。 在优秀的设计实践中,不要单独提供这些属性,我们将把它们封装到一个名为 TextValidator。 由于我们还需要对宿主属性扩展程序的引用以及客户端控件的ID,因此这些项在属性 HostingControlTarget 中都得到了。

将适当类型转换器属性放置在类级别上 [TypeConverter(typeof(ExpandableObjectConverter))] 允许客户端文本框在设计器网格的属性中以优雅可以扩展的方式显示这些属性。

我们将在属性网格中隐藏 HostingControlTarget,方法是用 [Browsable(false)] 属性标记它们。

ValidatorControl 类是从 System.Web.UI.WebControl.Label 继承的,因这里公开了标签的所有属性,使它的作为错误消息的显示方式。

从图 上面 可以看到,ValidatorControl 通过定义 CanExtend 方法 换句话说,限制了'客户端'控件的范围来实现 IExtenderProvider 接口。

publicbool CanExtend(object extendee)
{
 return extendee is TextBox;
}

为了实际定义一个提供的属性,我们使用一个 ProvideProperty 属性和一个 Get 方法来实现属性。 由于提供的属性是处理它的内部状态的TextValidator,因此不需要 Set 方法。

[ProvideProperty("Validation", typeof(TextBox))]...public TextValidator GetValidation(TextBox textBox)
{ 
 return FindValidation(textBox);
}
...

如果没有找到,FindValidation 方法必须在封装的集合中为每个 TextBox 实例找到对应的TextValidator,或者创建一个新的TextValidator

ValidatorControl 还实现 IValidator 接口使它成为由托管它的Web窗体拥有的确认器集合的一部分。 这个特性和基于 正规表达式的实际验证逻辑相当简单,示例项目中的源代码是自解释的。 这些细节在这篇叙述中省略了。

为什么不工作

执行了所有这些或者类似的步骤之后,你应该拥有一个完全功能的属性扩展程序控件,如果这是在 Windows 窗体环境中。 那么什么是与网页形式不同的? 如果在实际的Web窗体上测试控件,将发现分配给'客户端'控件的属性值在相同的设计会话中是'消失',并且没有持久化运行时。 显然,问题是属性序列化。 在 Windows 窗体中,设计器在表单类的ComponentInitialize 方法中将扩展属性序列化为 CodeDom,但 Visual Studio 2003的Web表单设计器不知道。 但是,它拥有和维护相同的ComponentInitialize 画布方法,使得你可以使用一些漂亮的CodeDom步法( 有关详细信息,请参阅文章的Wouter van Vugt ) 编写属性的定制序列化。 但是有一个难题。 ComponentInitialize 和CodeDom序列化在 Visual Studio 2005版本中不可用,如 Beta 2版本。 这些特性是'已经停止'的,支持更直接的基于html的持久性。 这意味着如果我们想要序列化这些属性,我们就必须找到CodeDom的替代方法。

解决所提供属性的序列化问题

有许多用于序列化和反序列化基于它的集合的属性的有用属性。 由于 ValidatorControlTextValidator 对象集合公开为名为 Validators ( 集合中的每个成员都负责验证 TextBox )的属性,我们可以利用这些属性。 给定 below 是该属性的声明:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)][PersistenceMode(PersistenceMode.InnerProperty)]
[Browsable(false)]public TextValidatorCollection Validators
{
 get {
 return m_Validators;
 }
}
  • DesignerSerializationVisibility 指示Web窗体设计环境将这里属性作为内容 换句话说,HTML标记与 ComponentInitialize 保持。
  • PersistenceMode 属性告诉设计器将属性保存为 InnerProperty,这是在 ValidatorControl的开始和结束标记之间序列化我们的集合。
  • 我们不希望在设计器网格的属性中公开这里属性,因此我们将它的标记为 Browsable(false) 属性。

这是结果HTML的一个示例:

<cc1:ValidatorControlID="multiFieldValidator"runat="server"IsValid="True"Font-Bold="True"Font-Size="Small"ForeColor="Red"><Validators><cc1:TextValidatorExpression="^d{0,10}$"Target="txtPhone"ErrorMessage="phone number should include only numerics"></cc1:TextValidator><cc1:TextValidatorExpression="^[a-zA-Z]{0,16}$"Target="txtLastName"ErrorMessage="last name should include only alpha characters"></cc1:TextValidator><cc1:TextValidatorExpression="^[a-zA-Z]{0,16}$"Target="txtFirstName"ErrorMessage="first name should include only alpha characters"></cc1:TextValidator></Validators></cc1:ValidatorControl>

你可以看到所有的TextValidator 对象都被整齐地放在 ValidatorControl 标签里。 这负责序列化。 现在,我们应该如何反序列化 TextValidators?

还有一个属性可以使这个任务成为一个简单的。 它被称为 ParseChildren 并且被放置在一个类级别上。 在应用属性时,我们指定必须将这里控件中的一个属性反序列化为子集合。 这就是它的工作方式:

[ParseChildren(true, "Validators")]publicclass ValidatorControl : Label, IExtenderProvider, 
 IValidator
{.. .

现在我们已经讨论了序列化和反序列化,仍然有一个小问题。 如果在设计时更改扩展属性的值,你将注意到,Web窗体设计器并不总是更新表示你的ValidationControl的HTML标记。 因为 Visual Studio 不能识别 TextBox 属性网格内所做的更改,以及与 ValidatorControl 相关的任何方式。 让我们看看我们能做些什么。

修正设计时间行为

因为在Web表单设计器功能中有明显的差异,因这里可以通知 ValidationControl 设计人员对它的状态的更改。 下面是 ValidatorControl 中的一个方法,每当需要它时,都可以对 Validators 属性的设计时间表示进行'刷新':

internalvoid NotifyDesigner()
{
 if (this.DesignMode)
 {
 IDesignerHost dh = 
 this.Site.Container as IDesignerHost;
 LabelDesigner designer = 
 dh.GetDesigner(this) as LabelDesigner;
 PropertyDescriptor pd =
 TypeDescriptor.GetProperties(this)["Validators"];
 ComponentChangedEventArgs ccea = 
 new ComponentChangedEventArgs(this, pd,
 Validators, Validators);
 designer.OnComponentChanged(this, ccea);
 }
}

这里发生的是让设计者通过将 ComponentChanged 事件显式发送到设计时环境来刷新它的ValidatorControl 呈现。 这将导致 Visual Studio 呈现控件的HTML标记。 当我们使用定制构建集合( TextValidatorCollection ) 存储 TextValidators 时,可以确保集合中的每个成员都有对它的父元素的引用( 内部属性和成员变量)。 private ValidatorControl m_Control; )。

TextValidator的公开属性中,每次设置属性时,我只调用 ValidatorControlNotifyDesigner

[Description("regex expression for validating a textbox")]publicstring Expression
{
 get {
 return m_Expression;
 }
 set {
 m_Expression = value;
 NotifyDesigner();
 }
}/// notifies hosting control that a property/// has been changed (check for null for run-time)privatevoid NotifyDesigner()
{
 if (m_Control!= null)
 {
 m_Control.NotifyDesigner();
 }
}

这种简单技术将使 ValidatorControl 正确反映设计时对扩展属性所做的更改。 最后,我们拥有一个完全功能的属性扩展控件,它与 ASP.NET 1.1和 ASP.NET 2.0一起发挥良好的作用。

结束语

本文展示了在 ASP.NET 项目中利用强大扩展技术的一种相当简单的方法。 设置属性扩展器控件所涉及的编码工作非常平衡,最终解决方案的灵活性和清洁性。 开发人员介绍这里问题的尝试。 我们已经将 ASP.NET 2.0带来的更改不鼓励网络表单,因为我们已经证明了有一种方法可以克服扩展属性和 Visual Studio 设计器事件模型的序列化困难。

本文对 ASP.NET 环境下的属性扩展进行了最后的分析,并对它的进行了充分的研究。 我相信有很多可以能导致更好或者更简单模型的设计和实验机会。 开心编码!

历史记录

  • 7/11/2005 - 发布的原始文章。

ext  asp  asp-net  Using  Extend  PROP  
相关文章