Explorando o GridView

Por Israel Aece

Neste artigo mostrarei o novo controle do ASP.NET 2.0, chamado GridView. Esta é uma versão muito melhorada do DataGrid das versões 1.x do ASP.NET, contendo uma série de vantagens, principalmente no que diz respeito à agilidade no desenvolvimento, ganhando assim uma enorme produtividade.

As melhorias vão desde as formas de carregarmos o GridView até funcionalidades inexistentes nas versões anteriores, as quais necessitávamos escrever muito código para atender algo mais específico. Grandes melhorias foram criadas, como por exemplo: novos tipos de colunas, ordenação e paginação de registros sem a escrita de nenhuma linha de código, entre outras.

Veremos no decorrer deste artigo essas novas funcionalidades, que estão cada uma delas, separadas por assunto, relacionadas abaixo:

Nesta página

Objetos de DataSource
Tipos de Colunas
Habilitando a Ordernação de Registros
Habilitando a Paginação de Registros
Formatando o GridView
Sumarizando Valores
Exibindo Imagens
Exibindo Detalhes de um Registro
Editando Registros
Excluindo Registros
Utilizando Template Columns
Outras Funcionalidades

Objetos de DataSource

É muito comum trabalharmos com acesso a dados em qualquer tipo de aplicação, seja Windows, Web, Web Service, etc., e com isso, escrevemos diversos tipos de códigos para diversas operações, como por exemplo:

  • paginação

  • habilitar a ordenação

  • exibição de registro detalhado

  • edição

  • exclusão

Para que conseguíssemos desenvolver estas funcionalidades na versão anterior do ASP.NET 1.x, tínhamos que escrever bastante linhas de códigos. Nada muito exagerado, porém de certa forma por muitas vezes o código era redundante.

Como estas tarefas são bastante utilizadas em nosso dia-a-dia, a Microsoft criou controles do tipo Data Bound para não precisarmos ir diretamente ao código para criar tais funcionalidades. Esses controles são: SqlDataSource, ObjectDataSource e o XmlDataSource.

Esses controles são totalmente compatíveis com o controle que é tema deste artigo, o GridView, permitindo com esta integração, uma grande facilidade em criar aplicações que acessam e manipulam os dados de forma bem fácil e intuitiva (através das Smart Tags), permitindo assim atingir também desenvolvedores iniciantes que ainda não estão à vontade trabalhando diretamente com código.

Os controls Data Bound encapsulam a maioria das operações, que antes precisavam ser desenvolvidas para diferentes repositórios de dados. Isso tira a preocupação de definir e/ou gerir a conexão com a base de dados, a query e seus respectivos parâmetros que serão utilizados. Através de propriedades podemos especificar seus devidos valores em design-time.

Abaixo vemos a finalidade de cada um desses controles:

  • SqlDataSource: Fornece acesso à uma base de dados SQL Server.

  • ObjectDataSource: Fornece acesso à um determinado método de uma alguma classe. Estas classes geralmente encapsulam o acesso à base de dados, ou qualquer repositório, retornando uma coleção de objetos que deve ser apresentada ao usuário.

  • XmlDataSource: Fornece acesso e manipulação à um arquivo XML.

Abaixo abordaremos cada um desses controles detalhadamente.

SqlDataSource

Como já vimos, este controle é responsável por encapsular a manipulação dos dados de uma base de dados SQL Server. Temos nele propriedades essencias, como por exemplo, os comandos SelectCommand, InsertCommand, DeleteCommand e UpdateCommand, que é onde definimos os comandos (queries ou Stored Procedures) que serão executadas para cada ação.

Podemos definir estas propriedades via design (IDE), através da janelas de propriedades do Visual Studio .NET 2005 ou declarativamente no código HTML da página ASPX. Utilizando a IDE do Visual Studio .NET 2005 existem as chamadas Smart Tags, que são uma espécie de "Wizard", onde podemos configurar passo a passo cada uma dessas propriedades, habilitando inclusive uma opção para manipular as colunas que queremos exibir ao usuário.

Depois de arrastar um SqlDataSource para o nosso WebForm, haverá um botão no canto superior direito deste que, ao clicar, um wizard é exibido para fazermos as configurações. Abaixo a Figura 1 ilustra este processo:

Cc518055.ExplorandoGridView01(pt-br,MSDN.10).png
Figura 1 - Utilizando as Smart Tags para configuração.

Ao clicar em Configure Data Source..., uma nova janela será aberta informando as configurações à serem efetuadas para o funcionamento do objeto SqlDataSource. Isso inclui desde a escolha do banco de dados, passando pela query (ou Stored Procedure) que será executada. Há uma opção chamada Advanced que, geramos automaticamente os comandos de Insert, Delete e Update (semelhante ao que já fazemos atualmente com o Dataset e DataAdapter). As imagens abaixo ilustram este processo passo à passo:

Cc518055.ExplorandoGridView02(pt-br,MSDN.10).png
Figura 2 - Definindo o banco de dados a ser utilizado.

Cc518055.ExplorandoGridView03(pt-br,MSDN.10).png
Figura 3 - Gerando as queries ou Stored Procedures para as operações de CRUD.

Cc518055.ExplorandoGridView04(pt-br,MSDN.10).png
Figura 4 - Esta opção é habilitada quando clicamos no botão Advanced... da figura anterior, onde definimos se os comandos de CRUD serão ou não gerados automaticamente.

É importante informar que os comandos gerados automaticamente são baseados no comando de seleção. Logo, os campos que são informados na seleção refletem nos comandos de inserção e atualização. Veremos abaixo o código HTML correspondente ao nosso objeto SqlDataSource e o controle GridView, gerado em nosso arquivo ASPX:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT [UsuarioID], [Nome], [Email] FROM [Usuario]" 
    DeleteCommand="DELETE FROM [Usuario] WHERE [UsuarioID] = @original_UsuarioID" 
    InsertCommand="INSERT INTO [Usuario] ([Nome], [Email]) VALUES (@Nome, @Email)" 
    UpdateCommand="UPDATE [Usuario] SET [Nome] = @Nome, [Email] = @Email 
	WHERE [UsuarioID] = @original_UsuarioID">
    <DeleteParameters>
        <asp:Parameter Name="original_UsuarioID" Type="Int32" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="Nome" Type="String" />
        <asp:Parameter Name="Email" Type="String" />
        <asp:Parameter Name="original_UsuarioID" Type="Int32" />
    </UpdateParameters>
    <InsertParameters>
        <asp:Parameter Name="Nome" Type="String" />
        <asp:Parameter Name="Email" Type="String" />
    </InsertParameters>
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1">
</asp:GridView>

Como podemos ver no código HTML correspondente ao objeto SqlDataSource, os comandos são gerados com códigos T-SQL e estão devidamente em suas respectivas propriedades. Para cada comando, há uma seção no código HTML do objeto SqlDataSource que são definidos os parâmetros que serão utilizados pela query (ou Stored Procedure), incluindo o seu nome e tipo de dado.

Depois de completamente configurado o objeto SqlDataSource, o definimos como a fonte de dados para o controle GridView, através da propriedade DataSourceID como vimos no código HTML acima. A imagem abaixo ilustra como definir o DataSource do GridView através da Smart Tag:

Cc518055.ExplorandoGridView05(pt-br,MSDN.10).png
Figura 5 - Selecionando o Data Source do controle GridView.

ObjectDataSource

Este objeto destina-se também a vincular os dados ao controle GridView, mas utilizando uma função de uma classe qualquer que nos retorne uma coleção de objetos. Porém sem a necessidade de fazer diretamente via código. O processo de vinculação é também parecido com o objeto SqlDataSource em relação à forma de vínculo ao GridView; o que difere logicamente é o "Wizard" que é exibido para a configuração do mesmo.

Antes de exibir o wizard vamos montar o código que será responsável por resgatar os dados da base de dados, popular os objetos e, consequentemente, retornar uma coleção do mesmo. Abaixo é exibido a classe "Usuario":

C#

using System;

public class Usuario
{
    private int _id;
    private string _nome;
    private string _email;
    private string _endereco;
    private string _cidade;
    private string _estado;
    private string _foto;

    public int ID
    {
        get { return this._id; }
        set { this._id = value; }
    }

    public string Nome
    {
        get { return this._nome; }
        set { this._nome = value; }
    }

    public string Email
    {
        get { return this._email; }
        set { this._email = value; }
    }

    public string Endereco
    {
        get { return this._endereco; }
        set { this._endereco = value; }
    }

    public string Cidade
    {
        get { return this._cidade; }
        set { this._cidade = value; }
    }

    public string Estado
    {
        get { return this._estado; }
        set { this._estado = value; }
    }

    public string Foto
    {
        get { return this._foto; }
        set { this._foto = value; }
    }
}

VB.NET

Imports Microsoft.VisualBasic

Public Class Usuario

    Private _id As Integer
    Private _nome As String
    Private _email As String
    Private _endereco As String
    Private _cidade As String
    Private _estado As String
    Private _foto As String

    Public Property ID() As Integer
        Get
            Return Me._id
        End Get
        Set(ByVal value As Integer)
            Me._id = value
        End Set
    End Property

    Public Property Nome() As String
        Get
            Return Me._nome
        End Get
        Set(ByVal value As String)
            Me._nome = value
        End Set
    End Property

    Public Property Email() As String
        Get
            Return Me._email
        End Get
        Set(ByVal value As String)
            Me._email = value
        End Set
    End Property

    Public Property Endereco() As String
        Get
            Return Me._endereco
        End Get
        Set(ByVal value As String)
            Me._endereco = value
        End Set
    End Property

    Public Property Cidade() As String
        Get
            Return Me._cidade
        End Get
        Set(ByVal value As String)
            Me._cidade = value
        End Set
    End Property

    Public Property Estado() As String
        Get
            Return Me._estado
        End Get
        Set(ByVal value As String)
            Me._estado = value
        End Set
    End Property

    Public Property Foto() As String
        Get
            Return Me._foto
        End Get
        Set(ByVal value As String)
            Me._foto = value
        End Set
    End Property

End Class

Depois da classe que armazena os valores provenientes do banco de dados ou de qualquer outra repositório, vamos criar a classe que tem a finalidade de ir até o banco de dados, resgatar os dados através de objetos que já são de nosso conhecimento (SqlConnection, SqlCommand, SqlDataReader) e popular objetos do tipo Usuario, que criamos logo acima. O código abaixo é responsável por esse processo:

C#

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Configuration;
using System.Collections.Generic;

public class Usuarios
{
    public List<Usuario> ResgataUsuarios() {
        List<Usuario> coll = new List<Usuario>();
        string connString = 
          WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString
        SqlConnection conn = new SqlConnection(connString);
        SqlCommand cmd = 
          new SqlCommand("SELECT UsuarioID, Nome, Email FROM Usuario", conn);
        SqlDataReader dr = null;
        try
        {
            conn.Open();
            dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            while(dr.Read()){
                Usuario u = new Usuario();
                u.ID = dr.GetInt32(0);
                u.Nome = dr.GetString(1);
                u.Email = dr.GetString(2);
                coll.Add(u);
            }
        }
        finally {
            if (dr != null)
                dr.Close();
        }
        return coll;
    }
}

VB.NET

Imports Microsoft.VisualBasic
Imports System.Data.SqlClient
Imports System.Web.Configuration

Public Class Usuarios

    Public Function ResgataUsuarios() As Generic.List(Of Usuario)
        Dim coll As New Generic.List(Of Usuario)
        Dim connString As String = 
          WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString
        Dim conn As New SqlConnection(connString)
        Dim cmd As New SqlCommand("SELECT UsuarioID, Nome, Email FROM Usuario", conn)
        Dim dr As SqlDataReader = Nothing
        Try
            conn.Open()
            dr = cmd.ExecuteReader(Data.CommandBehavior.CloseConnection)
            While dr.Read()
                Dim u As New Usuario
                With u
                    .ID = dr.GetInt32(0)
                    .Nome = dr.GetString(1)
                    .Email = dr.GetString(2)
                End With
                coll.Add(u)
            End While
        Finally
            If Not IsNothing(dr) Then
                dr.Close()
            End If
        End Try
        Return coll
    End Function

End Class

Como vemos, a classe Usuarios possui um método público que retorna uma lista tipificada de objetos do tipo Usuario. É este método que vamos definir no objeto ObjectDataSource, que fará todo o trabalho, desde criar a instância da classe até a chamada do método e a carga do controle GridView.

Veremos abaixo o passo à passo de como configurar o objeto ObjectDataSource com esta classe e método. Ao clicar na Smart Tag e escolhendo a Configure Data Source..., temos:

Cc518055.ExplorandoGridView06(pt-br,MSDN.10).png
Figura 6 - Selecionando o Objeto da classe de negócios.

Cc518055.ExplorandoGridView07(pt-br,MSDN.10).png
Figura 7 - Selecionando os métodos.

Como podemos ver na Figura 7, temos as abas para definir os métodos para Select, Update, Insert e Delete. Podemos ver que é mesmo muito semelhante a forma que trabalhamos com o objeto SqlDataSource mas, neste caso, devemos informar as funções que estão na classe de negócios responsáveis por cada ação. O código HTML resultante da criação deste objeto ObjectDataSource no arquivo ASPX é:

ASPX

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    SelectMethod="ResgataUsuarios"
    TypeName="Usuarios">
</asp:ObjectDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="ObjectDataSource1">
</asp:GridView>

Uma propriedade que ainda não analisamos é a TypeName, que recebe uma String que corresponde ao nome da classe que o objeto ObjectDataSource representará, utilizando Reflection para carregar e executar o(s) método(s) deste tipo. Esta String contém o nome de um determinado tipo particalmente qualificado, que está localizado nos diretórios \Bin ou \App_Code. Se o componente estiver registrado no Global Assembly Cache - GAC a String conterá o tipo complemento, incluindo o Namespace. Vale lembrar que, se o componente estiver no Global Assembly Cache, você deverá fazer as devidas referências no Machine.Config ou Web.Config, dependendo da sua necessidade.

Nota Importante: Se por algum motivo a classe de regras de negócios tiver um construtor parametrizado, você deverá utilizar o evento ObjectCreating do objeto ObjectDataSource. Este evento é invocado antes da criação do objeto, permitindo customizar esta instanciação. Um dos parâmetros do evento, é um objeto do tipo ObjectDataSourceEventArgs, que possui uma propriedade (escrita/leitura) chamada ObjectInstance, que tem a responsabilidade de executar os métodos que foram definidos nas propriedades [XXX]Method. Quando esta propriedade é definida neste evento, o objeto ObjectDataSource irá respeitar, executando os métodos baseando-se nesta instância. O código abaixo exibe a forma de como customizar:

C#

protected void ObjectDataSource1_ObjectCreating(object sender, 
    ObjectDataSourceEventArgs e){

    string conn =
        WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;    
    Usuarios usuarios = 
        new Usuarios(conn);
    e.ObjectInstance = usuarios;
}

VB.NET

Protected Sub ObjectDataSource1_ObjectCreating(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.ObjectDataSourceEventArgs) _    
    Handles ObjectDataSource1.ObjectCreating

    Dim conn As String = _
        WebConfigurationManager.ConnectionStrings.Item("ConnString").ConnectionString    
    Dim usuarios As New Usuarios(conn)
    e.ObjectInstance = usuarios
End Sub

XmlDataSource

Além dos objetos que vimos anteriormente, o ASP.NET 2.0 ainda disponibiliza um objeto nas mesmas características, mas este é utilizado para acessar arquivos XML. Este objeto é chamado de XmlDataSouce. Criei abaixo um arquivo XML chamado de Usuarios.xml, que contém uma relação de usuários para demonstrar a configuração e utilização deste objeto.

<?xml version="1.0" encoding="utf-8" ?>
<Usuarios>
    <Usuario
        Nome="Israel Aece"
        Email="israel@projetando.net"
        Cidade="Valinhos">    
    </Usuario>
    <Usuario
        Nome="Claudia Fernanda"
        Email="claudia@projetando.net"
        Cidade="Valinhos">
    </Usuario>
    <Usuario
        Nome="Juliano Aece"
        Email="juliano@projetando.net"
        Cidade="Valinhos">
    </Usuario>
    <Usuario
        Nome="Carlos Bonna"
        Email="bonna@projetando.net"
        Cidade="Campinas">
    </Usuario>
</Usuarios>

Depois do arquivo XML criado, vamos definí-lo como a fonte de dados de controle GridView utilizando o objeto XmlDataSource. A figura abaixo exibe como configurá-lo:

Cc518055.ExplorandoGridView08(pt-br,MSDN.10).png
Figura 8 - Configurando o objeto XmlDataSource.

No campo Data File (campo obrigatório) definimos o caminho do arquivo que queremos que o objeto XmlDataSource carregue. Já no campo Tranform File você pode definir opcionalmente um arquivo com os estilos (*.xsl) para que, se necessário, você altere a estrutura do arquivo XML. O código HTML gerado por este objeto é apresentado abaixo:

ASPX

<asp:XmlDataSource 
    ID="XmlDataSource1" 
    runat="server" 
    DataFile="~/Usuarios.xml">
</asp:XmlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="XmlDataSource1">
</asp:GridView>

Tipos de Parâmetros

Quando utilizamos algum objeto de DataSource, temos ainda uma seção dentro deles que não exploramos. Esta seção é definida através dos elementos FilterParameters, SelectParameters, InsertParameters, DeleteParameters e UpdateParameters. É através deles que passamos para a query, Stored Procedure ou método quando for uma classe de negócios os valores para os parâmetros que são necessários para efetuar a operação corretamente.

Dentro desta seção podemos definir os parâmetros que serão utilizados, porém customizando-os de acordo com a sua origem, ou seja, podemos ter parâmetros provenientes de QueryStrings, variáveis de sessão, Cookies, controles do formulário, entre outros.

Para cada um destes tipos de parâmetros existe uma classe específica que é herdada diretamente da classe Parameter que está contida no namespace System.Web.UI.WebControls. A classe Parameter fornece um mecanismo para preencher os parâmetros necessários na query, Stored Procedure ou classe de negócios. É esta a classe base para todos os tipos de parâmetros do ASP.NET.

Vejamos abaixo os tipos de parâmetros suportados pelo ASP.NET, que são classes derivadas da classe Parameter:

Classe

Descrição

ControlParameter

Este tipo de parâmetro é responsável por resgatar um determinado controle do WebForm através da propriedade ID que deve ser passada para este parâmetro através da propriedade ControlID. Outra propriedade essencial à este tipo de parâmetro é a PropertyName, na qual você deverá informar qual é a propriedade do controle que o ASP.NET deverá recuperar o valor. Abaixo é listado os controles e suas respectivas propriedades que são utilizadas neste cenário:

  • Label.Text

  • TextBox.Text

  • ListControl.SelectedValue

  • CheckBox.Checked

  • Calendar.SelectedDate

  • DetailsView.SelectedValue

  • GridView.SelectedValue

  • TreeView.SelectedValue

  • FileUpload.FileBytes

CookieParameter

Neste caso é passado ao parâmetro o valor contido dentro de um objeto do tipo HttpCookie. Deve-se informar através da propriedade CookieName o nome do Cookie à ser recuperado.

FormParameter

Define o valor do parâmetro com o valor que está contido dentro de um controle HTML. Deve-se informar através da propriedade FormField o nome do controle HTML à ser recuperado.

ProfileParameter

Define o valor do parâmetro com o valor que está contido dentro de uma propriedade do objeto Profile. Deve-se informar através da propriedade ParameterName o nome da propriedade do objeto Profile à ser recuperado.

QueryStringParameter

Define o valor do parâmetro com o valor que está contido dentro de uma QueryString. Deve-se informar através da propriedade QueryStringField o nome da QueryString à ser recuperado.

SessionParameter

Define o valor do parâmetro com o valor que está contido dentro de uma variável de sessão. Deve-se informar através da propriedade SessionField o nome da variável de sessão à ser recuperado.

Como todos esses parâmetros herdam da classe Parameter, os mesmos contém uma propriedade chamada DefaultValue que utilizamos para definir um valor padrão para quando o valor, controle ou propriedade dos respectivos parâmetros não estiverem disponíveis ou forem inválidos.

Abaixo veremos a utilização de dois desses parâmetros: o ControlParameter e o QueryStringParameter. O cenário do exemplo é resgatar os usuários baseando-se em um Estado (UF) informado pelo usuário, através de um TextBox (Figura 9) e QueryString (Figura 10):

ASPX

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT [Nome], [Email] FROM [Usuario] WHERE Estado = @UF">
    <SelectParameters>
        <asp:ControlParameter 
            ControlID="txtUF" 
            PropertyName="Text"
            Name="UF" />
    </SelectParameters>
</asp:SqlDataSource>

Cc518055.ExplorandoGridView09(pt-br,MSDN.10).png
Figura 9 - Utilizando o parâmetro do tipo ControlParameter.

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT [Nome], [Email] FROM [Usuario] WHERE Estado = @UF">
    <SelectParameters>
        <asp:QueryStringParameter 
            QueryStringField="UF" 
            Name="UF" />
    </SelectParameters>
</asp:SqlDataSource>

Cc518055.ExplorandoGridView10(pt-br,MSDN.10).png
Figura 10 - Utilizando o parâmetro do tipo QueryStringParameter.

Como podemos certificar, o valor do parâmetro (independente de onde está vindo) é passado, neste caso, para a query definida na propriedade SelectCommand utilizando o objeto SqlDataSource.

Eventos

Como estes objetos derivam de uma classe em comum (DataSourceControl) o mesmos tem alguns eventos semelhantes. Veremos cada sum desses eventos e suas respectivas descrições listadas na tabela abaixo:

Evento

Descrição

Deleted

Ocorre quando a exclusão é completada.

Deleting

Ocorre antes de executar a operação de exclusão.

Filtering

Ocorre antes de executar a operação de filtro.

Inserted

Ocorre quando a inserção é completada.

Inserting

Ocorre antes de executar a operação de inserção.

Selected

Ocorre quando a seleção é completada.

Selecting

Ocorre antes de executar a operação de seleção.

Updated

Ocorre quando a atualização é completada.

Updating

Ocorre antes de executar a operação de atualização.

ObjectCreated*

Ocorre depois que o objeto especificado na propriedade PropertyName for criado.

ObjectCreating*

Ocorre antes que o objeto especificado na propriedade PropertyName seja criado.

ObjectDisposing*

Ocorre antes que o objeto especificado na propriedade PropertyName seja destruído.

Tipos de Colunas

Ao contrário das versões anteriores, o controle GridView nos fornece uma grande quantidade de tipos de colunas, que antes só conseguíamos através da criação manual de uma Template Column. Para que o desenvolvedor perca não tempo na criação destes tipos de colunas a Microsoft já implementou no GridView alguns novos tipos de colunas que usamos com bastante frequencia nas aplicações atuais. As mesmas estão listadas abaixo:

  • BoundField

  • CheckBoxField

  • HyperLinkField

  • ImageField

  • ButtonField

  • CommandField

  • TemplateField

Voltando ao DataGrid da versão 1.x do ASP.NET, tínhamos que ir até o Property Builder para definirmos as colunas que queríamos que fossem apresentadas neste controle. Atualmente temos o que chamamos de Smart Tags que nos auxiliam, disponibilizando um acesso rápido e intuitivo às principais propriedades dos controles. É através destas Smart Tags que abrimos a janela para criarmos e editarmos as colunas que queremos em nosso controle GridView. A figura abaixo ilustra isso:

Cc518055.ExplorandoGridView11(pt-br,MSDN.10).png
Figura 11 - Definindo as colunas do GridView.

Ao clicar em Edit Columns... na Smart Tag uma nova janela é aberta, e então definiremos as colunas do nosso GridView. Na figura abaixo conseguimos visualizar, envolvidas em um quadrado vermelho, os tipos das colunas mencionadas acima:

Cc518055.ExplorandoGridView12(pt-br,MSDN.10).png
Figura 12 - Visualizando e definindo as colunas do GridView.

Dentro desta janela principal temos três importantes seções que configurarão as colunas do GridView. A figura abaixo exibe essas seções e veremos qual a finalidade de cada uma delas:

Cc518055.ExplorandoGridView13(pt-br,MSDN.10).png
Figura 13 - Seções da Janela de colunas.

  1. Exibe as colunas disponíveis para adicionarmos no GridView.

  2. Exibe as colunas que já fazem parte do GridView.

  3. As propriedades correspondentes a cada coluna selecionada na seção 2.

Quando já temos um controle do tipo Data Source definido, como é ilustrado na figura 3, já são listados como BoundFields os campos provenientes desta fonte, nos quais podemos também definir como uma coluna do GridView.

Para adicionarmos uma nova coluna ao GridView basta selecionar o tipo da mesma na seção 1 e clicar no botão Add. Assim a mesma aparecerá na seção 2, onde podemos ajustar e definir as propriedades na seção 3 desta janela. Também nesta seção (a número 2), temos a opção de exclusão de uma coluna, clicando no botão X. Finalmente, ainda nesta seção, temos mais dois botões onde definimos a ordem de cada coluna.

Outra opção que chama a atenção é a propriedade Auto-generate fields, que é responsável por gerar automaticamente as colunas do GridView refletindo exatamente os campos do controle Data Source informado. Há também um link chamado Convert this field into a TemplateField, que tem a finalidade de converter qualquer coluna definida no controle GridView em uma Template, que permite ao desenvolvedor manipular manualmente.

Tipos de Colunas

BoundField

Esta coluna simplesmente representa um campo da fonte de dados em uma coluna como um texto (Label) dentro do GridView. É esta a coluna padrão para todos os campos do tipo texto da fonte de dados, em caso de não defirnirmos manualmente.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:BoundField 
            DataField="UsuarioID" 
            HeaderText="UsuarioID" 
            InsertVisible="False"
            ReadOnly="True" 
            SortExpression="UsuarioID" />
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" 
            SortExpression="Nome" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" 
            SortExpression="Email" />
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna BoundField:

Propriedade

Descrição

DataField

Define a coluna da fonte de dados que será exibida.

NullDisplayText

Define um valor padrão para ser exibido quando o campo da fonte de dados for nulo.

CheckBoxField

Esta coluna é um caso típico onde nas versões 1.x do ASP.NET precisávamos criar uma coluna do tipo TemplateColumn se quiséssemos exibir um valor booleano em formato de um controle CheckBox. Isso foi resolvido com a criação da coluna CheckBoxField, que representa através de um controle CheckBox um valor booleano da fonte de dados.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server"
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:CheckBoxField 
            DataField="Ativo" 
            HeaderText="Ativo" 
            SortExpression="Ativo" />
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna CheckBoxField:

Propriedade

Descrição

DataField

Define a coluna booleana da fonte de dados que será exibida no controle CheckBox.

Text

Define um valor padrão a ser exibido como "Label" ao controle CheckBox.

HyperLinkField

Apesar desta coluna já existir na versão 1.x do ASP.NET, ela tem uma limitação: só podemos definir um único parâmetro através do Property Builder do controle DataGrid. Se por alguma situação especial precisássemos ter mais de um parâmetro definido na QueryString, tínhamos que recorrer ao código para fazer isso. Essa deficiência foi sanada nesta nova versão, onde agora podemos definir não apenas um campo da fonte de dados, mas sim um Array contendo os nomes dos campos que desejamos anexar à URL.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:HyperLinkField 
            DataNavigateUrlFields="UsuarioID,Ativo" 
            DataNavigateUrlFormatString="?ID={0}&Ativo={1}"
            DataTextField="Nome" 
            NavigateUrl="Pagina.aspx" />
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna HyperLinkField:

Propriedade

Descrição

DataNavigateUrlFields

Contém os nomes das colunas que serão anexadas à URL, separadas por vírgula.

DataNavigateUrlFormatString

Define a String que especifica os parâmetros e suas posições na QueryString.

DataTextField

Define a coluna da fonte de dados que será exibida na propriedade Text do HyperLink.

NavigateUrl

Define a página de destino a qual o usuário será redirecionado ao clicar.

Text

Define um valor que será exibido na propriedade Text do HyperLink.

ImageField

Como já vimos um pouco acima, este tipo de coluna também exigia a criação de uma coluna do tipo TemplateColumn nas versões anteriores do ASP.NET. A coluna ImageField veio para suprir esta necessidade, possibilitando exibir uma imagem proveniente de uma fonte de dados (seja o caminho/path ou a imagem em si).

Caso você tenha na sua fonte de dados a imagem em si ao invés de um path/caminho para um arquivo físico, você terá que criar um Handler (implementando a Interface IHttpHandler) para exibir a referida Imagem. Como isso foge do escopo deste artigo, fica registrado aqui um artigo do Dino Esposito, onde ele explica como criar e implementar este Handler.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:ImageField 
            DataImageUrlField="Foto" 
            DataImageUrlFormatString="~/Images/{0}">
        </asp:ImageField>
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna ImageField:

Propriedade

Descrição

DataImageUrlField

Define a coluna da fonte de dados que contém o Path da foto a ser exibida.

DataImageUrlFormatString

Contém a formatação da URL para a imagem a ser exibida.

ButtonField

Este tipo de coluna representa uma coluna da fonte de dados em formato de um Button. É utilizada quando queremos executar uma ação customizada em nosso GridView ou fora dele, no qual podemos identificá-la através da propriedade CommandName que definimos para esta coluna.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:ButtonField 
            DataTextField="Nome" 
            Text="Button" 
            CommandName="Exibir" />
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna ButtonField:

Propriedade

Descrição

CommandName

Define uma String como uma espécie de "chave" para sabermos que botão foi clicado e, assim, executar uma determinada ação.

DataTextField

Define o nome da coluna da fonte de dados que será apresentado na propriedade Text do Button.

CommandField

Representa um conjunto especial de controles que, acrescentado ao GridView, habilita-o para operações de Select, Cancel, Update, Insert e Delete para manipular os dados que foram carregados através da fonte de dados.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:CommandField 
            ShowDeleteButton="True" 
            ShowEditButton="True" 
            ShowSelectButton="True" />
    </Columns>
</asp:GridView>

Abaixo é mostrado uma listagem com as principais propriedades da coluna CommandField:

Propriedade

Descrição

ShowCancelButton

Exibe ou oculta o Button para cancelar.

ShowDeleteButton

Exibe ou oculta o Button para exclusão.

ShowEditButton

Exibe ou oculta o Button para edição.

ShowInsertButton

Exibe ou oculta o Button para inserção.

ShowSelectButton

Exibe ou oculta o Button para seleção.

TemplateField

Caso nenhuma das colunas apresentadas anteriormente satisfaça a necessidade, podemos optar pela criação de uma TemplateField ou TemplateColumn, como também é conhecida. Este tipo de coluna permite customizá-la, incluindo qualquer tipo de formatação ou controle.

O código HTML gerado na página ASPX por este tipo de coluna é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:RadioButton 
                    ID="RadioButton1" 
                    runat="server" />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Habilitando a Ordernação de Registros

Quando exibimos ao cliente uma grande quantidade de dados, algumas funcionalidades se fazem essenciais para que ele possa visualizar e analisar estes dados de forma mais eficiente. Uma dessas funcionalidades é chamada de ordenação de colunas, ou Sorting.

Quando utilizamos o DataGrid da versão 1.x do ASP.NET, para poder ter esta funcionalidade, tínhamos que, através do evento SortCommand do DataGrid, recuperar a coluna que foi clicada pelo usuário e invocar novamente o procedimento que resgata os dados e popula o Datagrid, informando a coluna que será utilizada para efetuar a ordenação. Agora, com o controle GridView, conseguimos obter esta funcionalidade sem a escrita de nenhuma linha de código, salvo quando utilizamos o objeto ObjectDataSource.

Utilizando o objeto SqlDataSouce

Para habilitarmos esta funcionalidade no controle GridView utilizando o objeto SqlDataSource devemos clicar na Smart Tag e marcar o CheckBox chamado Enable Sorting, como é mostrado na imagem abaixo:

Cc518055.ExplorandoGridView14(pt-br,MSDN.10).png
Figura 14 - Habilitando a Ordernação de Registros.

Habilitando esta opção, o controle GridView já fará a ordenação dos registros automaticamente. Através do código HTML abaixo da página ASPX, vemos como o mesmo ficou configurado:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    AllowSorting="True"	
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" 
            SortExpression="Nome" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" />
    </Columns>
</asp:GridView>

Analisando o código acima podemos notar a propriedade SortExpression da coluna do tipo BoundField. É através desta propriedade que o ASP.NET se encarrega de ordenar a coluna automaticamente clicada pelo usuário e, no caso acima, somente a ordenação para a coluna Nome está habilitada. Algo importante à dizer é que a propriedade SortExpression deve conter exatamente o nome da coluna da fonte de dados para que a ordernação seja possível.

O objeto SqlDataSource possui um propriedade chamada DataSourceMode. Através desta propriedade você define ao objeto SqlDataSource qual será a forma, Dataset ou DataReader, que ele utilizará para resgatar os dados da base de dados. Por padrão, ou seja, quando não definimos esta propriedade, o SqlDataSource faz isso utilizando um objeto do tipo Dataset.

como já vimos acima, essa ordenação é feita automaticamente somente quando definimos a propriedade DataSourceMode como Dataset (ou quando omitimos). Quando definimos esta propriedade como DataReader, sem mais nenhuma outra configuração no objeto SqlDataSource, receberemos a seguinte exceção:

Cc518055.ExplorandoGridView15(pt-br,MSDN.10).png
Figura 15 - Exceção causada utilizando a propriedade DataSourceMode incorretamente.

Quando definimos o objeto DataReader para recuperar os dados e quisermos também ter a ordenação dos registros, devemos fazer alguns outros ajustes no objeto SqlDataSource para alcançar este objetivo. Vamos analisá-los passo à passo abaixo:

  • A propriedade DataSourceMode: Definimos para DataReader quando queremos utilizar este para recuperar os dados da fonte de dados.

  • A propriedade SelectCommand: Por design, somente conseguimos isso utilizando uma Stored Procedure. É nesta propriedade que devemos definir o nome da Stored Procedure que será responsável por recuperar os dados.

  • A propriedade SelectCommandType: Como a propriedade SelectCommand estará armazenando uma Stored Procedure, através da SelectCommandType informaremos exatamente isso, ou seja, informar ao ASP.NET que é uma Stored Procedure ao invés de um Sql Statement.

  • A propriedade SortParameterName: É justamente o parâmetro que estará dentro da Stored Procedure e que será responsável pela ordenação. Automaticamente o ASP.NET associa o nome da coluna clicada pelo usuário (a propriedade SortExpression) como o value deste parâmetro. Nota: Não há necessidade de colocar o "@" no nome deste parâmetro.

Para exemplificar o uso do objeto DataReader para recuperar os dados através do SqlDataSource e utilizar a ordenação dos dados, abaixo é exibido o código HTML da página ASPX e a respectiva Stored Procedure (que está definida na propriedade SelectCommand):

ASPX

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    DataSourceMode="DataReader"
    SortParameterName="Coluna"            
    SelectCommand="Sorting" 
    SelectCommandType="StoredProcedure">
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    AllowSorting="True"	
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" 
            SortExpression="Nome" />
    </Columns>
</asp:GridView>

T-SQL

ALTER PROCEDURE dbo.Sorting 
	@Coluna As Varchar(30)
AS
	DECLARE @Query As NVarchar(4000)
	SET @Query = 'SELECT Nome, Email, UsuarioID FROM Usuario'
	
	IF @Coluna != ''
		SET @Query = @Query + ' ORDER BY ' + @Coluna
		
	EXECUTE sp_executesql @Query

Como vemos, a Stored Procedure recebe um parâmetro que será utilizado para efetuar a ordenação. Reparem que o nome do parâmetro da Stored Procedure é o mesmo que está definido na propriedade SortParameterName do objeto SqlDataSource. A verificação do valor deste parâmetro, se é ou não vazio, é necessária pois a primeira vez que a página for carregada não é passado nenhum valor à ele.

Utilizando o objeto ObjectDataSouce

Ao contrário do objeto SqlDataSouce, o objeto ObjectDataSouce necessita de algum código adicional justamente porque o mesmo está ligado à uma classe de regras negócios, a qual deve contemplar a ordenação de um result-set qualquer. O processo de ordenação para o objeto ObjectDataSource é basicamente idêntico ao mostrado acima com o objeto SqlDataSource, apenas contemplando nos parâmetros do método que retorna a coleção de objetos uma string que representará a coluna à ser ordenada.

Como a parte de vinculação do objeto ObjectDataSouce ao controle GridView é semelhante à forma acima, vamos diretamente analisar o código que será responsável pela ordenação de registros:

C#

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Configuration;
using System.Collections.Generic;

public class Usuarios
{
    public List<Usuario> ResgataUsuarios() {
        return ResgataUsuarios(string.Empty);
    }

    public List<Usuario> ResgataUsuarios(string coluna) {
        List<Usuario> coll = new List<Usuario>();
        string connString = 
          WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString
        SqlConnection conn = new SqlConnection(connString);
        string query = "SELECT UsuarioID, Nome, Email FROM Usuario";

        if(coluna != string.Empty)
            query += " ORDER BY " + coluna;

        SqlCommand cmd = new SqlCommand(query, conn);
        SqlDataReader dr = null;
        try
        {
            conn.Open();
            dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            while(dr.Read()){
                Usuario u = new Usuario();
                u.ID = dr.GetInt32(0);
                u.Nome = dr.GetString(1);
                u.Email = dr.GetString(2);
                coll.Add(u);
            }
        }
        finally {
            if (dr != null)
                dr.Close();
        }
        return coll;
    }
}

VB.NET

Imports Microsoft.VisualBasic
Imports System.Data.SqlClient
Imports System.Web.Configuration

Public Class Usuarios

    Public Function ResgataUsuarios() As Generic.List(Of Usuario)
        Return ResgataUsuarios(String.Empty)
    End Sub

    Public Function ResgataUsuarios(ByVal coluna As String) As Generic.List(Of Usuario)
        Dim coll As New Generic.List(Of Usuario)
        Dim connString As String = 
          WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString
        Dim conn As New SqlConnection(connString)
        Dim query As String = "SELECT UsuarioID, Nome, Email FROM Usuario"

        If Not coluna = String.Empty Then
            query &= " ORDER BY " & coluna
        End If

        Dim cmd As New SqlCommand(query, conn)
        Dim dr As SqlDataReader = Nothing
        Try
            conn.Open()
            dr = cmd.ExecuteReader(Data.CommandBehavior.CloseConnection)
            While dr.Read()
                Dim u As New Usuario
                With u
                    .ID = dr.GetInt32(0)
                    .Nome = dr.GetString(1)
                    .Email = dr.GetString(2)
                End With
                coll.Add(u)
            End While
        Finally
            If Not IsNothing(dr) Then
                dr.Close()
            End If
        End Try
        Return coll
    End Function

End Class

Analisando o código acima temos sobrecarga para o método ResgataUsuarios, onde em um deles recebemos uma string que corresponde à coluna a ser ordenada. Como fizemos com a Stored Procedure, neste método também analisamos se o parâmetro é ou não vazio pois, a partir disto, acrescentamos a cláusula ORDER BY, se necessário. Há ainda pequenas configurações a serem feitas no objeto ObjectDataSource, ou seja, no caso deste objeto, em sua Smart Tag, não temos o CheckBox para habilitar a Ordenação, tendo assim que recorrer à janela de propriedade e marcar a propriedade AllowSorting como True.

Além disso precisamos também definir a propriedade SortParameterName onde, neste caso, irá o nome do parâmetro responsável pela ordenação do nosso método ResgataUsuarios, que é coluna. Vejamos abaixo o código HTML resultante na página ASPX depois dessas configurações:

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    SelectMethod="ResgataUsuarios"
    SortParameterName="coluna"
    TypeName="Usuarios">
</asp:ObjectDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataSourceID="ObjectDataSource1" 
    AllowSorting="True">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" 
            SortExpression="Nome" />
    </Columns>
</asp:GridView>

Automaticamente em runtime, o ASP.NET define o valor da propriedade SortExpression clicada pelo usuário como value do parâmetro do método ResgataUsuarios. Essa atribuição é possível pois já temos mapeado na propriedade SortParameterName o nome do parâmetro responsável pela ordenação do método referido.

Observação Importante: A propriedade SortParameterName é extremamente importante, independentemente de qual objeto de Data Source utilizamos no controle GridView: isso porque podemos ter vários parâmetros na Stored Procedure ou mesmo no método da classe de negócios.

Habilitando a Paginação de Registros

Há momentos em que o result-set da base de dados é muito grande, ou seja, é retornado ao cliente uma grande quantidade de registros, o que pode custar muito caro para o mesmo pois recebe todos os registros e exibe apenas os registros que se enquadram na página atual, e limitando ao número de registros por páginas.

A paginação de dados vem justamente para suprir esta necessidade, podendo dar ao usuário uma forma mais simples de navegar pelo result-set e consequentemente, a aplicação ganha muito em performance. O controle GridView já fornece uma arquitetura que permite a paginação automática de registros.

A forma de como habilitar esta opção é idêntica à ordenação de colunas que fizemos anteriormente, ou seja, deve-se clicar na Smart Tag do controle GridView e marcar o CheckBox chamado Enable Paging, como é ilustrado na figura abaixo:

Cc518055.ExplorandoGridView16(pt-br,MSDN.10).png
Figura 16 - Habilitando a Paginação de Registros.

Habilitando esta opção, o controle GridView já fará a paginação dos registros automaticamente. Através do código HTML abaixo da página ASPX, vemos como o mesmo ficou configurado:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AllowPaging="True"             
    AutoGenerateColumns="False" 
    DataSourceID="SqlDataSource1"
    PageSize="2">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" />
    </Columns>
</asp:GridView>

Uma nova propriedade é apresentada: PageSize. Ela é responsável por definir a quantidade de registros que serão apresentados por página.

Utilizando o objeto SqlDataSouce

Como neste objeto temos a propriedade DataSourceMode, onde definimos qual será a forma de recuperar os dados da fonte de dados (Dataset ou DataReader), infelizmente ele não pode ser definido como DataReader, pois para efetuar a paginação o objeto deverá obrigatoriamente implementar a interface ICollection, que é somente o caso do objeto Dataset. Vale lembrar que isso já é um problema conhecido nas versões 1.x do ASP.NET.

Para conseguir a paginação de forma automática temos que definir a propriedade DataSourceMode como Dataset ou omití-la. Mas é importante dizer que nada impedirá você de criar uma paginação e controle customizados para esta situação.

Utilizando o objeto ObjectDataSouce

Ao utilizar este tipo de objeto temos que fazer alguns ajustes para habilitar a paginação no controle GridView. Apesar de existir algumas formas diferentes de se fazer, no exemplo mostrado abaixo a paginação retornará apenas os registros pertinentes a página solicitada pelo usuário. Logo a Stored Procedure responsável por retornar estes registros deve receber dois parâmetros:

  • PageNumber - O número da página requisitada pelo usuário.

  • PageSize - Quantidade de registros por página.

Abaixo é exibida a Stored Procedure que utiliza subqueries para retornar a quantidade de registros de uma determinada página:

T-SQL

ALTER PROCEDURE dbo.CustomPaging 
    @PageSize As Int,    
    @PageNumber As Int AS
	
    DECLARE @COUNTER As Int
    SET @COUNTER = (SELECT COUNT(*) FROM Usuario)
	
    IF @PageNumber = 0
        SET @PageNumber = 1
	
    SELECT Nome FROM Usuario
        WHERE UsuarioID IN 
            (SELECT TOP(@PageSize) UsuarioID FROM Usuario
                WHERE UsuarioID NOT IN 
                    (SELECT TOP(@PageSize * (@PageNumber - 1)) UsuarioID FROM Usuario
                        ORDER BY Nome)
            ORDER BY Nome)
        ORDER BY Nome
			
    RETURN @COUNTER

Agora com o SQL Server 2005 e o SQL Server Express 2005, a cláusula TOP do T-SQL pode ser parametrizada, o que nas versões anteriores só conseguíamos através de uma query dinâmica. A quantidade total de registros (que utilizamos a função COUNT para recuperar) é necessária, pois o objeto ObjectDataSource precisa desse valor para calcular as páginas à serem exibidas ao usuário.

Depois da Stored Procedure pronta vamos analisar a classe de negócios que receberá os parâmetros e executará a Stored Procedure acima. Abaixo o código da classe de negócios:

C#

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Collections.Generic;

public class Paging
{
    private int _count = 0;

    public int TotalUsuarios(){
        return this._count;
    }

    public List ResgataUsuarios(int pageSize, int pageNumber)
    {
        List coll = new List();
        SqlConnection conn = new SqlConnection(
            WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString);
        SqlCommand cmd = new SqlCommand("CustomPaging", conn);

        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@PageSize", pageSize);
        cmd.Parameters.AddWithValue("@PageNumber", pageNumber);
        cmd.Parameters.AddWithValue("@RETURN_VALUE", 0).Direction = 
            ParameterDirection.ReturnValue;

        SqlDataReader dr = null;
        try
        {
            conn.Open();
            dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            while (dr.Read())
            {
                Usuario u = new Usuario();
                u.Nome = dr.GetString(0);
                coll.Add(u);
            }
        }
        finally
        {
            if (dr != null)
                dr.Close();

            this._count = 
                Convert.ToInt32(cmd.Parameters["@RETURN_VALUE"].Value);
        }
        return coll;
    }
}

VB.NET

Imports Microsoft.VisualBasic
Imports System.Data
Imports System.Data.SqlClient
Imports System.Collections.Generic
Imports System.Web.Configuration

Public Class Paging

    Private _count As Integer = 0

    Public Function TotalUsuarios() As Integer
        Return Me._count
    End Function

    Public Function ResgataUsuarios(ByVal pageSize As Integer, _
        ByVal pageNumber As Integer) As Generic.List(Of Usuario)

        Dim coll As New Generic.List(Of Usuario)
        Dim conn As SqlConnection = _
            New SqlConnection( _
                WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString)
        Dim cmd As New SqlCommand("CustomPaging", conn)

        cmd.CommandType = CommandType.StoredProcedure
        cmd.Parameters.AddWithValue("@PageSize", pageSize)
        cmd.Parameters.AddWithValue("@PageNumber", pageNumber)
        cmd.Parameters.AddWithValue("@RETURN_VALUE", 0).Direction = _
            ParameterDirection.ReturnValue

        Dim dr As SqlDataReader = Nothing
        Try
            conn.Open()
            dr = cmd.ExecuteReader(Data.CommandBehavior.CloseConnection)
            While dr.Read()
                Dim u As New Usuario
                With u
                    .Nome = dr.GetString(0)
                End With
                coll.Add(u)
            End While
        Finally
            If Not IsNothing(dr) Then
                dr.Close()
            End If

            Me._count = _
                Convert.ToInt32(cmd.Parameters("@RETURN_VALUE").Value)
        End Try
        Return coll
    End Function

End Class

Analisando o código acima, temos um membro privado na classe Paging, que é responsável por armazenar a quantidade total de registros da base de dados (ou da condição da cláusula SQL). O método ResgataUsuarios que recebe os parâmetros pageSize e o pageNumber, os quais já descrevemos acima suas utilidades, e através dele os mesmos são anexados à coleção de parâmetros do objeto SqlCommand que executa a Stored Procedure.

Através de um objeto SqlDataReader percorremos os registros da base de dados e populamos o objeto Usuario que posteriormente é adicionado à uma coleção genérica deste mesmo tipo.

Já o método TotalUsuarios retorna o valor contido dentro do membro privado denominado _count, o qual representa a quantidade total de registros da base de dados (ou da condição da cláusula SQL). Como já foi falado, o objeto ObjectDataSource ou qualquer outro tipo de paginação requer este valor para calcular e exibir a quantidade de páginas disponíveis ao usuário. Recuperamos este valor total através de um parâmetro do tipo RETURN_VALUE, que a Stored Procedure retorna para a aplicação.

Através do código HTML abaixo da página ASPX, vemos como configurar o objeto ObjectDataSource e o controle GridView corretamente:

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    EnablePaging="True"
    SelectCountMethod="TotalUsuarios"                     
    StartRowIndexParameterName="pageNumber"
    MaximumRowsParameterName="pageSize"
    SelectMethod="ResgataUsuarios"
    TypeName="Paging">
</asp:ObjectDataSource>    

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AllowPaging="True" 
    AutoGenerateColumns="False"
    DataSourceID="ObjectDataSource1"
    PageSize="2">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
    </Columns>
</asp:GridView>

Analisando o código HTML da página ASPX acima, vemos duas propriedades que já são de nosso conhecimento: a SelectMethod e TypeName, que foram descritas na seção sobre Tipos de DataSource. Abaixo são listadas as propriedades do objeto ObjectDataSource que ainda não analisamos:

  • EnablePaging - É necessário definir para True quando queremos habilitar a paginação de registros no objeto ObjectDataSource.

  • SelectCountMethod - Definimos nesta propriedade o nome do método da classe de negócios que é responsável por retornar a quantidade total de registros.

  • StartRowIndexParameterName - Baseando-se na classe de negócios que criamos acima, esta propriedade é definida com o nome do parâmetro (pageNumber) responsável pela página que o usuário requisitou.

  • MaximumRowsParameterName - Baseando-se na classe de negócios que criamos acima, esta propriedade é definida com o nome do parâmetro (pageSize) responsável pela quantidade de registros exibidos por página.

Já na configuração do controle GridView não se tem muito à fazer: temos que definir a fonte de dados a ser utilizada; habilitar a paginação através da propriedade AllowPaging e, finalmente, através da propriedade PageSize definir a quantidade de registros que serão exibidos por página. Realizados esses ajustes, o GridView já está pronto para realizar a paginação de registros sem a necessidade de definirmos os valores aos parâmetros, pois o controle GridView em conjunto com o objeto ObjectDataSource fará isso de forma transparente.

A imagem abaixo exibe o controle GridView já em funcionamento utilizando o objeto ObjectDataSource como fonte de dados:

Cc518055.ExplorandoGridView17(pt-br,MSDN.10).png
Figura 17 - GridView em funcionamento.

Formatando o GridView

Assim como nas versões anteriores, o ASP.NET 2.0 fornece ao desenvolvedor alguns estilos para definirmos no controle GridView. Utilizando esses estilos evitam-se escrever códigos CSS para tal finalidade mas, claro, desde que os estilos pré-definidos pelo Visual Studio .NET sejam compatíveis com o layout da aplicação.

Para acessar esses estilos no controle GridView basta simplesmente clicar em sua Smart Tag e, posteriormente no link Auto Fomart. Uma janela semelhante a mostrada abaixo é exibida para você escolher o estilo que desejar.

Cc518055.ExplorandoGridView18(pt-br,MSDN.10).png
Figura 18 - Utilizando o Auto Format.

Como vemos, existem vários estilos/formatações que podemos escolher para se aplicar no controle GridView. Apesar do controle GridView dar suporte à uma nova funcionalidade chamada "Temas e Skins" (através da propriedade SkinID), que são arquivos que definem a aparência visual de um determinado controle, o Auto Format não tem a ver com isso.

De qualquer forma, não será abordado nesta série de artigos Temas e Skins por ser um assunto extenso, que requer um artigo específico, mas fica registro aqui um artigo diretamente do portal MSDN que explica detalhadamente tal funcionalidade.

Além disso, podemos definir para cada coluna um estilo diferente, podendo inclusive informar através da propriedade CssClass uma classe em nosso arquivo de estilos (*.css) que corresponderá a formatação de uma coluna ou uma seção dela. A figura abaixo exibe como ter acesso à essas propriedades de formatação.

Cc518055.ExplorandoGridView19(pt-br,MSDN.10).png
Figura 19 - Definindo a formatação das colunas.

Como já vimos anteriormente, clica-se na Smart Tag do controle GridView e em seguida no link Edit Columns. A janela acima é exibada e, assim, podemos clicar em cima da coluna que desejamos alterar, e suas respectivas propriedades são apresentadas no painel da direita. Em vermelho vemos as propriedades de estilos para a coluna e percebemos a flexibilidade, onde podemos definir um estilo completamente diferente para cada seção (Control, Footer, Header e Item) do controle GridView.

Formatando Valores

Freqüentemente quando utilizamos um controle do tipo GridView e inserimos valores do tipo Data, Dinheiro, Inteiros ou Decimais, precisamos formatar esse valor de acordo com a finalidade desse Campo. Para tal necessidade as colunas nos fornecem uma propriedade chamada DataFormatString, que veremos a sua utilidade abaixo.

Existem dois tipos de formatação: Standard Formats e Custom Formats. O objetivo é mostrar as formatações mais utilizadas em aplicativos para serem executados nos padrões brasileiros, então deixemos claro o seguinte: o padrão para valores numéricos será o Stardand Format. Já a formatação para datas, será utilizado o Custom Format.

A propriedade DataFormatString fornece uma formatação customizada para o valor inserido na BoundColumn. Esta propriedade consiste em duas partes separadas por dois pontos estando dentro de um par de chaves da seguinte forma: {:}. Isso é válido apenas quando estiver inserindo na BoundColumn valores numéricos ou do tipo data.

A sintaxe é a seguinte: {0:[Formato][Qtde. Casas Decimais]}. O caracter que vem após os dois pontos é o formato em que o valor será exibido. Você também poderá optar por definir a quantidade de casas decimais da seguinte forma: {0:C2}. A seguir uma lista com os valores possíveis:

Standard Format

Descrição

C

Exibe o valor no formato de moeda.

D

Exibe o valor no formato decimal.

E

Exibe o valor no formato científico (exponencial).

F

Exibe o valor no formato fixo.

G

Exibe o valor no formato geral.

N

Exibe o valor no formato numérico.

P

Exibe o valor no formato de porcentagem.

X

Exibe o valor no formato hexadecimal.

Observação: Os caracteres acima que especificam o formato a ser exibido não são case-sensitive, exceto para o X, pois se ele for minúsculo os valores serão apresentados em minúsculo, do contrário, serão exibidos em maiúsculo.

Custom Format

Descrição

MM/dd/yyyy

Formato Mês/Dia/Ano

dd/MM/yyyy

Formato Dia/Mês/Ano

hh:mm

Formato Hora:Minuto

hh:mm:ss

Formato Hora:Minuto:Segundo

dd/MM/yyyy hh:mm:ss

Formato Dia/Mês/Ano Hora:Minuto:Segundo

Observação 1: Devemos nos atentar para o MM e para o mm, pois o maiúsculo significa Mês, já o minúsculo significa Minutos.

Observação 2: Você poderia também ao invés de barras "/" utilizar o hífen "-" como separador para as Datas, ficando a String de formatação da seguinte forma: {0:dd-MM-yyyy hh:mm:ss}.

Para configurar os valores no controle GridView clique na Smart Tag do mesmo e selecione Edit Columns. Em seguida, clique em cima da coluna que deseja formatar e a propriedade DataFormatString será exibida no painel do lado direito, como é exibido na figura abaixo:

Cc518055.ExplorandoGridView20(pt-br,MSDN.10).png
Figura 20 - Definindo a propriedade DataFormatString.

Cc518055.ExplorandoGridView21(pt-br,MSDN.10).png
Figura 21 - GridView com a propriedade DataFormatString configurada.

Há ainda vários outros padrões para a formatação de datas os quais optei por não colocar aqui pois utilizamos na maioria das vezes o formato brasileiro. Mas para quem se interessar pode encontrar maiores informações no link direto da fonte da Microsoft: Standard DateTime Format Strings.

Customização em runtime

Há casos em que necessitamos customizar alguns desses estilos em runtime, como por exemplo: se um valor exceder um X, a cor da linha em questão do controle GridView deve ser vermelha. No controle DataGrid utilizávamos o evento ItemDataBound para fazer essa consistência. Já no controle GridView temos ainda este evento, mas com um outro nome: RowDataBound. O exemplo abaixo ilustra esse processo:

C#

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow) {
        DbDataRecord data = (DbDataRecord)e.Row.DataItem;
        if (data.GetDecimal(0) > 2)
            e.Row.BackColor = Color.Red;
        }
}

VB.NET

Protected Sub GridView1_RowDataBound(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
    Handles GridView1.RowDataBound

    If e.Row.RowType = DataControlRowType.DataRow Then
        Dim data As DbDataRecord = DirectCast(e.Row.DataItem, DbDataRecord)
        If data.GetDecimal(0) > 2 Then
            e.Row.BackColor = Color.Red
        End If
    End If
End Sub

Através do enumerador DataControlRowType verificamos qual o tipo da linha, pois esse evento é disparado a cada linha criada no controle GridView. Se a linha for do tipo DataRow, aí iremos verificar se o valor da coluna "Valor" é ou não maior que 2 (essa será a consistência para o exemplo).

Devemos agora nos atentar para qual o tipo de fonte de dados está sendo passado para a propriedade DataSource do controle. Isso varia quando utilizamos DataSets e DataReaders, pois devemos fazer o Cast para o tipo de objeto correto. Finalmente, se a condição for atendida, mudamos a cor da linha em questão para vermelho.

O código abaixo mostra os tipos de casts para as diferentes fonte de dados:

C#

//Datasets
((DataRowView)e.Row.DataItem)["NomeColuna"]
((DataRowView)e.Row.DataItem)[0]

//DataReaders
((DbDataRecord)e.Row.DataItem)["NomeColuna"]
((DbDataRecord)e.Row.DataItem)[0]

//Objetos
((TipoObjeto)e.Row.DataItem).Propriedade

VB.NET

'Datasets
DirectCast(e.Row.DataItem, DataRowView).Item("NomeColuna")
DirectCast(e.Row.DataItem, DataRowView).Item(0)

'DataReaders
DirectCast(e.Row.DataItem, DbDataRecord).Item("NomeColuna")
DirectCast(e.Row.DataItem, DbDataRecord).Item(0)

'Objetos
DirectCast(e.Row.DataItem, TipoObjeto).Propriedade

Lembrando que no caso do uso dos DataReaders, devemos importar o Namespace System.Data.Common para poder utilizar a Classe DbDataRecord.

Observação: O objeto GridViewRowEventArgs que vem como parâmetro no evento RowDataBound contém as informações completas da linha em questão, ou seja, como este evento é disparado a cada linha que é criada dentro do controle GridView, ele nos fornece informações como o tipo da linha, os valores provenientes da fonte de dados que serão exibidos, os controles filhos de cada linha, entre diversas outras propriedades.

Sumarizando Valores

Há casos onde necessitamos somar o valor de uma determinada coluna do controle GridView de uma aplicação ASP.NET, onde é exibido ao usuário um valor total/soma para conferência no final desta coluna. Para isso utilizaremos um GridView onde será exibido o conteúdo de uma coluna da Base de Dados e a sua somatória.

A solução para isso é utilizar o evento RowDataBound do controle GridView, evento qual é disparado a cada linha que está sendo criada pelo controle. Antes de mais nada, depois de criado o GridView no WebForm, devemos habilitar o mesmo para exibir a seção Footer, marcando a propriedade Show Footer através da janela de propriedades do controle.

Depois destes passos devemos criar no CodeBehind um membro do tipo Decimal chamado _valorTotal, que será responsável por armazenar a soma do valor de cada registro no evento RowDataBound. Abaixo o código do evento RowDataBound que ilustrará o processo de contabilização:

C#

private decimal _valorTotal;

...

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
        this._valorTotal +=
            ((DbDataRecord)e.Row.DataItem).GetDecimal(0);
    else if (e.Row.RowType == DataControlRowType.Footer)
        e.Row.Cells[0].Text = "Total: " + this._valorTotal.ToString("C2");
}

VB.NET

Private _valorTotal As Decimal;

...

Protected Sub GridView1_RowDataBound(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
    Handles GridView1.RowDataBound

    If e.Row.RowType = DataControlRowType.DataRow Then
        Me._valorTotal += DirectCast(e.Row.DataItem, DbDataRecord).GetDecimal(0);
    ElseIf e.Row.RowType = DataControlRowType.Footer Then
        e.Row.Cells(0).Text = "Total: " & Me._valorTotal.ToString("C2")
    End If
End Sub

Analisando o código acima podemos ver que é utilizada uma condicional para verificar qual o tipo da linha/seção. Se ela for um tipo DataRow sabemos que é uma linha de exibição de dados normal e sendo assim, apenas atribuímos o valor para o membro _valorTotal. Agora se a linha for do tipo Footer devemos exibir a soma desta coluna para o usuário. Como nesse ponto já passamos por todos os registros da página corrente do GridView, nos resta apenas atribuir o valor do membro _valorTotal na seção Footer do GridView.

Para exibir o valor ao usuário definimos o membro _valorTotal à célula 0 (zero) da seção Footer do controle GridView. Para uma melhor visualização é interessante definir a propriedade HorizontalAlign para Right da coluna que deseja somar pois, assim, os valores ficam alinhados à direita, o que é padrão em valores numéricos.

Observação: O objeto GridViewRowEventArgs que vem como parâmetro no evento RowDataBound contém as informações completas da linha em questão, ou seja, como este evento é disparado a cada linha que é criada dentro do controle GridView, ele nos fornece informações como o tipo da linha, os valores provenientes da fonte de dados que serão exibidos, os controles filhos de cada linha, entre diversas outras propriedades.

A imagem abaixo exibe o controle GridView já com a coluna sumarizada e o valor desta soma sendo exibido na seção Footer do mesmo:

Cc518055.ExplorandoGridView22(pt-br,MSDN.10).png
Figura 22 - GridView com a coluna sumarizada.

Exibindo Imagens

Há ocasiões onde na aplicação precisamos exibir na tela uma listagem de registros que contenham uma coluna que é uma imagem. Anteriormente tínhamos que criar uma coluna do tipo TemplateColumn e colocar dentro desta um controle Image para ser possível apresentá-la.

Agora com o controle GridView, as coisas ficaram mais fáceis, pois foi acrescentado à este controle um tipo de coluna específico para imagens, chamada ImageField. É através desta que informamos a coluna/propriedade da fonte de dados que contém o path/nome da imagem à ser exibida ao usuário.

Esse tipo de coluna possui duas propriedades principais, que já vimos na seção Tipos de Colunas e que também estão listadas abaixo:

Propriedade

Descrição

DataImageUrlField

Define a coluna da fonte de dados que contém o Path da foto a ser exibida.

DataImageUrlFormatString

Contém a formatação da URL para a imagem a ser exibida.

Para exibirmos as imagem no controle GridView basta incluirmos uma coluna do tipo ImageField no mesmo e configurar as propriedades acima. O código HTML na página ASPX abaixo ilustra essa coluna já configurada:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT [Nome], [Foto] FROM [Usuario]">
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    DataSourceID="SqlDataSource1" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID">
    <Columns>
        <asp:ImageField 
            DataImageUrlField="Foto" 
            DataImageUrlFormatString="~/Images/{0}">
        </asp:ImageField>
    </Columns>
</asp:GridView>

Cc518055.ExplorandoGridView23(pt-br,MSDN.10).png
Figura 23 - Configurando a coluna ImageField.

Quando a página é renderizada, o ASP.NET automaticamente substitui o {0} pelo nome da imagem, que está definida na propriedade DataImageUrlField. A imagem abaixo exibe o controle GridView sendo exibido com as imagens de cada registro:

Cc518055.ExplorandoGridView24(pt-br,MSDN.10).png
Figura 24 - GridView com a coluna do tipo ImageField.

Há situações onde na base de dados não temos o nome/path da imagem, mas sim uma coluna do tipo binária que armazena a imagem em si. Neste caso o ideal é criarmos uma classe que implementa a interface IHttpHandler, que processa individualmente uma URL (extensão do arquivo solicitado) da aplicação. Como isso foge do escopo deste artigo, deixo aqui apenas uma referência à um artigo do Dino Esposito onde ele explica detalhadamente este processo.

Quando utilizamos este Handler para gerar a imagem, a configuração da propriedade DataImageUrlFormatString do controle GridView muda, ou seja, devemos ali definir o nome do arquivo que será responsável por processar e exibir o conteúdo binário da base de dados. Abaixo é apresentado o controle GridView configurado para utilizar este Handler:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT [Nome], [Foto] FROM [Usuario]">
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
        <asp:ImageField 
            DataImageUrlField="ID" 
            DataImageUrlFormatString="ViewImage.ashx?Image={0}"
            HeaderText="Imagem">
        </asp:ImageField>
    </Columns>
</asp:GridView>

A extensão *.ashx é denominada como um WebHandler, que é também um novo recurso do ASP.NET 2.0. Este nada mais é que uma classe que implementa a Interface IHttpHandler para poder customizar uma requisição de acordo com a nossa necessidade, mas com uma grande vantagem: não há necessidade de mapear uma determinada extensão de arquivo para o aspnet_isapi.dll dentro do IIS como era feito antes, pois o WebHandler já está devidamente registrado. No exemplo acima é passado para o WebHandler o ID do registro à ser recuperado e, conseqüentemente, resgatamos a coluna binária da tabela e exibimos ao usuário.

Exibindo Detalhes de um Registro

Além do controle GridView o ASP.NET 2.0 nos traz um outro grande controle para trabalharmos em conjunto com o GridView: o controle DetailsView. Este, por sua vez, exibe as colunas de um determinado registro/linha do GridView.

Um cenário bastante comum para o uso deste é quando no controle GridView só exibimos algumas colunas da nossa fonte de dados, justamente por questões de limitação no layout. Com isso, o controle DetailsView vem nos auxiliar, onde definimos à ele uma fonte de dados e o mesmo apresentará um determinado registro e suas respectivas colunas, detalhando o registro por completo.

O controle DetailsView também é flexível como o GridView à nível de customização, ou seja, temos a propriedade AutoGenerateRows que automaticamente, gera as linhas de acordo com as colunas que selecionamos na Query T-SQL do objeto SqlDataSource, ou de acordo com as propriedades da coleção em caso de estarmos trabalhando com o objeto ObjectDataSouce. Além desta propriedade ainda temos a Smart Tag semelhante ao controle GridView, mas agora temos a propriedade Edit Fields..., onde podemos customizar as linhas à serem exibidas, definindo os estilos, formatos, etc. A imagem abaixo ilustra a Smart Tag do controle DetailsView:

Cc518055.ExplorandoGridView25(pt-br,MSDN.10).png
Figura 25 - Smart Tag do controle DetailsView.

Para exemplificar o uso do controle DetailsView em conjunto com o GridView teremos o seguinte cenário: no controle GridView listaremos apenas as colunas Nome e Email da fonte de dados e, quando o usuário clicar em algum dos registros que estão dentro do controle GridView, o detalhamento deste registro será exibido no controle DetailsView que estará logo abaixo.

Além de definirmos as colunas à serem exibidas no controle GridView teremos que adicionar um coluna do tipo CommandField com um comando de Select, justamente para quando o usuário clicar neste botão os detalhes do mesmo sejam exibidos no controle DetailsView. A figura abaixo ilustra como adicionar uma coluna deste tipo no controle GridView:

Cc518055.ExplorandoGridView26(pt-br,MSDN.10).png
Figura 26 - Definindo a coluna do tipo commandField no controle GridView.

Depois do comando Select já adicionado no controle GridView e as colunas já exibidas no mesmo, adicionamos mais um controle SqlDataSource onde, neste, configuramos para retornar todas as colunas da fonte de dados (inclusive as que não estão visíveis no GridView) e, assim, termos um detalhamento do registro solicitado pelo usuário através do clique no botão Select no GridView. O código abaixo ilustra a configurações desses controles:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT UsuarioID, Nome, Email FROM Usuario">
</asp:SqlDataSource>    

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID"
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:CommandField ShowSelectButton="True" />
        <asp:BoundField DataField="Nome" HeaderText="Nome" />
        <asp:BoundField DataField="Email" HeaderText="Email" />
    </Columns>
</asp:GridView>

<asp:SqlDataSource 
    ID="SqlDataSource2" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>" 
    SelectCommand="SELECT Nome, Email, UsuarioID, Endereco, Cidade, Estado FROM Usuario"
    FilterExpression="UsuarioID={0}">
    <FilterParameters>
        <asp:ControlParameter 
            ControlID="GridView1" 
            PropertyName="SelectedValue" 
            Name="UsuarioID" 
            DefaultValue="-1"
            Type="Int32" />
    </FilterParameters>        
</asp:SqlDataSource>

<asp:DetailsView 
    ID="DetailsView1" 
    runat="server" 
    AutoGenerateRows="False" 
    DataSourceID="SqlDataSource2" 
    Height="50px" 
    Width="125px">
    <Fields>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" />
        <asp:BoundField 
            DataField="UsuarioID" 
            HeaderText="UsuarioID" 
            InsertVisible="False"
            ReadOnly="True" />
        <asp:BoundField
            DataField="Endereco" 
            HeaderText="Endereco" />
        <asp:BoundField 
            DataField="Cidade" 
            HeaderText="Cidade" />
        <asp:BoundField 
            DataField="Estado" 
            HeaderText="Estado" />
    </Fields>
</asp:DetailsView>

Analisando o código acima vemos algo bem semelhante ao que vimos nos capítulos anteriores onde abordamos a configuração do objeto SqlDataSource no controle GridView. Vemos no controle GridView GridView1 uma coluna do tipo CommandField que define a propriedade ShowSelectButton como True. Vendo um pouco mais abaixo temos mais um objeto do tipo SqlDataSource que define em sua propriedade SelectCommand uma query T-SQL que retorna as colunas (propriedades no caso de um objeto do tipo ObjectDataSource) da fonte de dados para termos o detalhamento de um determinado registro.

Existe uma propriedade no objeto SqlDataSource chamada FilterExpression onde definimos um critério para ser aplicado no SelectCommand e assim filtrarmos o resultado. Até então apenas criamos o critério para efetuar o filtro, tendo agora que definir um valor para ele. Para isso temos uma seção no objeto SqlDataSource chamada FilterParameters e, utilizando um parâmetro do tipo ControlParameter, recuperamos o valor selecionado pelo usuário no comando Select do GridView e passamos para o objeto SqlDataSource SqlDataSource2 para efetuar o filtro e popular o controle DetailsView logo abaixo.

Ainda explorando o ControlParameter definimos como a propriedade ID o ID do controle GridView, que é justamente onde o usuário clicará para obter o detalhamento do registro. Já na propriedade PropertyName definimos o valor "SelectedValue" que corresponde a propriedade SelectedValue do controle GridView, e retorna o valor do registro selecionado pelo usuário. Como já vimos, a propriedade Name é responsável por armazenar o nome do parâmetro (que neste caso é definido na propriedade FilterExpression) e, finalmente, a propriedade DefaultValue onde informamos um valor padrão para quando não tivermos um registro selecionado no controle GridView (Exemplo: Load da página.).

A imagem abaixo exibe os controles já em funcionamento:

Cc518055.ExplorandoGridView27(pt-br,MSDN.10).png
Figura 27 - Os controles GridView e DetailsView em funcionamento.

Observação: Apesar dos exemplos deste artigo serem voltados para o objeto SqlDataSource, a configuração para o objeto ObjectDataSource é da mesma forma onde, ao invés de definirmos a propriedade SelectCommand, temos que definir o método de seleção através da propriedade SelectMethod do objeto ObjectDataSource.

Editando Registros

Como não poderia ser diferente, o controle GridView também permite a edição de registros "in-place", ou seja, no mesmo local que exibimos estes registros podemos editá-los e gravar este novo valor na base de dados (ou qualquer outro repositório) e o mais interessante: sem escrever nenhuma linha de código, ao contrário do controle DataGrid do ASP.NET 1.x.

A facilidade que este controle fornece para criarmos a edição de registros é bastante simples pois, além dos Wizards, temos ainda as Smart Tags que habilitam ao desenvolvedor, através de poucos cliques, deixar isso em funcionamento. Veremos abaixo a forma de como habilitar a edição de registros "in-place" utilizando os objetos SqlDataSource e o ObjectDataSource.

Utilizando o objeto SqlDataSource

Primeiramente devemos definir o objeto SqlDataSource, que será o intermediário entre o controle GridView e a fonte de dados SQL Server. É através dele que vamos configurar as colunas à serem exibidas e editadas no controle GridView. Como já vimos isso nos capítulos anteriores, vamos pular esta parte, mas apenas chamo a atenção para o momento que definir as colunas onde, haverá um botão chamado Advanced... , o qual deveremos clicar. Com isso uma janela será exibida e devemos marcar a opção "Generate INSERT, UPDATE and DELETE statements" que com isso os comandos de Insert, Update e Delete serão automaticamente gerados baseando-se nas colunas que selecionamos no SelectCommand. A imagem abaixo ilustra este processo:

Cc518055.ExplorandoGridView28(pt-br,MSDN.10).png
Figura 28 - Definindo os comandos de CRUD.

Feito isso temos que vincular esta fonte de dados ao controle GridView através da propriedade DataSourceID. Com este vínculo podemos então definir as colunas que desejamos exibir no controle GridView e, além disso, devemos adicionar uma coluna do tipo CommandField ou apenas marcando a opção Enable Editing na Smart Tag. Através das imagens abaixo vemos estes dois processos respectivamente:

Cc518055.ExplorandoGridView29(pt-br,MSDN.10).png
Figura 29 - Definindo a coluna do tipo CommandField.

Cc518055.ExplorandoGridView30(pt-br,MSDN.10).png
Figura 30 - Habilitando a edição de registros através da propriedade Enable Editing da Smart Tag.

O código HTML gerado com essas configurações na página ASPX está sendo exibido abaixo:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT Email, Nome, UsuarioID FROM Usuario"
    UpdateCommand="UPDATE Usuario SET Email = @Email, Nome = @Nome 
        WHERE UsuarioID = @original_UsuarioID">
    <UpdateParameters>
        <asp:Parameter Name="Email" Type="String" />
        <asp:Parameter Name="Nome" Type="String" />
        <asp:Parameter Name="original_UsuarioID" Type="Int32" />
    </UpdateParameters>
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID"
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:CommandField 
            ShowEditButton="True" />
        <asp:BoundField 
            DataField="UsuarioID"
            HeaderText="UsuarioID" 
            ReadOnly="True" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" />
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
    </Columns>
</asp:GridView>

Analisando o código acima vemos o que já descrevemos: a vinculação do objeto de Data Source ao controle GridView. A única propriedade que vemos neste código e que ainda não nos deparamos é a propriedade ReadOnly. Esta é utilizada quando não queremos habilitar um registro que é exibido no controle GridView para edição. Um caso comum é quando temos em nosso GridView o ID ou um código de registro em que não devemos alterá-lo. Ao definí-la para True, a coluna é exibida apenas em forma de Label, mesmo a linha em formato de edição, assim como podemos ver a imagem abaixo:

Cc518055.ExplorandoGridView31(pt-br,MSDN.10).png
Figura 31 - A edição do controle GridView em funcionamento.

Observação: A propriedade DataKeyNames, que no caso deste exemplo está definida como UsuarioID, ou seja, é o ID do registro, faz se necessário, pois o ASP.NET utiliza esta propriedade para recuperar o ID do registro corrente que está sendo editado e passa este valor para o parâmetro original_UsuarioID (qual vamos analisá-lo mais adiante, em um artigo posterior) e, conseqüentemente, o comando responsável pela atualização (UpdateCommand) utilizará este valor para persistir as alterações na base de dados.

Utilizando o objeto ObjectDataSource

Como a forma de definirmos as colunas é independente da fonte de dados que estamos utilizando, apenas vamos nos restringir a falar de como configurar os métodos a serem utilizados para este processo, já que o restante é da mesma forma que utilizamos com o objeto SqlDataSource.

Devemos primeiramente criar um método responsável pela atualização dos dados na fonte de dados que, por sua vez, receberá os parâmetros necessários para efetuar tal atualização. No cenário que vamos utilizar, este método receberá três parâmetros: nome, email e ID (identificação do registro como único) e o método se chamará Update. Abaixo é exibido como criar este método na classe de negócios:

C#

public void Update(string nome, string email, int id) {
    SqlConnection conn = new
        SqlConnection(
            WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString);

    SqlCommand cmd = 
        new SqlCommand("UPDATE Usuario SET Nome = @Nome, Email = @Email 
            WHERE UsuarioID = @ID", conn);

    cmd.Parameters.AddWithValue("@Nome", nome);
    cmd.Parameters.AddWithValue("@Email", email);
    cmd.Parameters.AddWithValue("@ID", id);

    try
    {
        conn.Open();
        cmd.ExecuteNonQuery();
    }
    finally {
        if (conn != null) {
            conn.Close();
        }
    }
}

VB.NET

Public Sub Update(ByVal nome As String, ByVal email As String, ByVal id As Integer)
    Dim conn As New _
        SqlConnection(
            WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString)

    Dim cmd As New _
        SqlCommand("UPDATE Usuario SET Nome = @Nome, Email = @Email
            WHERE UsuarioID = @ID", conn)

    cmd.Parameters.AddWithValue("@Nome", nome)
    cmd.Parameters.AddWithValue("@Email", email)
    cmd.Parameters.AddWithValue("@ID", id)

    Try
        conn.Open()
        cmd.ExecuteNonQuery()
    Finally
        If Not IsNothing(conn) Then
            conn.Close()
        End If
    End Try
End Sub

Analisando o código acima vemos que é criado um objeto que é responsável pela conexão com a fonte de dados e outro objeto em qual definimos a query T-SQL a ser executada (neste caso, uma query de Update). Definidos estes objetos, utilizamos o método AddWithValue da coleção de parâmetros do objeto SqlCommand para adicionarmos os parâmetros necessários para executarmos a query de atualização. Finalmente abrimos a conexão com a fonte de dados, invocamos o método ExecuteNonQuery para executar o comando e posteriormente fechamos a conexão com a fonte de dados.

Depois do método codificado corretamente na classe que contém as regras de negócios devemos então, neste momento, configurar o objeto ObjectDataSource onde, na propriedade UpdateMethod, definiremos este método e seus respectivos parâmetros. O código ASPX abaixo mostra este configuração:

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    SelectMethod="ResgataUsuarios"
    TypeName="Usuarios" 
    UpdateMethod="Update">
    <UpdateParameters>
        <asp:Parameter Name="nome" Type="String" />
        <asp:Parameter Name="email" Type="String" />
    </UpdateParameters>
</asp:ObjectDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataSourceID="ObjectDataSource1">
    <Columns>
        <asp:CommandField ShowEditButton="True" />
        <asp:BoundField 
            DataField="ID" 
            HeaderText="ID" />
        <asp:BoundField 
            DataField="Email" 
            HeaderText="Email" />
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
    </Columns>
</asp:GridView>

Analisando o código superior vemos todos os parâmetros que definimos na assinatura do método Update configurados na seção UpdateParameters que, automaticamente, o ASP.NET define seus valores e invoca o método Update quando o usuário clicar no botão de atualização.

Muitas vezes o método de atualização não espera tipos de dados simples como vimos acima, ou seja, o mesmo receberá ao invés de strings contendo os valores como por exemplo nome, email, etc., ele receberá um objeto complexo do tipo Usuario, que contém suas propriedades. Neste cenário devemos modificar o código do método de atualização para receber este tipo de objeto e também ajustar a propriedade DataObjectTypeName no controle ObjectDataSource que é onde definimos uma string contendo o tipo de objeto que este controle irá trabalhar. Para exemplificar melhor isso, veremos abaixo o código HTML da página ASPX e o método responsável pela atualização, recebendo em seu parâmetro um tipo complexo:

ASPX

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    SelectMethod="ResgataUsuarios"
    TypeName="Usuarios" 
    DataObjectTypeName="Usuario" 
    UpdateMethod="Update">
</asp:ObjectDataSource>

C#

public void Update(Usuario u) {
    SqlConnection conn = new
        SqlConnection(
            WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString);

    SqlCommand cmd = 
        new SqlCommand("UPDATE Usuario SET Nome = @Nome, Email = @Email 
            WHERE UsuarioID = @ID", conn);

    cmd.Parameters.AddWithValue("@Nome", u.Nome);
    cmd.Parameters.AddWithValue("@Email", u.Email);
    cmd.Parameters.AddWithValue("@ID", u.ID);

    try
    {
        conn.Open();
        cmd.ExecuteNonQuery();
    }
    finally {
        if (conn != null) {
            conn.Close();
        }
    }
}

VB.NET

Public Sub Update(u As Usuario)
    Dim conn As New _
        SqlConnection(
            WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString)

    Dim cmd As New _
        SqlCommand("UPDATE Usuario SET Nome = @Nome, Email = @Email
            WHERE UsuarioID = @ID", conn)

    cmd.Parameters.AddWithValue("@Nome", u.Nome)
    cmd.Parameters.AddWithValue("@Email", u.Email)
    cmd.Parameters.AddWithValue("@ID", u.ID)

    Try
        conn.Open()
        cmd.ExecuteNonQuery()
    Finally
        If Not IsNothing(conn) Then
            conn.Close()
        End If
    End Try
End Sub

É importante dizer que quando utilizamos o objeto complexo, no caso Usuario, deverá obrigatoriamente ter um construtor público sem parâmetros, as propriedades deverão ser estar definidas como públicas e com o mesmo nome que são passadas para o GridView e, as propriedades devem aceitar leitura e escrita (Get e Set).

Excluindo Registros

O controle GridView disponibiliza também a exclusão de registros dos registros que estão sendo exibidos nele de forma bem simples. Vale lembrar que neste caso, se o controle GridView estiver ligado à algum objeto de DataSource a exclusão já será refletida diretamente em seu repositório.

Nesta seção vamos analisar como efetuar a exclusão de dados utilizando os dois objetos que utilizamos no decorrer deste artigo: SqlDataSource e o ObjectDataSource.

Utilizando o objeto SqlDataSource

Assim como já vimos anteriormente, temos que definir a propriedade SelectCommand através da Smart Tag que irá retornar os dados à serem apresentados no controle GridView. Nesta mesma seção já definimos a propriedade DeleteCommand com o código T-SQL responsável pela exclusão. Para automatizar clique no botão Advanced... e marque a opção "Generate INSERT, UPDATE and DELETE statements", assim como é mostrado na imagem abaixo:

Cc518055.ExplorandoGridView32(pt-br,MSDN.10).png
Figura 32 - Definindo a propriedade DeleteCommand.

Depois da propriedade DeleteCommand definida temos, neste momento, que incluir no controle GridView a coluna responsável pela exclusão do registro, ou seja, a inclusão de uma coluna com LinkButtons que executará este processo. A imagem abaixo exibe a inclusão de uma coluna do tipo CommandField para Delete.

Cc518055.ExplorandoGridView33(pt-br,MSDN.10).png
Figura 33 - Incluindo a coluna do tipo CommandField no controle GridView.

Depois destas configurações efetuadas o código HTML da página ASPX resultante é exibido abaixo:

<asp:SqlDataSource 
    ID="SqlDataSource1" 
    runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnString %>"
    SelectCommand="SELECT UsuarioID, Nome, Email FROM Usuario"
    DeleteCommand="DELETE FROM Usuario WHERE UsuarioID = @original_UsuarioID">
</asp:SqlDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID"
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" />
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
        <asp:BoundField 
            DataField="UsuarioID" 
            HeaderText="UsuarioID" 
            ReadOnly="True" />
    </Columns>
</asp:GridView>

Analisando o código acima definimos a query de Delete na propriedade DeleteCommand e assim quando o usuário clicar no botão de exclusão este código T-SQL será executado. Em tempo de execução o ASP.NET define o valor do parâmetro da exclusão "@original_UsuarioID" através das DataKeys (as quais falaremos mais adiante), que neste caso é utilizado a coluna UsuarioID para identificação do registro/linha.

Depois destes passos, o controle GridView é exibido no browser da seguinte forma:

Cc518055.ExplorandoGridView34(pt-br,MSDN.10).png
Figura 34 - O controle GridView com a coluna de exclusão definida.

Habilitando a confirmação antes da exclusão

Muitas vezes, antes de efetuarmos definitivamente a exclusão na base de dados, queremos certificar de que o usuário realmente tem certeza de que deseja fazer isso. A solução mais comum neste caso é utilizar Javascript para exibir um aviso questionando o usuário; se ele confirmar, a exclusão é realizada, caso contrário nada acontece.

Primeiramente temos que converter a coluna Delete em Template (não abordaremos isso a fundo agora justamente porque teremos um capítulo específico para explicar esta funcionalidade) como é exibido na imagem abaixo:

Cc518055.ExplorandoGridView35(pt-br,MSDN.10).png
Figura 35 - Convertendo a coluna Delete em TemplateColumn.

Para efetuar esta conversão temos apenas que clicar no link (que está marcado em vermelho) chamado "Convert this field into a TemplateField". Depois desta conversão realizada temos que criar a função Javascript na página ASPX para ser invocada quando o usuário tentar excluir o registro. O código abaixo é a função Javascript que utilizaremos posteriormente:

<script language=javascript>
    function ConfirmaExclusao(){
        return confirm('Deseja realmente excluir este registro?');
    }
</script>

Em seguida temos apenas que vincular esta função Javascript ao controle LinkButton, que é responsável por disparar a rotina de exclusão. Devemos definir a propriedade OnClientClick deste controle com o nome da função Javascript e, para isso, a imagem abaixo exibi-nos como fazer para alcançar esta propriedade de um controle que está contido dentro de uma TemplateColumn do GridView:

Cc518055.ExplorandoGridView36(pt-br,MSDN.10).png
Figura 36 - Editando a TemplateColumn.

Finalmente temos o código HTML da página ASPX gerado por todas essas configurações:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="UsuarioID"
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:TemplateField ShowHeader="False">
            <ItemTemplate>
                <asp:LinkButton 
                    ID="LinkButton1" 
                    runat="server" 
                    CausesValidation="False" 
                    CommandName="Delete"
                    Text="Delete" 
                    OnClientClick="javascript:return ConfirmaExclusao();">
                </asp:LinkButton>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField 
            DataField="Nome" 
            HeaderText="Nome" />
        <asp:BoundField 
            DataField="UsuarioID" 
            HeaderText="UsuarioID" 
            ReadOnly="True" />
    </Columns>
</asp:GridView>

Ao executar a aplicação, o resultado é mostrado como na imagem abaixo:

Cc518055.ExplorandoGridView37(pt-br,MSDN.10).png
Figura 37 - Confirmando a exclusão de um registro/linha.

Utilizando o objeto ObjectDataSource

A utilização do objeto ObjectDataSource é bem semelhante ao que vimos anteriormente com o objeto SqlDataSource. Temos apenas que nos preocupar com a criação de um método na classe de negócios que será responsável pela exclusão do registro. Este método terá apenas um parâmetro que é o ID do registro/linha que queremos excluir. O código deste método é exibido abaixo:

C#

public void Delete(int original_ID)
{
    SqlConnection conn =
        new SqlConnection(
            WebConfigurationManager.ConnectionStrings["ConnString"].ConnectionString);
    SqlCommand cmd =
        new SqlCommand("DELETE FROM Usuario WHERE UsuarioID = @ID", conn);

    cmd.Parameters.AddWithValue("@ID", original_ID);

    try
    {
        conn.Open();
        cmd.ExecuteNonQuery();
    }
    finally
    {
        if (conn != null)
        {
            conn.Close();
        }
    }
}

VB.NET

Public Sub Delete(ByVal original_ID As Integer)
    Dim conn As New _
        SqlConnection(
            WebConfigurationManager.ConnectionStrings("ConnString").ConnectionString)
    Dim cmd As New _
        SqlCommand("DELETE FROM Usuario WHERE UsuarioID = @ID", conn)

    cmd.Parameters.AddWithValue("@ID", original_ID)

    Try
        conn.Open()
        cmd.ExecuteNonQuery()
    Finally
        If Not IsNothing(conn) Then
            conn.Close()
        End If
    End Try
End Sub

Se compararmos este código com o código de atualização veremos que é bem semelhante, apenas mudando a query que é executada e a quantidade de parâmetros necessários. Depois deste método criado devemos definir a propriedade DeleteMethod do objeto ObjectDataSource com este método. O código HTML da página ASPX é exibido abaixo:

<asp:ObjectDataSource 
    ID="ObjectDataSource1" 
    runat="server" 
    DeleteMethod="Delete"
    SelectMethod="ResgataUsuarios" 
    TypeName="Usuarios">
</asp:ObjectDataSource>

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataSourceID="ObjectDataSource1" 
    DataKeyNames="ID">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" />
        <asp:BoundField DataField="ID" HeaderText="ID" />
        <asp:BoundField DataField="Nome" HeaderText="Nome" />
    </Columns>
</asp:GridView>

Com este código já é possível executar a exclusão de registros de uma determinada fonte de dados através de uma classe de regras de negócio. A única observação importante é que aqui também não vemos a seção DeleteParameters com o parâmetro necessário para a exclusão definido. Isso tem a ver com a propriedade DataKeys do controle GridView, a qual vamos abordar mais detalhadamente em capítulos posteriores.

Utilizando Template Columns

A utilização de colunas do tipo Template é freqüentemente utilizada quando as colunas que já existem por padrão no controle GridView não atendem a nossa necessidade. Através delas conseguimos customizar o controle GridView, adicionando um coluna e podendo manipulá-la do jeito desejado para atingir o objetivo.

Podemos formatar, incluir controles, podendo até incluir um outro controle GridView dentro da coluna do tipo TemplateColumn. Para incluir uma TemplateColumn basta adicionar uma nova coluna do tipo TemplateField, conforme é exibido na imagem abaixo:

Cc518055.ExplorandoGridView38(pt-br,MSDN.10).png
Figura 38 - Adicionando uma coluna do tipo TemplateField.

Com esta coluna adicionada no controle GridView podemos, através da Smart Tag, editar esta coluna e assim customizar de acordo com a necessidade. O cenário que utilizaremos é incluir em cada linha do controle GridView que contém os registros provenientes de uma DataSource adicionando um controle DropDownList com alguns dos estados do Brasil. Vejamos abaixo o passo à passo para realizar esta tarefa:

Cc518055.ExplorandoGridView39(pt-br,MSDN.10).png
Figura 39 - Editando a coluna TemplateField.

Cc518055.ExplorandoGridView40(pt-br,MSDN.10).png
Figura 40 - Selecionando a seção que queremos customizar.

Cc518055.ExplorandoGridView41(pt-br,MSDN.10).png
Figura 41 - Adicionando o controle DropDownList.

Quando temos uma coluna do tipo TemplateField no controle GridView, esta por sua vez, tem várias seções, justamente para customizar cada uma delas. A tabela abaixo mostra algumas destas principais seções:

Seção

Descrição

HeaderTemplate

É a seção do cabeçalho.

ItemTemplate

É a seção que define os itens que serão exibidos.

EditItemTemplate

É a seção GridView quando este estiver em modo de edição.

FooterTemplate

É a seção do rodapé.

Para o cenário do exemplo deste artigo utilizamos a inserção manual de itens dentro do controle DropDownList, mas é importante salientar que é perfeitamente possível a vinculação deste à um objeto de DataSource. O código HTML da página ASPX resultante desta configuração é exibido abaixo:

<asp:GridView 
    ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataSourceID="SqlDataSource1">
    <Columns>
        <asp:BoundField DataField="Nome" HeaderText="Nome" />
        <asp:TemplateField>
            <ItemTemplate>
                <asp:DropDownList ID="DropDownList1" runat="server">
                    <asp:ListItem>SP</asp:ListItem>
                    <asp:ListItem>MT</asp:ListItem>
                    <asp:ListItem>RJ</asp:ListItem>
                </asp:DropDownList>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Finalmente, depois dessas configurações efetuadas no controle GridView, o resultado do browser é apresentado abaixo:

Cc518055.ExplorandoGridView42(pt-br,MSDN.10).png
Figura 42 - A coluna TemplateField em funcionamento

A quantidade de colunas Templates que podemos ter no controle GridView é indefinida. Apenas devemos utilizá-las quando nenhuma das colunas existentes por padrão no controle GridView não antender as necessidades. Vale lembrar que no ASP.NET 1.x, algumas das colunas, como por exemplo a ImageField e CheckBoxField, era necessário a utilização de colunas Templates, que hoje já não precisamos mais.

Outras Funcionalidades

DataKeys

Nas versões anteriores do ASP.NET poderia ser definida na propriedade DataKeyField apenas uma string contendo o nome de uma coluna ou propriedade da fonte de dados para ser a identificação da linha, ou seja, torná-la única dentro do controle DataGrid. Mas, em alguns casos, isso é limitado, pois necessitamos ter mais de uma coluna/propriedade para definir a linha como única, vide chaves compostas. Com esta necessidade o controle GridView possibilita definir um array de strings contendo o nome da colunas ou propriedades que farão parte da identificação da linha.

Esta propriedade é chamada de DataKeyNames. É nela que definimos as colunas/propriedades separadas por uma vírgula. O código HTML da página ASPX que demonstra esta definição é exibido abaixo:

<asp:GridView .... DataKeyNames="ID, AnotherID">

C#

DataKey keys = this.GridView1.DataKeys[N];
foreach(DictionaryEntry d in keys.Values){
    Response.Write(d.Key.ToString() + ": " + d.Value.ToString() + "<BR>");
}

VB.NET

Dim keys As DataKey = Me.GridView1.DataKeys(N)
For Each d As DictionaryEntry In keys.Values
    Response.Write(d.Key.ToString() & ": " & d.Value.ToString() & "<BR>")
Next

* Onde N é um número inteiro que corresponde à coluna que deseja recuperar o(s) valor(es).

Quando utilizamos os métodos de exclusão (Delete) e atualização (Update) necessitamos informar aos mesmos o valor do ID do registro para que eles possam executar corretamente o comando. Há uma propriedade nas classes de DataSource chamada OldValuesParameterFormatString. Esta propriedade armazena, por padrão, a seguinte string: "original_{0}" que, em tempo de execução o ASP.NET define o parâmetro (0 (zero)) com o nome do ID que definiremos na propriedade DataKeyNames do controle GridView. Justamente por isso que não precisamos informar estes parâmetros para o objeto (identificações - ID).

Eventos

Evento

Descrição

PageIndexChanged

Ocorre quando algum dos botões de paginação for clicado, mas depois da operação ter sido executada.

PageIndexChanging

Ocorre quando algum dos botões de paginação for clicado, mas antes da operação ser executada.

RowCancelingEdit

Ocorre quando o botão de cancelamento da edição é clicado, mas é executado antes da linha sair do modo de edição.

RowCommand

Ocorre sempre quando algum controle do tipo Button for clicado.

RowCreated

Ocorre sempre quando uma linha é criada.

RowDataBound

Ocorre sempre quando uma linha está sendo populada pelos dados.

RowDeleted

Ocorre quando o botão de Delete é clicado, mas depois da linha ser excluída.

RowDeleting

Ocorre quando o botão de Delete é clicado, mas antes da linha ser excluída.

RowEditing

Ocorre quando o botão de Edit é clicado, mas antes da linha entrar em modo de edição.

RowUpdated

Ocorre quando o botão de Update é clicado, mas depois da linha ser atualizada.

RowUpdating

Ocorre quando o botão de Update é clicado, mas antes da linha ser atualizada.

SelectedIndexChanged

Ocorre quando o botão de Select é clicado, mas depois da operação ter sido executada.

SelectedIndexChanging

Ocorre quando o botão de Select é clicado, mas antes de executar a operação.

Sorted

Ocorre quando o link de ordenação é clicado, mas depois da operação ter sido executada.

Sorting

Ocorre quando o link de ordenação é clicado, mas antes de executar a operação.

Download do Código Fonte

Como os projetos estão na mesma solução, você pode definir qual deles vai quer iniciar quando você pressionar Ctrl + F5. Independente de qual dos projetos você escolha, uma página Default.aspx será visualizada no browser com o índice do artigo (Visual Basic .NET | Visual C#). Vale lembrar também que talvez precise ajustar o path do arquivo *.mdf (base de dados), pois isso depende do local onde você executa o projeto.

Considerações Finais: Utilizamos no decorrer desta série de artigos várias características do ASP.NET 2.0, mas que não entramos muito à fundo justamente por estar fora do escopo do mesmo. Coisas como Generics, Nullables Types, Log de excessões, entre muitas outras formas de gerar e manipular estes dados que foram deixadas de lado ou não foram utilizadas. Mas isso fica à cargo do leitor melhorar isso, tendo uma melhor performance e um código "mais seguro" e confiável.

Conclusão: Ao longo desta série de artigos vimos detalhadamente o controle GridView e suas características. Abordamos também controles que estão bem próximos ao GridView, ou melhor, que trabalham juntos. Podemos também analisar a facilidade nas configurações da interface com o usuário quando falamos em listagem e edição de registros, coisa que, comparado ao ASP.NET 1.x, está infinitamente mais "inteligente".