简体   繁体   English

对于asp.net控件使用Decorator Pattern

[英]Use Decorator Pattern for asp.net controls

I've created some custom textboxes which are inherited from textbox. 我创建了一些从文本框继承的自定义文本框。 For the next step I want to register javascript with a wrapper. 对于下一步,我想用包装器注册javascript。

Decorator pattern allow me to do if only I can inherit it from textbox and pass custom textbox as a constructor parameter. 装饰器模式允许我这样做,只要我可以从文本框继承它并传递自定义文本框作为构造函数参数。

Problem is that how can I use constructor when I add a control to aspx page or basically how can I use decorator pattern for asp.net controls. 问题是我在向aspx页面添加控件时如何使用构造函数,或者基本上如何在asp.net控件中使用装饰器模式。

EDIT: 编辑:

Simply this is my validation base class (IField is an validation interface. This can be ignored): 这只是我的验证基类(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);
    }
}

And this is my concrete class (EmailField is a concrete impl. of IField ignore...): 这是我的具体课程(EmailField是IField忽略的具体内容......):

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

And finally I want to implement this (I've made up my mind on wordpad this can't be the exact impl.): 最后我想实现这个(我已经在wordpad上下定决心,这不是确切的impl。):

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);
    }
}

The problem is that How can I use this decorator? 问题是我怎么能用这个装饰器? Because asp.net construct controls automatically: 因为asp.net构造控件自动:

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

I don't know can I use this (where can I put the constructor parameter?): 我不知道我可以使用它(我可以在哪里放置构造函数参数?):

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

I don't think Decorator pattern fits well here. 我认为Decorator模式不适合这里。 In general I saw more applications of Builder and Factory Method for ASP.NET controls. 总的来说,我看到了更多针对ASP.NET控件的Builder和Factory Method的应用程序。

To partially solve your task you can use ControlBuilder . 要部分解决您的任务,您可以使用ControlBuilder It will give you ability to change the type of the control from ValidationBase to JsRegisterDecorator or ValidationEmail . 它将使您能够将控件的类型从ValidationBase更改为JsRegisterDecoratorValidationEmail You need to decorate ValidationBase class with ControlBuilderAttribute , inherit builder class from ControlBuilder and override Init method. 您需要使用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);
    }
}

But I'm not sure about such approach. 但我不确定这种做法。 ControlBuilder cannot give you easy control over constructor. ControlBuilder无法让您轻松控制构造函数。 Surely you can override ProcessGeneratedCode in ControlBuilder and David Ebbo has a blog post worth reading but it would not be an easy task to rewrite constructor for control and make solution simple. 当然,你可以在ControlBuilder中覆盖ProcessGeneratedCode ,而David Ebbo有一篇值得一读的博客文章,但重写构造函数进行控制并简化解决方案并不是一件容易的事。

As alternative that will work I can suggest to add an abstract (or virtual) method like RegisterScripts inside ValidationBase and call it in OnPreRender . 作为替代方案,我可以建议在ValidationBase添加一个抽象(或虚拟)方法,如RegisterScripts ,并在OnPreRender调用它。 Every control will know what scripts it needs and the process of new validator control creation will be clean and simple. 每个控件都会知道它需要哪些脚本,并且新验证器控件创建的过程将是简洁的。 If you want to separate knowledge of JS scripts from concrete implementations then approach as seen in ASP.NET DynamicData (read MetaTable ) could be used. 如果您想将JS脚本的知识与具体实现分开,那么可以使用ASP.NET DynamicData(read MetaTable )中的方法。

Another thing that I can see is that your idea is close enough to DynamicControl and maybe it would be possible to get more ideas from ASP.NET DynamicData like Field Templates and IFielTemplateFactory . 我能看到的另一件事是你的想法足够接近DynamicControl ,也许有可能从ASP.NET DynamicData获得更多的想法,比如Field TemplatesIFielTemplateFactory

I solve my problem AlexanderManekovskiy's help and also some other questions: 我解决了我的问题AlexanderManekovskiy的帮助以及其他一些问题:

And here is the solution: 这是解决方案:

I've made JsRegistererForValidationBase as a WebControl and implemented INamingContaier . 我已将JsRegistererForValidationBase作为WebControl并实现了INamingContaier

For the children elements I've created Children property which accepts olny list of Validation Base . 对于children元素,我创建了Children属性,它接受了Validation Base olny列表。

And finally OnInit method, I've registered the js. 最后是OnInit方法,我已经注册了js。

Here is the code: 这是代码:

[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> { }

} }

And at the aspx side it becomes like this: 在aspx方面它变成这样:

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

For the detailed imlementation I added the code to codeplex 对于详细的实现,我将代码添加到codeplex中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM