代码改变世界

【原创】利用User Control与Control生成HTML

2012-08-14 16:40  杨新华  阅读(781)  评论(4编辑  收藏  举报

背景:我们在页面操作时,可能为了用户的体验使用了异步调用数据并且绑定,如果对于一些只需改变变量或是少量数据时,可以在异步服务器返回我们想要的数据,直接绑定到我们的页面,可是对于一些数据量比较大的数据,这些数据又包含Html代码等等时,我们就只能在异步服务器端进行字符串的拼接,然后返回,

例如:我们对于一个带分页功能的Repeater进行分页时,每点击下一页或是上一页时,要异步去请求需要的那一页数据,然后绑定在Repeater上。对于拼接字符串显然是又吃力又不是最优的解决方案。

最优解决方案:利用User Control与Control的某些方法直接生成HTML,然后返回给服务器,这样我们就不用拼接字符串了吧。

第一种方法:利用User Control生成HTML

这个方法是引用了老赵的文章,我只是给展示一下。

第一步,我们定义一个Comment类,内容如下:

 1 using System;
 2 using System.Data;
 3 using System.Configuration;
 4 using System.Web;
 5 using System.Web.Security;
 6 using System.Web.UI;
 7 using System.Web.UI.HtmlControls;
 8 using System.Web.UI.WebControls;
 9 using System.Web.UI.WebControls.WebParts;
10 using System.Collections.Generic;
11 namespace UserControlToHtml
12 {
13     public partial class Comment
14     {
15         public DateTime CreateTime { get; set; }
16 
17         public string Content { get; set; }
18     }
19 
20     public partial class Comment
21     {
22         private static List<Comment> s_comments = new List<Comment>
23         {
24             new Comment
25             {
26                 CreateTime = DateTime.Parse("2007-1-1"),
27                 Content = "今天天气不错1"
28             },
29             new Comment
30             {
31                 CreateTime = DateTime.Parse("2007-1-2"),
32                 Content = "挺风和日丽的2"
33             },
34             new Comment
35             {
36                 CreateTime = DateTime.Parse("2007-1-3"),
37                 Content = "我们下午没有课3"
38             },
39             new Comment
40             {
41                 CreateTime = DateTime.Parse("2007-1-10"),
42                 Content = "这的确挺爽的4"
43             },
44              new Comment
45             {
46                 CreateTime = DateTime.Parse("2007-1-13"),
47                 Content = "我们下午没有课5"
48             },
49             new Comment
50             {
51                 CreateTime = DateTime.Parse("2007-1-10"),
52                 Content = "这的确挺爽的6"
53             }
54         };
55 
56         public static List<Comment> GetComments(int pageSize, int pageIndex, out int totalCount)
57         {
58             totalCount = s_comments.Count;
59 
60             List<Comment> comments = new List<Comment>(pageSize);
61 
62             for (int i = pageSize * (pageIndex - 1); i < pageSize * pageIndex && i < s_comments.Count; i++)
63             {
64                 comments.Add(s_comments[i]);
65             }
66 
67             return comments;
68         }
69     }
70 }

第二步,为了显示一个评论列表,我们可以使用一个用户控件(ItemComments.ascx)来封装。自然,分页也是必不可少的:

 1 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ItemComments.ascx.cs" Inherits="UserControlToHtml.ItemComments" %>
 2 
 3 
 4 <script src="jquery-1.5.1.min.js" type="text/javascript"></script>
 5 <script src="prototype.js" type="text/javascript"></script>
 6 
 7 <script type="text/javascript" language="javascript">
 8     function getComments(pageIndex) {
 9         new Ajax.Updater(
10             "comments",
11             "/GetComments.ashx?page=" + pageIndex + "&t=" + new Date(),
12             { method: "get" });        
13         return false; // IE only
14     }
15 </script>
16 
17 <asp:Repeater runat="server" ID="rptComments">
18     <ItemTemplate>
19         时间:<%# (Container.DataItem as UserControlToHtml.Comment).CreateTime.ToString()%><br />
20         内容:<%# (Container.DataItem as UserControlToHtml.Comment).Content%> 
21     </ItemTemplate>
22     <SeparatorTemplate>
23         <hr />
24     </SeparatorTemplate>
25     <FooterTemplate>
26         <hr />
27     </FooterTemplate>
28 </asp:Repeater>
29  
30 <% if (this.PageIndex > 1)
31    { %>
32         <a href="#" title="上一页" onclick="return getComments(<%= this.PageIndex - 1 %>);">上一页</a>&nbsp;
33 <% } %>
34 
35 <% if (this.PageIndex * this.PageSize < this.TotalCount)
36    { %>
37         <a href="#" title="上一页" onclick="return getComments(<%= this.PageIndex +1 %>);">下一页</a>
38 <% } %>

后台方法:

 protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            this.rptComments.DataSource = Comment.GetComments(this.PageSize,
                this.PageIndex, out this.m_totalCount);
            this.DataBind();

        }

        public int PageIndex { get; set; }
        public int PageSize { get; set; }
       

        private int m_totalCount;
        public int TotalCount
        {
            get
            {
                return this.m_totalCount;
            }
        }

 

第三步:我们在第二步中,要进行异步去请求服务器的数据,那个服务器页面是GetComments.ashx,那么我们就建立一个一般处理程序GetComments.ashx,代码如下:

  public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";

            ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
            ItemComments control = viewManager.LoadViewControl("~/ItemComments.ascx");

            control.PageIndex = Int32.Parse(context.Request.QueryString["page"]);
            control.PageSize = 3;

            
            context.Response.Write(viewManager.RenderView(control));
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

这几行代码调用了关键代码,我们建立ViewManager类,类的内容如下:

 public class ViewManager<T> where T : UserControl
    {
        private Page m_pageHolder;

        //创建一个Control实例并返回
        public T LoadViewControl(string path)
        {
            this.m_pageHolder = new Page();
            return (T)this.m_pageHolder.LoadControl(path);
        }
        //就是生成HTML
        public string RenderView(T control)
        {
            StringWriter output = new StringWriter();

            this.m_pageHolder.Controls.Add(control);
            HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);

            
            return output.ToString();
        }
    }

上面有两个方法得关注一下:那就是:LoadViewControl和RenderView,LoadViewControl方法的作用是创建一个Control实例并返回,RenderView方法的作用则就是生成HTML了。

使用UserControl进行HTML代码生成是一个十分常用的技巧。尤其在AJAX应用越来越普及的情况下,合理使用上面提到的方式可以方便的为我们的应用添加AJAX效果。而且很多情况下,我们即使不需要在页面上显示内容,也可以将内容使用UserControl进行编辑。因为编写UserControl比拼接字符串的方式无论是在开发效率上还是可维护性上都高出许多。由于这个方式其实使用了WebForms这个久经考验的模型,因此在执行效率方面也是相当高的。此外,就刚才的例子来说,使用UserCotrol进行HTML生成还有其他好处:

  1. 页面呈现逻辑只实现了一次,提高了可维护性。
  2. 不会影响页面的SEO,因为在客户端<a />的href还是有效的。

事实上,WebForms是一个非常强大的模型,所以ASP.NET MVC的View也使用了WebForms的引擎。通过上面这个例子,我们其实还可以做到其他很多东西——例如用UserControl来生成XML数据,因为UserControl本身不会带来任何额外的内容。

 

第二种方法:利用Control生成HTML

这个方法是利用ajax来实现的。我们用上面的实例再利用Control生成HTML的方法再实现一次,废话也不多说了,直接上代码

第一步:建立WebForm页面 ViewItem.aspx,代码如下:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>RenderControlToHtml</title>    

    <script src="jquery-1.5.1.min.js" type="text/javascript"></script>
    <script type="text/javascript" language="javascript">
       
        function getComments(pageIndex) {


            $.ajax({
                type: 'POST',
                url: "ViewItem.aspx",
                data: 'page=' + pageIndex,
                error: function() {
                    alert('发生错误');
                },
                success: function(msg) {
                    var total = "<%=TotalCount %>";
                    var pageSize = "<%=PageSize %>";

                    var pageContent = "";
                    if (pageIndex > 1) {
                        pageContent = " <a href=\"#\" title=\"上一页\" onclick=\"return getComments(" + (pageIndex - 1) + ");\">上一页</a>&nbsp;";
                    }
                    if (pageIndex * pageSize < total) {
                        pageContent = pageContent + " <a href=\"#\" title=\"下一页\" onclick=\"return getComments(" + (pageIndex + 1) + ");\">下一页</a>&nbsp;";
                    }

                    $("#comments").html(msg + pageContent);
                }
            });

            
            return false; // IE only
        }
     
    </script>
    
</head>
<body>
    <form id="form1" runat="server">
        <div id="comments">
           
            <asp:Repeater runat="server" ID="rptComments">
                <ItemTemplate>
                    时间:<%# (Container.DataItem as RenderControlToHtml.Comment).CreateTime.ToString()%><br />
                    内容:<%# (Container.DataItem as RenderControlToHtml.Comment).Content%> 
                </ItemTemplate>
                <SeparatorTemplate>
                    <hr />
                </SeparatorTemplate>
                <FooterTemplate>
                    <hr />
                </FooterTemplate>
            </asp:Repeater>
               <a href="#" title="上一页" onclick="return getComments(2);">下一页</a>
        </div>
     
         
    </form>
</body>
</html>

第二步:后台代码:

 protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                PageSize = 2;

                if (!string.IsNullOrEmpty(Request.Form["page"]))
                {
                    this.rptComments.DataSource = Comment.GetComments(this.PageSize, int.Parse(Request.Form["page"]), out this.m_totalCount);
                    this.DataBind();

                    Response.Write(RenderHtml(rptComments));
                    Response.End();
                }
                else
                {
                    this.rptComments.DataSource = Comment.GetComments(this.PageSize, 1, out this.m_totalCount);
                    this.DataBind();
                    
                }
            }
            
        }

        protected int PageIndex
        {
            get
            {
                int result = 0;
                Int32.TryParse(this.Request.Form["page"], out result);

                return result > 0 ? result : 1;
            }
        }
        public int PageSize { get; set; }
        private int m_totalCount;
        public int TotalCount
        {
            get
            {
                return this.m_totalCount;
            }
        }
        /// <summary>
        /// 把一个控件的绑定数据生成一个html代码
        /// </summary>
        /// <param name="Ctrl"></param>
        /// <returns></returns>
        private string RenderHtml(Control Ctrl)
        {
            StringBuilder sb = new StringBuilder();

            StringWriter sw = new StringWriter(sb);
            HtmlTextWriter htw = new HtmlTextWriter(sw);

            Ctrl.RenderControl(htw);

            sw.Close();
            htw.Close();


            return sb.ToString();
        }
    }
}

第三步:需要的Comment.cs类

  public partial class Comment
    {
        public DateTime CreateTime { get; set; }

        public string Content { get; set; }
    }

    public partial class Comment
    {
        private static List<Comment> s_comments = new List<Comment>
        {
            new Comment
            {
                CreateTime = DateTime.Parse("2007-1-1"),
                Content = "今天天气不错1"
            },
            new Comment
            {
                CreateTime = DateTime.Parse("2007-1-2"),
                Content = "挺风和日丽的2"
            },
            new Comment
            {
                CreateTime = DateTime.Parse("2007-1-3"),
                Content = "我们下午没有课3"
            },
            new Comment
            {
                CreateTime = DateTime.Parse("2007-1-10"),
                Content = "这的确挺爽的4"
            },
             new Comment
            {
                CreateTime = DateTime.Parse("2007-1-13"),
                Content = "我们下午没有课5"
            },
            new Comment
            {
                CreateTime = DateTime.Parse("2007-1-10"),
                Content = "这的确挺爽的6"
            }
        };

        public static List<Comment> GetComments(int pageSize, int pageIndex, out int totalCount)
        {
            totalCount = s_comments.Count;

            List<Comment> comments = new List<Comment>(pageSize);

            for (int i = pageSize * (pageIndex - 1); i < pageSize * pageIndex && i < s_comments.Count; i++)
            {
                comments.Add(s_comments[i]);
            }

            return comments;
        }

 

 这个方法最主要的是第二步中的RenderControl方法,它的作用是将服务器控件的内容输出到所提供的 System.Web.UI.HtmlTextWriter 对象中; 

好了,两种方法说完了,根据自己的实际情况来选择使用哪个实现方式吧。

 

欢迎转载,原创地址: http://www.cnblogs.com/yxhblog/archive/2012/08/14/2638118.html