ASP.NET自定义控件-复合
概括
大家好,
好的,通过自定义控件进一步探索我的冒险...
总而言之,我已经了解了自定义控件的三个主要"类"。如果有任何错误,请随时纠正我!
- 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#中设置控件样式的麻烦!