ASP.NET自定义控件-复合

时间:2020-03-05 18:41:00  来源:igfitidea点击:

概括

大家好,
好的,通过自定义控件进一步探索我的冒险...

总而言之,我已经了解了自定义控件的三个主要"类"。如果有任何错误,请随时纠正我!

  • UserControls-从UserControl继承并包含在ASCX文件中。它们的功能相当有限,但是在设计人员的支持下,这是获得某些UI通用性的一种快速简便的方法。
  • 自定义复合控件-这些控件从WebControl继承,我们可以在其中向CreateChildControls方法内的控件添加预先存在的控件。这提供了很大的灵活性,但是缺少设计人员的支持而没有其他编码。由于它们可以编译成DLL,因此它们具有很高的可移植性。
  • 自定义渲染控件-与自定义复合控件类似,它们被添加到Web控件库项目中。控件的呈现完全由程序员通过重写Render方法来控制。

我的想法..

好的,因此在使用自定义复合材料时,我发现了以下内容:

  • 我们几乎无法控制HTML输出,因此很难"调试"。
  • CreateChildControls(和后续方法)可以随处使用Controls.Add(myControl)。
  • 我发现渲染表(用于布局或者内容)相当尴尬。

问题..

因此,我承认,我对此并不陌生,因此我可能会因为上文提到的一些观点而偏离基础。

  • 我们是否使用复合材料?
  • 我们有任何巧妙的技巧来控制HTML输出吗?
  • 我们是否只是说"让它死了"然后继续创建自定义呈现控件?

因为我知道良好的控件开发可以节省多少总体开发时间,所以我非常想让自己牢牢记住这一点。

期待回答^ _ ^

解决方案

回答

我说继续使用自定义呈现控件。我发现,在大多数情况下,合成可以更轻松地完成并在UserControl中使用,但除此之外,我们还需要具有更好的控制度(无双关),才能拥有自己的渲染策略。

也许控件很简单,值得组合(例如,一个文本框与一个基于javascript / dhtml的datepicker结合使用),但是除了那一个示例之外,自定义呈现的控件似乎是可行的方法。

回答

我经常使用复合控件。而不是覆盖Render或者RenderContents,只需为每个Control分配一个CssClass并使用样式表即可。对于多个Controls.Add,我使用扩展方法:

//Controls.Add(c1, c2, c3)
static void Add(this ControlCollection coll, params Control[] controls)
 { foreach(Control control in controls) coll.Add(control);
 }

为了快速而肮脏的渲染,我使用如下代码:

writer.Render(@"<table>
                   <tr><td>{0}</td></tr>
                   <tr>
                       <td>", Text);
control1.RenderControl(writer);
writer.Render("</td></tr></table>");

为了初始化控件属性,我使用属性初始化程序语法:

childControl = new Control {  ID="Foo"
                            , CssClass="class1"
                            , CausesValidation=true;
                           };

回答

在我们拥有大型Web应用程序并且想要在许多地方重用大块的情况下,使用自定义复合控件很重要。然后,我们将只添加我们正在开发的控件的子控件,而无需重复自己。
在最近的一个大型项目中,我们所做的工作如下:

  • 每个复合控件都有一个容器。用作控件内部所有内容的包装。
  • 每个复合控件都有一个模板。一个仅包含模板标记的ascx文件(不包含<%Control%>指令)。
  • 容器(本身就是控件)是从模板初始化的。
  • 容器公开模板中所有其他控件的属性。
  • 我们只能在复合控件中使用this.Controls.Add([the_container])。

实际上,我们需要一个基类,该基类将负责使用指定的模板初始化容器,并且在模板中未找到控件时也会引发异常。当然,在小型应用程序中这可能是过大的选择。如果我们没有重用代码和标记,而只想编写简单的控件,那么最好使用用户控件。

回答

这是我用于自定义渲染的另一种扩展方法:

public static void WriteControls
        (this HtmlTextWriter o, string format, params object[] args)
 { 
    const string delimiter = "<2E01A260-BD39-47d0-8C5E-0DF814FDF9DC>";
    var controls  = new Dictionary<string,Control>();

    for(int i =0; i < args.Length; ++i)
    { 
       var c = args[i] as Control; 
       if (c==null) continue;
       var guid = Guid.NewGuid().ToString();
       controls[guid] = c;
       args[i] = delimiter+guid+delimiter;
    }

    var _strings = string.Format(format, args)
                         .Split(new string[]{delimiter},
                                StringSplitOptions.None);
    foreach(var s in _strings)
    { 
       if (controls.ContainsKey(s)) 
           controls[s].RenderControl(o);
       else 
           o.Write(s);
    }
}

然后,要在RenderContents()方法中呈现自定义合成,我将其编写为:

protected override void RenderContents(HtmlTextWriter o)
{ 
    o.WriteControls
         (@"<table>
               <tr>
                    <td>{0}</td>
                    <td>{1}</td>
               </tr>
             </table>"
            ,Text
            ,control1);
 }

回答

罗布,你是对的。我提到的方法是一种混合方法。拥有ascx文件的优点是,在我所见过的每个项目中,设计人员都可以最轻松地编辑实际的标记,并且我们和设计人员可以分别工作,而ascx则最为舒适。如果以后不打算对控件本身进行实际的CSS /标记/设计更改,则可以使用自定义的呈现控件。如我所说,我的方法仅适用于更复杂的场景(这些可能是我们需要设计师的地方:)

回答

我们也许可以利用此技术来简化设计时间:

http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx

基本上,我们是在运行时使用LoadControl方法创建用户控件的实例,然后将其交给某种状态包,然后将其添加到控件树。因此,复合控件实际上将像一个控制器一样工作,而.ascx文件将类似于一个视图。

这样可以省去实例化整个控件树并在C#中设置控件样式的麻烦!