对于asp.net控件使用Decorator Pattern

时间:2013-05-04 14:06:09

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

我创建了一些从文本框继承的自定义文本框。 对于下一步,我想用包装器注册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))
    {
    }
}

最后我想实现这一点(我已经在wordpad上下定决心,这不是确切的意思。):

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"/>

2 个答案:

答案 0 :(得分:1)

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

要部分解决您的任务,您可以使用ControlBuilder。它使您能够将控件的类型从ValidationBase更改为JsRegisterDecoratorValidationEmail。您需要使用ValidationBase装饰ControlBuilderAttribute类,从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值得阅读,但重写构造函数以进行控制并简化解决方案并不是一件容易的事。

作为替代方案,我可以建议在RegisterScripts中添加ValidationBase之类的抽象(或虚拟)方法,并在OnPreRender中调用它。每个控件都会知道它需要哪些脚本,并且新验证器控件创建的过程将是简洁的。如果您想将JS脚本的知识与具体实现分开,那么可以使用ASP.NET DynamicData(read MetaTable)中的方法。

我能看到的另一件事是你的想法足够接近DynamicControl,也许有可能从ASP.NET动态数据中获得更多的想法,如Field TemplatesIFielTemplateFactory

答案 1 :(得分:1)

我解决了我的问题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