【转载】直接通过User Control生成HTML
来源:互联网 发布:百旺税控开票软件下载 编辑:程序博客网 时间:2024/05/13 16:51
对于使用User Control生成HTML的方式,大家应该已经比较熟悉了,老赵也曾经写过一篇文章(《技巧:使用User Control做HTML生成》)来描述这个做法。使用User Control进行HTML生成最大的好处就是将表现(Presentation)逻辑集中在一处,并且能够让前台开发人员使用传统的方式参与到页面开发中来。在其他方面,使用User Control生成HTML的做法直接使用了ASP.NET WebForms引擎,在开发时能够利用到ASP.NET的各种优秀实践。
在“我的衣橱”中大量使用了这种生成HTML的做法。不过当项目达到一定规模之后,这个方法的不足之处也慢慢地体现了出来。就拿《技巧:使用User Control做HTML生成》作为例子来讲,除了显示上必要的Comments.aspx页面和Comments.ascx控件之外,还有一个额外的GetComments.ashx进行客户端与服务器端之间的通信。不过问题就出在这里,当此类做法越来越多时,项目中就会出现大量的此类ashx文件。冗余代码的增加降低了代码的可维护性,试想如果我们需要在某个控件上增加一个额外的属性,就需要去Handler那里编写对应的逻辑。虽不算是繁重的工作,但是如果能解决这个问题,无疑是一个锦上添花的做法。
如果要避免大量Handler的出现,必然需要找到这些Handler的共同之处。这一点并不困难,因为每个Handler的逻辑的确大同小异:
- 使用ViewManager加载User Control
- 从QueryString或Form中获取参数,并设置对应属性
- 使用ViewManager生成HTML代码,并使用Response.Write输出
写一个统一的Handler来将User Control转化为HTML是一件轻而易举的事情。不过因为有第2步,我们就必须应对为不同控件赋不同值的情况。这种“描述性”的内容即是该控件的“元数据(metadata)”,而说到“元数据”各位应该就能很快想到自定义属性(Custom Attribute)这个.NET特有的事物。因此我们的解决方案也使用这种方式来对控件的属性进行“描述”,且看该属性的定义:
public enum UserControlRenderingPropertySource{ Form, QueryString} [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]public class UserControlRenderingPropertyAttribute : Attribute{ public string Key { get; set; } public UserControlRenderingPropertySource Source { get; set; }}
这个自定义属性只能标记在属性上,而它的作用是定义这个属性值的来源(是Query String还是Form)与集合中的键名。而我们的实现还会根据属性上标记的DefaultValueAttribute为属性设定默认值。定义了Custom Attrubte之后,我们就可以编写这个统一的Handler了。为了在客户端“隐藏”直接请求ascx文件的事实,我们只响应对扩展名为“ucr”的请求:
public class UserControlRenderingHandler : IHttpHandler{ public void ProcessRequest(HttpContext context) { string appRelativePath = context.Request.AppRelativeCurrentExecutionFilePath; string controlPath = appRelativePath.ToLower().Replace(".ucr", ".ascx"); var viewManager = new ViewManager<UserControl>(); var control = viewManager.LoadViewControl(controlPath); SetPropertyValues(control, context); context.Response.ContentType = "text/html"; context.Response.Write(viewManager.RenderView(control)); }}
上面代码中的SetPropertyValues方法便是为User Control实例的属性赋值:
private static void SetPropertyValues(UserControl control, HttpContext context){ var metadata = GetMetadata(control.GetType()); foreach (var property in metadata.Keys) { object value = GetValue(metadata[property], context) ?? GetDefaultValue(property); if (value != null) { property.SetValue(control, Convert.ChangeType(value, property.PropertyType), null); } }}
SetPropertyValues方法中会调用三个方法:GetMetadata,GetValue和GetDefaultValue。GetMetadata方法会得到关于这个control的元数据,为PropertyInfo - List<UserControlRenderingPropertyAttribute>的键值对(即Dictionary)。然后将metadata传入GetValue方法,以获取由UserControlRenderingPropertyAttribute标记的值。如果GetValue方法返回null,则调用GetDefaultValue方法获取标记在该属性上DefaultValueAttribute。以下就将其余代码附上,没有技术含量,但做一参考:
[展开代码]
private staticDictionary<
Type,
Dictionary<
PropertyInfo,
List<UserControlRenderingPropertyAttribute>>> s_metadataCache =
new Dictionary<
Type,
Dictionary<
PropertyInfo,
List<UserControlRenderingPropertyAttribute>>>();
private staticDictionary<PropertyInfo,object> s_defaultValueCache =
new Dictionary<PropertyInfo,object>();
private staticobject s_mutex = newobject();
private staticDictionary<
PropertyInfo,
List<UserControlRenderingPropertyAttribute>> GetMetadata(Type type)
{
if (!s_metadataCache.ContainsKey(type))
{
lock (s_mutex)
{
if (!s_metadataCache.ContainsKey(type))
{
s_metadataCache[type] = LoadMetadata(type);
}
}
}
return s_metadataCache[type];
}
private staticDictionary<
PropertyInfo,
List<UserControlRenderingPropertyAttribute>> LoadMetadata(Type type)
{
var result = new Dictionary<PropertyInfo,List<UserControlRenderingPropertyAttribute>>();
PropertyInfo[] properties = type.GetProperties(
BindingFlags.Public |BindingFlags.Instance | BindingFlags.SetProperty);
foreach (var pin properties)
{
var attributes = p.GetCustomAttributes(
typeof(UserControlRenderingPropertyAttribute),true);
if (attributes.Length > 0)
{
result[p] = new List<UserControlRenderingPropertyAttribute>(
attributes.Cast<UserControlRenderingPropertyAttribute>());
}
}
return result;
}
private staticobject GetDefaultValue(PropertyInfo property)
{
if (!s_defaultValueCache.ContainsKey(property))
{
lock (s_mutex)
{
if (!s_defaultValueCache.ContainsKey(property))
{
var attributes = property.GetCustomAttributes(typeof(DefaultValueAttribute),true);
object value = attributes.Length > 0 ?
((DefaultValueAttribute)attributes[0]).Value :null;
s_defaultValueCache[property] = value;
}
}
}
return s_defaultValueCache[property];
}
private staticvoid SetPropertyValues(UserControl control,HttpContext context)
{
var metadata = GetMetadata(control.GetType());
foreach (var propertyin metadata.Keys)
{
object value = GetValue(metadata[property], context) ?? GetDefaultValue(property);
if (value != null)
{
property.SetValue(control, Convert.ChangeType(value, property.PropertyType), null);
}
}
}
private staticobject GetValue(
IEnumerable<UserControlRenderingPropertyAttribute> metadata,
HttpContext context)
{
foreach (var attin metadata)
{
var collection = (att.Source ==UserControlRenderingPropertySource.QueryString) ?
context.Request.QueryString : context.Request.Params;
object value = collection[att.Key];
if (value != null) return value;
}
return null;
}
至此,UserControlRenderingHandler完成了。不过在真正使用时,还需要进行一些配置。例如,您需要在IIS的ISAPI Mapping中将“.ucr”与aspnet_isapi.dll进行映射,并且在web.config中将*.ucr与UserControlRenderingHandler关联起来。当然,对User Control的属性进行标记是必须的。例如还是《技巧:使用User Control做HTML生成》一文中的例子:
public partial class Comments : System.Web.UI.UserControl{ protected override void OnPreRender(EventArgs e) { // ... } [UserControlRenderingProperty(Key = "page", Source = UserControlRenderingPropertySource.QueryString)] public int PageIndex { get; set; } [DefaultValue(10)] public int PageSize { get; set; } // ...}
然后,在客户端代码中只要根据路径发起请求即可,UserControlRenderingHandler会在服务器端完成余下的工作。
<script type="text/javascript" language="javascript"> function getComments(pageIndex) { new Ajax.Updater( "comments", "/Controls/Comments.ucr?page=" + pageIndex + "&t=" + new Date(), { method: "get" }); return false; // IE only }</script>
不过,这就够了吗?对于一个例子来说,这已经足够了,不过要在产品环境中使用很可能还略显不够。例如,如果只让用户访问到特定的User Control,或者只有特定权限的用户才能访问,又该如何对UserControlRenderingHandler进行改造呢?相信您了解上述做法之后,这点要求对您一定不成问题。
原载地址:赵劼:http://blog.zhaojie.me/2008/07/user-control-rendering.html
- 【转载】直接通过User Control生成HTML
- 方案改进:直接通过User Control生成HTML——转自 博客园 老赵
- 直接通过User Control生成HTML-asp.net页面的换皮肤方案
- 技巧:使用User Control做HTML生成
- 技巧:使用User Control做HTML生成
- 使用User Control做HTML生成
- 使用User Control做HTML生成 ——转自 博客园 老赵
- 技巧:使用User Control做HTML生成(bs分销项目用到)
- 通过axure做原型设计生成的html代码可以让开发直接用吗
- 通过axure做原型设计生成的html代码可以让开发直接用吗
- 转载:通过浏览器直接打开Android应用程序
- 通过HTML直接执行DOS命令
- 通过文件结构直接生成xls文件
- 通过文件结构直接生成xls文件
- 通过文件结构直接生成xls文件
- 用户控件(User Control)
- Asp.net动态生成html页面[转载]
- java中生成静态html(转载)
- 2011年4月26日
- Linux安装Oracle报Checking operating system version must be redhat-3, SuSE-9, redhat-4, UnitedLin
- isapi_1
- 想自己学会做phpcms模板的进来看一下!
- isapi_2
- 【转载】直接通过User Control生成HTML
- isapi_3
- 进程调度算法
- isapi_4
- MyEclipse终极优化四要点
- 经典JS
- 关于spring与hibernate整合,导入包的冲突
- JS应用
- 2010年4月26日