概要
大家好,
好的,通过自定义控件进一步探索我的冒险...
总而言之,我已经了解了自定义控件的三个主要"类"。如果有任何错误,请随时纠正我!
UserControls-从UserControl继承并包含在ASCX文件中。它们的功能相当有限,但是在设计人员的支持下,这是获得某些UI通用性的一种快速简便的方法。
自定义复合控件-这些控件从WebControl继承,您可以在其中向CreateChildControls方法内的控件添加预先存在的控件。这提供了很大的灵活性,但是缺少设计人员的支持而没有其他编码。由于它们可以编译成DLL,因此它们具有很高的可移植性。
自定义渲染控件-与自定义复合控件类似,它们被添加到Web控件库项目中。控件的呈现完全由程序员通过重写Render方法来控制。
我的想法..
好的,因此在使用自定义复合材料时,我发现了以下内容:
-
您几乎无法控制HTML输出,因此很难"调试"。
-
CreateChildControls(和后续方法)可以随处忙于Controls.Add(myControl)。
-
我发现渲染表(用于布局或内容)相当尴尬。
问题。
因此,我承认,我对此并不陌生,因此我可能会因为上文提到的一些观点而偏离基础。
-
您是否使用复合材料?
-
您有任何巧妙的技巧来控制HTML输出吗?
-
您是否只是说"让它死了"然后继续创建自定义呈现控件?
因为我知道良好的控件开发可以节省多少总体开发时间,所以我很想让自己牢牢记住这一点。
期待您的回答^ _ ^
我说继续使用自定义呈现控件。我发现,在大多数情况下,合成可以更轻松地完成并在UserControl中使用,但是除此之外,您还需要具有更好的控制度(无双关),才能拥有自己的渲染策略。
也许控件很简单,值得组合(例如,一个文本框与一个基于javascript / dhtml的datepicker结合使用),但是除了那一个示例之外,自定义呈现的控件似乎是必经之路。
这是我用于自定义渲染的另一种扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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()方法中呈现自定义合成,请编写以下代码:
1 2 3 4 5 6 7 8 9 10 11 12
| protected override void RenderContents(HtmlTextWriter o)
{
o.WriteControls
(@"<table>
<tr>
<td>{0}</td>
<td>{1}</td>
</tr>
</table>"
,Text
,control1);
} |
罗布,你是对的。我提到的方法是一种混合方法。拥有ascx文件的优点是,在我所见过的每个项目中,设计人员都可以最轻松地编辑实际标记,并且您和设计人员可以分别工作,而ascx则最为舒适。如果以后不打算对控件本身进行实际的CSS /标记/设计更改,则可以使用自定义的呈现控件。如我所说,我的方法仅适用于更复杂的场景(这些可能是您需要设计师的地方:)
在您拥有大型Web应用程序并且想要在许多地方重用大块的情况下,使用自定义复合控件很重要。然后,您将只添加您正在开发的控件的子控件,而无需重复自己。
在最近的一个大型项目中,我们所做的工作如下:
-
每个复合控件都有一个容器。用作控件内部所有内容的包装。
-
每个复合控件都有一个模板。一个仅包含模板标记的ascx文件(不包含<%Control%>指令)。
-
容器(本身就是控件)是从模板初始化的。
-
容器公开模板中所有其他控件的属性。
-
您只能在复合控件中使用this.Controls.Add([the_container])。
实际上,您需要一个基类,该基类将负责使用指定的模板初始化容器,并且在模板中找不到控件时也会引发异常。当然,在小型应用程序中这可能是过大的选择。如果您没有重用代码和标记,而只想编写简单的控件,那么最好使用用户控件。
我经常使用复合控件。不必覆盖Render或RenderContents,只需为每个Control分配一个CssClass并使用样式表即可。对于多个Controls.Add,我使用扩展方法:
1 2 3 4
| //Controls.Add(c1, c2, c3)
static void Add(this ControlCollection coll, params Control[] controls)
{ foreach(Control control in controls) coll.Add(control);
} |
为了快速而肮脏的渲染,我使用如下代码:
1 2 3 4 5 6
| writer.Render(@"<table>
<tr><td>{0}</td></tr>
<tr>
<td>", Text);
control1.RenderControl(writer);
writer.Render("</td></tr></table>"); |
为了初始化控件属性,我使用属性初始化程序语法:
1 2 3 4
| childControl = new Control { ID="Foo"
, CssClass="class1"
, CausesValidation=true;
}; |
您可能可以利用此技术来简化设计时间:
http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx
基本上,您是在运行时使用LoadControl方法创建用户控件的实例,然后将其交给某种状态包,然后将其附加到控件树。因此,您的复合控件实际上将像一个控制器一样工作,而.ascx文件将类似于一个视图。
这样可以省去实例化整个控件树并在C#中设置控件样式的麻烦!