none
Gridview paginação RRS feed

  • Pergunta

  • estou desenvolvendo em c#  asp.net

    qual a melhor maneira para pagina um gridview por código

    para que não sobrecarregue o servidor

    quarta-feira, 9 de abril de 2014 17:23

Respostas

  • jceoms,

    Eu particularmente acho que utilizar uma interface de paginação para servir de intermediário entre o GridView e a base de dados uma abordagem bem interessante. Podemos fazer uso da interface IQueryable<T> que o LINQ to Entities utiliza para retornar os dados de suas consultas para permitir que a paginação ocorra de forma Lazy ou seja, sob demanda. Cada vez que uma página é solicitada, é realizada uma consulta na base de dados que traz somente os registros que devem ser exibidos na página. Para tanto, utilizamos os extension methods Skip e Take de IQueyable<T> para fazer essa "mágica".

    Eu vou exemplificar essa ideia com um pouco de código:

    Vamos considerar a seguinte classe de modelo/entidade:

        public class Usuario
        {
            public string CPF { get; set; }
            public string Nome { get; set; }
            public string Nacionalidade { get; set; }
            public string GrauEscolaridade { get; set; }
        }

    Para simular os acessos à base de dados irei criar uma classe de repositório conforme o código a seguir:

        public class Repository
        {
    
            public IPagedList<Usuario> GetPagedUsers<TKey>(int pageIndex, int pageSize, Expression<Func<Usuario, TKey>> orderBy)
            {
                return new PagedList<Usuario>(Usuarios.OrderBy(orderBy), pageIndex, pageSize);
            }
    
            public IQueryable<Usuario> Usuarios
            {
                get
                {
                    IList<Usuario> usuarios = new List<Usuario>();
                    usuarios.Add(new Usuario() { CPF = "000.000.000-00", Nome = "João", GrauEscolaridade = "Superior Completo", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "111.111.111-11", Nome = "Henandez", GrauEscolaridade = "Fundamental", Nacionalidade = "Mexico" });
                    usuarios.Add(new Usuario() { CPF = "222.222.222-22", Nome = "Cibele", GrauEscolaridade = "Doutorado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "333.333.333-33", Nome = "Constanza", GrauEscolaridade = "Superior Incompleto", Nacionalidade = "Chile" });
                    usuarios.Add(new Usuario() { CPF = "444.444.444-44", Nome = "Jussara", GrauEscolaridade = "Mestrado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "555.555.555-55", Nome = "Marcos", GrauEscolaridade = "Fundamental", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "666.666.666-66", Nome = "Benedito", GrauEscolaridade = "Superior Completo", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "777.777.777-77", Nome = "Renato", GrauEscolaridade = "Mestrado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "888.888.888-88", Nome = "Florencia", GrauEscolaridade = "Segundo Grau", Nacionalidade = "Argentina" });
                    usuarios.Add(new Usuario() { CPF = "999.999.999-99", Nome = "José", GrauEscolaridade = "Fundamental", Nacionalidade = "Brasil" });
                    return usuarios.AsQueryable();
                }
            }
        }


    Observe que essa classe expõe o método GetPagedUsers que retornar um lista do tipo IPagedList<Usuario>.

    Vamos criar um interface genérica chamada IPagedList<T> que irá possuir as informações necessárias para a paginação:

        public interface IPagedList<T>
        {
            int PageIndex { get; }
            int PageSize { get; }
            int TotalCount { get; }
            int TotalPages { get; }
            bool HasPreviousPage { get; }
            bool HasNextPage { get; }
        }

    Vamos então criar uma classe chamada PagedList que represente uma lista paginada. Para tanto iremos fazer com que essa classe implemente IPagedList<T> e herde de List<T>:

        public class PagedList<T> : List<T>, IPagedList<T>
        {
            public PagedList(IQueryable<T> source, int pageIndex, int pageSize)
            {
                int total = source.Count();
                this.TotalCount = total;
                this.TotalPages = total / pageSize;
    
                if (total % pageSize > 0)
                    TotalPages++;
    
                this.PageSize = pageSize;
                this.PageIndex = pageIndex;
                this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
            }
    
            public PagedList(List<T> source, int pageIndex, int pageSize)
            {
                int total = source.Count();
                this.TotalCount = total;
                this.TotalPages = total / pageSize;
    
                if (total % pageSize > 0)
                    TotalPages++;
    
                this.PageSize = pageSize;
                this.PageIndex = pageIndex;
                this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
            }
    
            public int PageIndex { get; private set; }
            public int PageSize { get; private set; }
            public int TotalCount { get; private set; }
            public int TotalPages { get; private set; }
    
            public bool HasPreviousPage
            {
                get { return (PageIndex > 0); }
            }
            public bool HasNextPage
            {
                get { return (PageIndex + 1 < TotalPages); }
            }
    
        }


    Vamos então criar um novo componente GridView chamado PagedGridView. Este componente herda de GridView e permite que uma paginação customizada seja utilizada para o componente, mantendo informações como VirtualItemCount e CurrentPageIndex no ViewState.  

       public class PagedGridView : GridView
        {
            private const string _virtualItemCount = "virtualItemCount";
            private const string _currentPageIndex = "currentPageIndex";
    
            [Browsable(true), Category("Custom")]
            [Description("Set the virtual item count for this grid")]
            public override int VirtualItemCount
            {
                get
                {
                    if (ViewState[_virtualItemCount] == null)
                        ViewState[_virtualItemCount] = -1;
                    return Convert.ToInt32(ViewState[_virtualItemCount]);
                }
                set
                {
                    ViewState[_virtualItemCount] = value;
                }
            }
    
            private int CurrentPageIndex
            {
                get
                {
                    if (ViewState[_currentPageIndex] == null)
                        ViewState[_currentPageIndex] = 0;
                    return Convert.ToInt32(ViewState[_currentPageIndex]);
                }
                set
                {
                    ViewState[_currentPageIndex] = value;
                }
            }
    
            public override object DataSource
            {
                get
                {
                    return base.DataSource;
                }
                set
                {
                    base.DataSource = value;
                    this.CurrentPageIndex = this.PageIndex;
                }
            }
    
            protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
            {
                if (CustomPaging)
                {
                    pagedDataSource.AllowCustomPaging = true;
                    pagedDataSource.VirtualCount = this.VirtualItemCount;
                    pagedDataSource.CurrentPageIndex = this.CurrentPageIndex;
                }
                base.InitializePager(row, columnSpan, pagedDataSource);
            }
    
            public bool CustomPaging
            {
                get { return (this.VirtualItemCount != -1); }
            }
        }


    Iremos então criar a nossa página ASPX referenciando esse novo componente:

    <%@ Page Title="GridView Paging" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridViewPaging._Default" %>
    <%@ Register Assembly="GridViewPaging" Namespace="GridViewPaging.Controls" TagPrefix="cc" %>
    
    <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
        <cc:PagedGridView ID="myGridView" OnPageIndexChanging="myGridView_PageIndexChanging" PageSize="3" AllowPaging="true" runat="server">
        </cc:PagedGridView>
    </asp:Content>

    Observe que eu defini que o GridView irá apresentar apenas 3 itens por página.

    E por fim vamos criar o Code-Behind da página que irá manipular o evento de paginação e realizar o Data Binding:

        public partial class _Default : Page
        {
    
            Repository db = new Repository();
    
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!IsPostBack) {
                    BindGrid(0, myGridView.PageSize);
                }
            }
    
    
            protected void myGridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
            {
                myGridView.PageIndex = e.NewPageIndex;
                BindGrid(e.NewPageIndex, myGridView.PageSize);
            }
    
            protected void BindGrid(int pageIndex, int pageSize)
            {
                var usuarios = db.GetPagedUsers<string>(pageIndex, pageSize, u => u.Nome);
                myGridView.VirtualItemCount = usuarios.TotalCount;
                myGridView.DataSource = usuarios;
                myGridView.DataBind();
            }
        }

    Observe que a lista de usuário é obtida ordenada pelo nome do usuário. Isso é feito dentro do método BindGrid esta chamada:

    var usuarios = db.GetPagedUsers<string>(pageIndex, pageSize, u => u.Nome);


    E pronto, basta rodar para que seu grid esteja devidamente paginado e realizando as requisições para a base de dados sob demanda, tornando esse menismo leve e eficiente.

    Ao rodar a aplicação a interface terá a seguinte aparência:


    O código-fonte do exemplo que utilizei nesta demonstração pode ser obtido aqui.

    Espero que você possa utilizar esta técnica em seus projetos.

    Se isso te foi útil, por favor não se esqueça de marcar como Resposta.


    Renato Person | MCPD


    • Sugerido como Resposta Renato Person quarta-feira, 9 de abril de 2014 20:19
    • Editado Renato Person quarta-feira, 9 de abril de 2014 20:21 link do download não funciona
    • Marcado como Resposta Giovani Cr quinta-feira, 10 de abril de 2014 18:58
    quarta-feira, 9 de abril de 2014 20:19

Todas as Respostas

  • jceoms,

    Eu particularmente acho que utilizar uma interface de paginação para servir de intermediário entre o GridView e a base de dados uma abordagem bem interessante. Podemos fazer uso da interface IQueryable<T> que o LINQ to Entities utiliza para retornar os dados de suas consultas para permitir que a paginação ocorra de forma Lazy ou seja, sob demanda. Cada vez que uma página é solicitada, é realizada uma consulta na base de dados que traz somente os registros que devem ser exibidos na página. Para tanto, utilizamos os extension methods Skip e Take de IQueyable<T> para fazer essa "mágica".

    Eu vou exemplificar essa ideia com um pouco de código:

    Vamos considerar a seguinte classe de modelo/entidade:

        public class Usuario
        {
            public string CPF { get; set; }
            public string Nome { get; set; }
            public string Nacionalidade { get; set; }
            public string GrauEscolaridade { get; set; }
        }

    Para simular os acessos à base de dados irei criar uma classe de repositório conforme o código a seguir:

        public class Repository
        {
    
            public IPagedList<Usuario> GetPagedUsers<TKey>(int pageIndex, int pageSize, Expression<Func<Usuario, TKey>> orderBy)
            {
                return new PagedList<Usuario>(Usuarios.OrderBy(orderBy), pageIndex, pageSize);
            }
    
            public IQueryable<Usuario> Usuarios
            {
                get
                {
                    IList<Usuario> usuarios = new List<Usuario>();
                    usuarios.Add(new Usuario() { CPF = "000.000.000-00", Nome = "João", GrauEscolaridade = "Superior Completo", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "111.111.111-11", Nome = "Henandez", GrauEscolaridade = "Fundamental", Nacionalidade = "Mexico" });
                    usuarios.Add(new Usuario() { CPF = "222.222.222-22", Nome = "Cibele", GrauEscolaridade = "Doutorado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "333.333.333-33", Nome = "Constanza", GrauEscolaridade = "Superior Incompleto", Nacionalidade = "Chile" });
                    usuarios.Add(new Usuario() { CPF = "444.444.444-44", Nome = "Jussara", GrauEscolaridade = "Mestrado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "555.555.555-55", Nome = "Marcos", GrauEscolaridade = "Fundamental", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "666.666.666-66", Nome = "Benedito", GrauEscolaridade = "Superior Completo", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "777.777.777-77", Nome = "Renato", GrauEscolaridade = "Mestrado", Nacionalidade = "Brasil" });
                    usuarios.Add(new Usuario() { CPF = "888.888.888-88", Nome = "Florencia", GrauEscolaridade = "Segundo Grau", Nacionalidade = "Argentina" });
                    usuarios.Add(new Usuario() { CPF = "999.999.999-99", Nome = "José", GrauEscolaridade = "Fundamental", Nacionalidade = "Brasil" });
                    return usuarios.AsQueryable();
                }
            }
        }


    Observe que essa classe expõe o método GetPagedUsers que retornar um lista do tipo IPagedList<Usuario>.

    Vamos criar um interface genérica chamada IPagedList<T> que irá possuir as informações necessárias para a paginação:

        public interface IPagedList<T>
        {
            int PageIndex { get; }
            int PageSize { get; }
            int TotalCount { get; }
            int TotalPages { get; }
            bool HasPreviousPage { get; }
            bool HasNextPage { get; }
        }

    Vamos então criar uma classe chamada PagedList que represente uma lista paginada. Para tanto iremos fazer com que essa classe implemente IPagedList<T> e herde de List<T>:

        public class PagedList<T> : List<T>, IPagedList<T>
        {
            public PagedList(IQueryable<T> source, int pageIndex, int pageSize)
            {
                int total = source.Count();
                this.TotalCount = total;
                this.TotalPages = total / pageSize;
    
                if (total % pageSize > 0)
                    TotalPages++;
    
                this.PageSize = pageSize;
                this.PageIndex = pageIndex;
                this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
            }
    
            public PagedList(List<T> source, int pageIndex, int pageSize)
            {
                int total = source.Count();
                this.TotalCount = total;
                this.TotalPages = total / pageSize;
    
                if (total % pageSize > 0)
                    TotalPages++;
    
                this.PageSize = pageSize;
                this.PageIndex = pageIndex;
                this.AddRange(source.Skip(pageIndex * pageSize).Take(pageSize).ToList());
            }
    
            public int PageIndex { get; private set; }
            public int PageSize { get; private set; }
            public int TotalCount { get; private set; }
            public int TotalPages { get; private set; }
    
            public bool HasPreviousPage
            {
                get { return (PageIndex > 0); }
            }
            public bool HasNextPage
            {
                get { return (PageIndex + 1 < TotalPages); }
            }
    
        }


    Vamos então criar um novo componente GridView chamado PagedGridView. Este componente herda de GridView e permite que uma paginação customizada seja utilizada para o componente, mantendo informações como VirtualItemCount e CurrentPageIndex no ViewState.  

       public class PagedGridView : GridView
        {
            private const string _virtualItemCount = "virtualItemCount";
            private const string _currentPageIndex = "currentPageIndex";
    
            [Browsable(true), Category("Custom")]
            [Description("Set the virtual item count for this grid")]
            public override int VirtualItemCount
            {
                get
                {
                    if (ViewState[_virtualItemCount] == null)
                        ViewState[_virtualItemCount] = -1;
                    return Convert.ToInt32(ViewState[_virtualItemCount]);
                }
                set
                {
                    ViewState[_virtualItemCount] = value;
                }
            }
    
            private int CurrentPageIndex
            {
                get
                {
                    if (ViewState[_currentPageIndex] == null)
                        ViewState[_currentPageIndex] = 0;
                    return Convert.ToInt32(ViewState[_currentPageIndex]);
                }
                set
                {
                    ViewState[_currentPageIndex] = value;
                }
            }
    
            public override object DataSource
            {
                get
                {
                    return base.DataSource;
                }
                set
                {
                    base.DataSource = value;
                    this.CurrentPageIndex = this.PageIndex;
                }
            }
    
            protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
            {
                if (CustomPaging)
                {
                    pagedDataSource.AllowCustomPaging = true;
                    pagedDataSource.VirtualCount = this.VirtualItemCount;
                    pagedDataSource.CurrentPageIndex = this.CurrentPageIndex;
                }
                base.InitializePager(row, columnSpan, pagedDataSource);
            }
    
            public bool CustomPaging
            {
                get { return (this.VirtualItemCount != -1); }
            }
        }


    Iremos então criar a nossa página ASPX referenciando esse novo componente:

    <%@ Page Title="GridView Paging" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridViewPaging._Default" %>
    <%@ Register Assembly="GridViewPaging" Namespace="GridViewPaging.Controls" TagPrefix="cc" %>
    
    <asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
        <cc:PagedGridView ID="myGridView" OnPageIndexChanging="myGridView_PageIndexChanging" PageSize="3" AllowPaging="true" runat="server">
        </cc:PagedGridView>
    </asp:Content>

    Observe que eu defini que o GridView irá apresentar apenas 3 itens por página.

    E por fim vamos criar o Code-Behind da página que irá manipular o evento de paginação e realizar o Data Binding:

        public partial class _Default : Page
        {
    
            Repository db = new Repository();
    
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!IsPostBack) {
                    BindGrid(0, myGridView.PageSize);
                }
            }
    
    
            protected void myGridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
            {
                myGridView.PageIndex = e.NewPageIndex;
                BindGrid(e.NewPageIndex, myGridView.PageSize);
            }
    
            protected void BindGrid(int pageIndex, int pageSize)
            {
                var usuarios = db.GetPagedUsers<string>(pageIndex, pageSize, u => u.Nome);
                myGridView.VirtualItemCount = usuarios.TotalCount;
                myGridView.DataSource = usuarios;
                myGridView.DataBind();
            }
        }

    Observe que a lista de usuário é obtida ordenada pelo nome do usuário. Isso é feito dentro do método BindGrid esta chamada:

    var usuarios = db.GetPagedUsers<string>(pageIndex, pageSize, u => u.Nome);


    E pronto, basta rodar para que seu grid esteja devidamente paginado e realizando as requisições para a base de dados sob demanda, tornando esse menismo leve e eficiente.

    Ao rodar a aplicação a interface terá a seguinte aparência:


    O código-fonte do exemplo que utilizei nesta demonstração pode ser obtido aqui.

    Espero que você possa utilizar esta técnica em seus projetos.

    Se isso te foi útil, por favor não se esqueça de marcar como Resposta.


    Renato Person | MCPD


    • Sugerido como Resposta Renato Person quarta-feira, 9 de abril de 2014 20:19
    • Editado Renato Person quarta-feira, 9 de abril de 2014 20:21 link do download não funciona
    • Marcado como Resposta Giovani Cr quinta-feira, 10 de abril de 2014 18:58
    quarta-feira, 9 de abril de 2014 20:19