在 ASP.NET 中,模板化控件的一种替代方法

分享于 

10分钟阅读

Web开发

  繁體

customreplacedemo

介绍

本文解释了使用替代方法来控制模板,以便在运行时替换某些字符串值。

背景

我需要一些使用模板来支持 ASP.NET 控件和自定义HTML替换的方法。 请记住 [Key] 到"值"替换字符串的经典 ASP? 我发现在处理 inline JavaScript或者CSS样式类时非常有用,例如。 我们还可以使用反射自动在对象的所有属性中循环,并在模板中替换它们。

我还需要实现一些其他特性,所以我决定去做一个基类。

使用代码

首先,首先创建一个基类,所有模板控件都将从这个基类。

记住,自定义基类必须至少从类或者 UserControl 类( 依赖于你将使用该控件的控件) 中至少必须有。 在我们的例子中,我们将从 UserControl 中进行 inherit,因为我们希望能够在运行时动态加载模板。

下一步是为我们的替换对象创建一些容器。

// Collection of custom objects that will be// used to replace variables with actual values.private List<object> _ReplaceObjects = new List<object>();// Collection of key values that will be// used to replace variables with actual values.private NameValueCollection _ReplaceStrings = new NameValueCollection();

创建包含所有替换对象和字符串的两个成员,我们希望替换它们。 在对象的情况下,我们将替换它的public 属性值,并在字符串的情况下替换字符串值本身。 它的具体工作将在以后讨论。

当控件被呈现时,实际的魔法发生了,所以我们必须重写基类的Render 方法。

protectedoverridevoid Render(HtmlTextWriter writer)
{
 // Check if any replace objects have been addedif (ReplaceObjects.Count >0 || _ReplaceStrings.Count >0)
 {
 //// Additional code, see bellow...// }
 else {
 // Normal renderingbase.Render(writer);
 }
}

如果集合中没有替换对象或者字符串,则直接将内容呈现到输出流。

如果至少有一个对象或者字符串等待替换,我们将控件呈现给 StringBuilder,而不是直接呈现到输出流。 这给了我们在将控件发送到输出流之前操作它的呈现的HTML的机会。

StringBuilder sb = new StringBuilder();using (StringWriter sw = new StringWriter(sb))
{
 using (HtmlTextWriter tw = new HtmlTextWriter(sw))
 {
 // Render HTML to stringbuilderbase.Render(tw);
 // String.Replace method should perform faster than// using StringBuilder.Replace.string html = sb.ToString();
 //// Additional code, see bellow...//// Write replaced HTML to output stream writer.Write(html);
 }
}

如在 background 部分中已经提到的,如果你记住了在ASP中使用这种技术,我相信你还记得 替换( html"[key]","val" 用于替换类的public 属性的行。 没有恐惧,反射在这里:)

我们可以使用反射在所有属性中循环,并在运行时获取每个属性的值。

for (int i = 0; i < ReplaceObjects.Count; i++)
{
 // Loop trough all object's propertiesforeach (PropertyInfo prop in ReplaceObjects[i].GetType().GetProperties())
 {
 if (ReplaceObjects[i]!= null)
 {
 object val = prop.GetValue(ReplaceObjects[i], null);
 if (val == null)
 {
 html = html.Replace("[" + prop.Name + "]", "");
 }
 else {
 html = html.Replace("[" + prop.Name + "]", val.ToString());
 }
 }
 }
}

相当简单,我们循环了我们的替换对象 Collection,使用反射,得到对象公开的属性的一个 Collection。 属性名称用作将在模板上使用属性值替换的键。 你可以使用一个递归调用扩展这里示例,并在所有子对象中循环,以保持性能的。

替换我们的自定义字符串的Collection 更容易。 一个简单的循环调用 Replace 方法就可以。

if (_ReplaceStrings.Count >0)
{
 for (int i = 0; i < _ReplaceStrings.Count; i++)
 {
 html = html.Replace("[" + _ReplaceStrings.Keys[i] + 
 "]", _ReplaceStrings[i]);
 }
}

处理服务器控件

我们可以使用 FindControl 方法来处理服务器控件。 由于某些模板可以包含某些服务器控件,而有些则没有实现 FindControl 方法的自定义变量。 当未找到该控件时,返回控件的新实例。 操作这些"空"控件的实例不会影响呈现的内容,因为它们在页上不存在。 通过这种方式 对于模板上的每个控件都不是必需的,并且节省了大量时间。

publicstatic T FindControl<T>(this Control control, string id) where T : Control
{
 // Use normal FindControl method to get the control Control _control = control.FindControl(id);
 // If control was found and is of the correct type we return itif (_control!= null && _control is T)
 {
 return (T)_control;
 }
 // Use reflection to create a new instance of the controlreturn (T)Activator.CreateInstance(typeof(T));
}

我发现这种方法非常有用,我决定将它作为扩展程序方法实现,以便在从 Control 基类继承的所有 ASP.NET 控件上使用它。

准备模板

这是一个简单的模板示例,它使你可以同时组合服务器控件和替换字符串。

<%@ControlLanguage="C#"AutoEventWireup="true"Inherits="CustomReplaceDemo.CustomReplaceBase"%><h2> [Title]</h2>(ID: [ArticleID])<br/>Price: [Price] EUR<asp:Buttonrunat="server"ID="bDetils"Text="Details"OnClientClick="javascript:alert('[Title] costs [Price] EUR.');return false;"/>

记住,所有模板都必须从模板基类中进行 inherit。

快速示例

在ASPX页上放置一个 Repeater 控件:

<asp:RepeaterID="rItems"runat="server"><ItemTemplate></ItemTemplate></asp:Repeater>

将你想要的任何数据绑定到 Repeater 控件。 在本例中,它是一个基本产品类,它保存一些产品数据,如文章 ID。标题和价格。

当数据绑定时,我们为每个转发器项加载模板,就像加载常规用户控件一样。

void rItems_ItemDataBound(object sender, 
 System.Web.UI.WebControls.RepeaterItemEventArgs e) {
 if (e.Item.ItemType == ListItemType.Item || 
 e.Item.ItemType == ListItemType.AlternatingItem)
 {
 Article article = (Article)e.Item.DataItem;
 // Load template CustomReplaceBase articleTemplate = (CustomReplaceBase)
 LoadControl("Templates/ArticleTemplate.ascx");
 // Add custom replace object articleTemplate.ReplaceObjects.Add(article);
 // Add custom replace string articleTemplate.ReplaceStrings["Number"] = _Count++.ToString();
 // Add template to list item e.Item.Controls.Add(articleTemplate);
 // Bind template controls...// No logic is implemented on the template itself. Button bDetils = articleTemplate.FindControl<Button>("bDetils");
 // You can now add event listeners or manipulate// controls on the temaplte...// bDetils.Text ="My new text";// bDetils.Click += new EventHandler(bDetils_Click); }
} 

postback 处理

postback 控件必须订阅基本模板中的postback 事件处理程序,这将将请求转发给代码波纹管。

<asp:Buttonrunat="server"ID="bPostback"Text="Postback"OnClick="Postback"/>


在模板上订阅 postback 事件有两种方法。 首先,我们可以订阅模板上的OnPostback事件,或者为承载模板的页面实现一个定制的基类。

// Subscribe to the postback forwarding eventarticleTemplate.OnPostback += new EventHandler(articleTemplate_OnPostback);
// Postback, outoevent wireuppublicoverridevoid OnPostback(object sender, EventArgs e)
{
. . .
}

结束语

我已经在几个项目中使用这种方法,而且它们似乎工作得很好。 当然,版本版本应该与演示项目有点不同,比如在模板更改时启动应用程序。


控制  asp  asp-net  TEMP  ALT  Controls  
相关文章