【问题标题】:Use Decorator Pattern for asp.net controls将装饰器模式用于 asp.net 控件
【发布时间】:2013-04-28 19:36:10
【问题描述】:

我创建了一些从文本框继承的自定义文本框。 对于下一步,我想使用包装器注册 javascript。

装饰器模式允许我这样做,前提是我可以从文本框继承它并将自定义文本框作为构造函数参数传递。

问题是当我将控件添加到 aspx 页面时如何使用构造函数,或者基本上如何将装饰器模式用于 asp.net 控件。

编辑:

这就是我的验证基类(IField是一个验证接口,这个可以忽略):

public abstract class ValidationBase : TextBox, IField
{
    private readonly IField _field;
    protected ValidationBase(IField field)
    {
        _field = field;
    }

    public int MinLength
    {
        get { return _field.MinLength; }
        set { _field.MinLength = value; }
    }

    public bool Required
    {
        get { return _field.Required; }
        set { _field.Required = value; }
    }

    // other porperties etc...

    protected override void OnPreRender(EventArgs e)
    {
        // DO SOME STUFF...

        base.OnPreRender(e);
    }
}

这是我的具体类(EmailField 是 IField 忽略的具体实现...):

public class ValidationEmail : ValidationBase
{
    public ValidationEmail() 
        : base(new EmailField(string.Empty))
    {
    }
}

最后我想实现这个(我已经在写字板上下定决心,这不能是确切的实现。):

public class JsRegisterDecorator : ValidationBase
{
    private readonly ValidationBase _validationObj;

    //I am not quite sure about the constructor but i can handle
    public JsRegisterDecorator(ValidationBase concreteValidationObj) 
        : base(concreteValidationObj)
    {
        _validationObj = concreteValidationObj;
    }

    //Wrap the properties

    protected override void OnPreRender(EventArgs e)
    {
        //Register JS Files...
        _validationObj.OnPreRender(e);
    }
}

问题是我该如何使用这个装饰器?因为asp.net会自动构造控件:

<vc:ValidationEmail ID="ValidationEmail1" runat="server"/>

不知道能不能用这个(构造函数参数放哪里?):

<vc:JsRegisterDecorator ID="ValidationEmailWithJs1" runat="server"/>

【问题讨论】:

  • 您能否详细说明您的具体情况?将装饰器模式应用于控件,您想实现什么,您尝试过或尝试过什么?
  • @AlexanderManekovskiy 感谢您的评论。我已经更新了我的问题。

标签: c# asp.net user-controls decorator


【解决方案1】:

我认为 Decorator 模式在这里不太合适。总的来说,我看到了更多 ASP.NET 控件的 Builder 和 Factory Method 应用。

要部分解决您的任务,您可以使用ControlBuilder。它将使您能够将控件的类型从ValidationBase 更改为JsRegisterDecorator 或ValidationEmail。您需要用ControlBuilderAttribute 装饰ValidationBase 类,从ControlBuilder 继承构建器类并覆盖Init 方法。

[ControlBuilder(typeof(ValidationBaseBuilder))]
public abstract class ValidationBase : TextBox, IField { }

public class ValidationBaseBuilder: ControlBuilder
{
    public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, System.Collections.IDictionary attribs)
    {
        var newType = typeof(/*here you can put a JsRegisterDecorator type*/);
        base.Init(parser, parentBuilder, t, tagName, id, attribs);
    }
}

但我不确定这种方法。 ControlBuilder 无法让您轻松控制构造函数。当然,您可以在 ControlBuilder 中覆盖 ProcessGeneratedCode,David Ebbo 的 blog post 值得一读,但重写构造函数以进行控制并简化解决方案并非易事。

作为替代方案,我可以建议在ValidationBase 中添加一个抽象(或虚拟)方法,如RegisterScripts,并在OnPreRender 中调用它。每个控件都将知道它需要什么脚本,并且新验证器控件的创建过程将是干净和简单的。如果您想将 JS 脚本的知识与具体实现分开,则可以使用 ASP.NET DynamicData(阅读 MetaTable)中所见的方法。

我可以看到的另一件事是,您的想法与DynamicControl 足够接近,也许可以从 ASP.NET DynamicData 中获得更多想法,例如Field Templates 和IFielTemplateFactory。

【讨论】:

  • 创建WebControl继承INamingContainer怎么样?作为孩子,它只接受 ValidationBase 和 OnLoad 它注册 javascriptes。
  • @pilavu​​st 如果适合您的情况,听起来不错。另一个想法是,您可以创建一个“每页/母版一个”ValidationScriptManager(如ScriptManager)控件,将在其中注册验证控件,并在请求期间收集、组合和返回必要的脚本。
  • 我同意你的看法。我非常感谢您的支持。要提出这个问题,我可以用我的解决方案/实施来回答它吗?
  • @pilavu​​st 当然可以。看看你如何解决这个问题会很有趣。
【解决方案2】:

我解决了我的问题 AlexanderManekovskiy 的帮助以及其他一些问题:

这是解决方案:

我将JsRegistererForValidationBase 设为WebControl 并实现了INamingContaier。

对于子元素,我创建了 Children 属性,该属性接受 Validation Base 的 olny 列表。

最后OnInit方法,我已经注册了js。

代码如下:

[ParseChildren(true)]
[PersistChildren(true)]
[ToolboxData(@"<{0}:JsRegistererForVB runat=""server""></{0}:JsRegistererForVB>")]
public class JsRegistererForValidationBase : WebControl, INamingContainer
{
    private ValidationFieldCollection _children;

    [PersistenceMode(PersistenceMode.InnerProperty)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ValidationFieldCollection Children
    {
        get
        {
            if (_children == null)
                _children = new ValidationFieldCollection();
            return _children;
        }
    }

    protected override void CreateChildControls()
    {
        Controls.Clear();
        foreach (var c in _children)
            Controls.Add(c);
    }

    protected override void OnInit(EventArgs e)
    {
        //DO THE REGISTER STUFF

        base.OnInit(e);
    }

    protected override void Render(HtmlTextWriter writer)
    {
        RenderChildren(writer);
    }
}

public class ValidationFieldCollection : List<ValidationBase> { }

}

而在aspx端它变成了这样:

<vc:JsRegisterer ID="JsRegisterer1" runat="server">
    <Children>
        <vc:ValidationEmail ID="ValidationEmail1" runat="server"/>
        <vc:ValidationEmail ID="ValidationEmail2" runat="server"/>,
        <!--etc-->
    </Children>
</vc:JsRegisterer>

为了详细的实现,我将代码添加到codeplex

【讨论】:

    猜你喜欢
    • 2011-02-02
    • 2015-02-01
    • 2013-05-07
    • 1970-01-01
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-01
    相关资源
    最近更新 更多