ASP.NET Ajax using a custom ViewManager to render paged data without updatepanels

Sometimes you do not want the overhead of using an updatepanel, as each time you make a request it is posting more back than you might need, sure it can be powerful and easy to use but not always the most effective way Say you would like to be able to not use viewstate or postbacks but would like to get a result set of data that can be paged and you would like this totally client driven and to use the least ammount of overhead.

Following on from Scott Guthries ViewManager (thanks Scott), and some ideas I have had lately to do alot more things client side and maybe stop using updatepanels. I have made a more generic ViewManager which I blogged about earlier, basically is just allows you to set properties of your control alot easier and can set any properties the control may have before you go ahead and render it. Don't flame for writing something already done, I find my version of the ViewManager easier to use, so thought I would share.


Data Control:

You will need to create a usercontrol that will be used to render the pages of data, on mine I gave it a few properties so that you can set the current page and page size, I am just rendering my data into a listview, my data comes from a database and I am using LINQ to SQL to access it:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Data.ascx.cs" Inherits="NoUpdatePanels.Data"  EnableViewState="false" %>
<asp:ListView ID="lvData" runat="server" ItemPlaceholderID="plcItem">           
    <LayoutTemplate>
        <br /><br />
        <asp:PlaceHolder ID="plcItem" runat="server"></asp:PlaceHolder>
    </LayoutTemplate>
    <ItemTemplate>
        Name: <%# Eval("Name") %>
        <hr />
    </ItemTemplate>
</asp:ListView>

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

namespace NoUpdatePanels {
    public partial class Data : System.Web.UI.UserControl {

        #region Properties

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

        #endregion

        #region Constructors/Load

        // Constructor, just init properties
        public Data() {
            this.CurrentPage = 0;
            this.PageSize = 5;
        }

        protected void Page_Load(object sender, EventArgs e) {
            TestDataContext db = new TestDataContext();

            var query = (from p in db.Persons
                         select p).Skip(CurrentPage * PageSize).Take(PageSize);

            lvData.DataSource = query;
            lvData.DataBind();
        }

        #endregion

    }
}


So basically all I am doing is taking into account the current page and page size to get a page of data and bind this to the listview, viewstate has been turned off for this control also.


The Page:

The hosting page is what does all the work to get the data control rendered. All this is done client side and done using web service methods, I did not use a webservice in my case due to pure laziness but used a static method on my page, then I set EnablePageMethods=true on the ScriptManager so this would all work, basically all the page is doing is using this webmethod to get a page of data. The webmethod accepts a paremeter which is the current page, is creates an instance of the ViewManager based on the Data control sets the CurrentPage property and then renders the control, it returns this data.

The page uses javascript to call this method, it places the result in a div. I keep track of the current page in a variable so that we know which page to load as we page through the data. A few things to note here, it will not stop at the end of paging, it doesnt really do any real error checking etc., this is just a quick prototype of an idea of mine. The code for the page html and codebehind are below:


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="NoUpdatePanels._Default" %>

<%@ Register src="Data.ascx" tagname="Data" tagprefix="uc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Client Side Data Loading</title>   
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true"></asp:ScriptManager>

        <a href="http://weblogs.asp.net/controlpanel/blogs/posteditor.aspx?SelectedNavItem=NewPost#" onclick="PreviousPage();">Previous &lt;</a> - <a href="http://weblogs.asp.net/controlpanel/blogs/posteditor.aspx?SelectedNavItem=NewPost#" onclick="NextPage();">Next &gt;</a>
        <div id="contentRegion">       
            Loading...
        </div>

    </form>
    <script type="text/javascript">
        var currentPage = 0;       
       
        function LoadPage(page) {
            PageMethods.GetDataPage(page, function(result) {
                // We loaded our data populate our div.               
                document.getElementById("contentRegion").innerHTML = result;
            },
            function(error) {
                alert(error.get_message());
            });
        }
       
        function PreviousPage() {
            currentPage--;
            LoadPage(currentPage);   
        }
       
        function NextPage() {
            currentPage++;
            LoadPage(currentPage);
        }
       
        // Add our load event handler
        Sys.Application.add_load(LoadPage);
    </script>
</body>
</html>

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Web.Services;

namespace NoUpdatePanels {
    public partial class _Default : System.Web.UI.Page {

        [WebMethod()]
        public static string GetDataPage(int page) {

            // Create an instance of our viewmanager.
            ViewManager<Data> man = new ViewManager<Data>("~/Data.ascx");

            // Set the current page property.
            man.Control.CurrentPage = page;

            // Return the rendered control.
            return man.Render();
        }

    }
}

The ViewManager:

This is the generic viewmanager that does the actual rendering of the control.

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Text;
using System.IO;

namespace NoUpdatePanels {

    /// <summary>
    /// A generic user control rendering helper, basically you initialise the view manager and
    /// call render to render that control, but the benifit of this version is you can access the control
    /// the view manager is rendering and can set custom properties etc.
    /// </summary>
    /// <typeparam name="T">The type of the control you are rendering</typeparam>
    public class ViewManager<T> where T : Control {

        #region Properties

        private T _control = default(T);

        /// <summary>
        /// Gives you access to the control you are rendering allows
        /// you to set custom properties etc.
        /// </summary>
        public T Control {
            get {
                return _control;
            }
        }

        // Used as a placeholder page to render the control on.
        private Page _holder = null;

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor for this view manager, pass in the path for the control
        /// that this view manager is render.
        /// </summary>
        /// <param name="inPath"></param>
        public ViewManager(string path) {
            //Init the holder page
            _holder = new Page();

            // Create an instance of our control
            _control = (T)_holder.LoadControl(path);

            // Add it to our holder page.
            _holder.Controls.Add(_control);
        }

        #endregion

        #region Rendering

        /// <summary>
        /// Renders the current control.
        /// </summary>
        /// <returns></returns>
        public string Render() {
            StringWriter sw = new StringWriter();

            // Execute the page capturing the output in the stringwriter.
            HttpContext.Current.Server.Execute(_holder, sw, false);

            // Return the output.
            return sw.ToString();
        }

        #endregion

    }

}
 

 

Thanks for reading hope you found that useful, all comments appreciated.
Stefan

 

1 Comment

Comments have been disabled for this content.