Inserindo Datas no SQL Server

Umas preocupações que mais vejo quando alguém quer incluir datas em uma coluna do tipo DateTime no banco de dados SQL Server 2000 é saber qual o formato enviar. Já vi muitos casos onde o pessoal faz algo do tipo:

     Dim d As DateTime = DateTime.Now
     Dim query As String = “INSERT INTO Tabela VALUES(‘” & d.ToString(“MM/dd/yyyy”) & “‘)”

Eu particularmente também já tive essa dúvida e ao procurar saber, o segredo está em não espeficar um formato como é feito acima, deixando o SqlParameter/SqlCommand se encarregar de fazer essa trabalho por nós. Para consertar o código acima, fazendo da forma correta, temos que criar os devidos parametros para o campo (não somente do tipo data, mas qualquer um) e executar a query. Abaixo o exemplo:

     Dim cmd As New SqlCommand(“INSERT INTO Tabela VALUES(@Data)”, conn)

     Dim data As New SqlParameter(“@Data”, SqlDbType.DateTime)
     data.Value = DateTime.Now
     cmd.Parameters.Add(data)
     ‘….
     cmd.ExecuteNonQuery()

Operador ??

Dando uma olhada no Help do Visual Studio 2005 Beta 2, encontrei um novo operador dentro da linguagem C#. Trata-se do ??. Um exemplo do uso do mesmo, é mostrado abaixo:

     int x = 2;
     int? y = null;

     int z = x ?? y;
     Console.WriteLine(z.ToString());

O sintaxe retorna o valor que está definido do lado esquerdo do operador se o mesmo não for nulo. Caso contrário, o valor que está definido do lado direito do operador é retornado. O output do código acima retorna 2, pois y está definido como nulo. Se alterarmos o valor de y de nulo para algum outro valor válido (inteiro), o valor dele será atribuido à z e, consequentemente exibido.

Para quem quiser saber mais sobre Nullable Types, acesse este link.

Criando um DalHelper

Como a maioria dos sistemas atuais necessitam de Base de Dados, sempre quando precisamos acessar, inserir, alterar ou excluir um determinado registro, precisamos criar a conexão com a Base de Dados, definir a query, os parâmetros e escolher a opção para executá-la (ExecuteNonQuery, ExecuteScalar ou ExecuteReader).

Para não ficarmos fazendo a todo momento todos esses passos, ou seja, criar conexão, parâmetros, etc., explicarei nesse artigo como criar um DALHelper. DALHelper quer dizer: Data Application Layer (Camada de Dados) e Helper (Ajudante), que auxiliará no acesso aos dados, reduzindo assim a quantidade de código em nossa Aplicação.

Para isso criaremos uma Classe onde encapsularemos código ADO.NET. Só para recapitular, Encapsulamento é quando as funções internas de um determinado objeto não interessa ao utilizador do mesmo, ou seja, temos a descrição das operações que o objeto executa que é acessível ao cliente, a implementação de tais operações fica encapsulada e só é visível ao próprio objeto.

Criaremos uma Windows Application chamada DALHelper e nela criaremos também um arquivo chamado “DB.vb” onde ficará a nossa Classe chamada “DB” que será responsável pelo Acesso aos Dados.

Devemos importar as seguintes Namespaces:

Imports System.Data
Imports System.Data.SqlClient
Código 1 – Importando Namespaces necessários.

Agora criaremos a nossa Classe “DB”:

Imports System.Data
Imports System.Data.SqlClient

Public Class DB

End Class

Código 2 – Criando a Classe que controlará o acesso à Base de Dados.

Devemos declarar os seguintes membros:

Public Class DB

    Private m_connectionString As String
    Private m_manterConexao As Boolean
    Private conexao As SqlConnection

End Class

Código 3 – Declarando os membros da Classe.

A propriedade ConnectionString será responsável para receber a conexão com a Base de Dados que deverá ser informada pela Aplicação.

Public Property ConnectionString() As String
    Get
        Return Me.m_connectionString
    End Get
    Set(ByVal Value As String)
        Me.m_connectionString = Value
    End Set
End Property
Código 4 – ConnectionString.

E a propriedade ManterConexão será responsável para sabermos se após executarmos um determinada ação na Base de Dados deixaremos ou não a conexão aberta.

Public Property ManterConexao() As Boolean
    Get
        Return Me.m_manterConexao
    End Get
    Set(ByVal Value As Boolean)
        Me.m_manterConexao = Value
    End Set
End Property
Código 5 – Manter ou não a conexão aberta.

Observação: Devemos tomar muito cuidado para não deixarmos a conexão aberta desnecessariamente.

O próximo passo é criarmos os construtores da Classe. Aqui chamo a atenção para um detalhe: criaremos duas opções para os contrutores (Overload ou Sobrecarga), para que já possamos preencher as propriedades ConnectionString e ManterConexao.

Public Sub New(ByVal connectionString As String)
    Me.m_connectionString = connectionString
End Sub

Public Sub New(ByVal connectionString As String, ByVal manterConexao As Boolean)
    Me.m_connectionString = connectionString
    Me.m_manterConexao = manterConexao
End Sub

Código 6 – Construtores Sobrecarregados.

Podemos reparar que no primeiro construtor, onde há apenas um único parâmetro, que nesse caso é a String de conexão com a Base de Dados. Já no segundo construtor é passada a String de conexão e um valor Booelano que indicará se a conexão com a Base de Dados permanecerá aberta após a execução do comando.

Vamos criar uma Sub-Rotina para podermos fechar a conexão com a Base de Dados. Mas antes de chamarmos o método Close() da SqlConnection, devemos verificar se a variável “conexao” ainda encontra-se ativa.

Public Sub Fechar()
    If Not (conexao Is Nothing) Then
        conexao.Close()
    End If
End Sub
Código 7 – Sub-Rotina para fechar o acesso à Base de Dados.

Vamos iniciar pelo ExecuteNonQuery. O ExecuteNonQuery retorna o número de registros afetados pela instrução SQL.

Criaremos para o ExecuteNonQuery, ExecuteReader e ExecuteScalar dois métodos sobrecarregados (Overloads). Um deles receberá como parâmetro: o tipo do comando (CommandType, (Text ou Stored Procedure)) e o nome da Stored Procedure ou a Query SQL. E o outro além desses parâmetros, receberá um terceiro parâmetro ParamArray do tipo SqlParameter que conterá um Array com os parâmetros que serão passados para a Stored Procedure ou Query.

Nesse primeiro método do ExecuteNonQuery será apenas passado o comando (CommandText) e seu tipo (CommandType), já a Query ou Stored Procedure não necessitam parâmetros.

Public Overloads Function ExecuteNonQuery(ByVal cmdText As String, ByVal cmdType As CommandType) As Integer
    Return ExecuteNonQuery(cmdText, cmdType, Nothing)
End Function
Código 8 – Executa o ExecuteNonQuery e retorna o quantidade de registros afetados.

Agora, veremos o mesmo método (sobrecarregado) com mais um parâmetro, que como explicado acima, será um Array contendo os parâmetros que serão utilizados na Query ou Stored Procedure:

Public Overloads Function ExecuteNonQuery(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal ParamArray parameters() As SqlParameter) As Integer
    Dim cmd As SqlCommand = CriarCommando(cmdText, cmdType, parameters)
    Dim retVal As Integer
    Try
        If ManterConexao = False Then
            retVal = cmd.ExecuteNonQuery()
            Me.Fechar()
        Else
            retVal = cmd.ExecuteNonQuery()
        End If
        Return retVal
    Catch e As Exception
        Me.Fechar()
        Throw e
    End Try
End Function
Código 9 – Criamos um objeto do tipo SqlCommand que será responsável pela execução da Query ou Stored Procedure e atribuimos a ele o retorno da Função CriarCommando(), qual veremos mais adiante a sua utilidade e funcionamento. Atente-se também à condicional que é feita para verificar se devemos ou não mantermos a conexão com a Base de Dados aberta. E retornamos um inteiro que é o número de registros afetados pela instrução SQL.

OBS.: Mas o utilize somente quando a instrução SQL necessite de parâmetros.

Devemos fazer o mesmo para os que restaram: ExecuteScalar e ExecuteReader, mas temos que prestar atenção no valor retorno dos mesmos. Vamos então ver como funciona o ExecuteScalar:

Public Overloads Function ExecuteScalar(ByVal cmdText As String, ByVal cmdType As CommandType) As Object
    Return ExecuteScalar(cmdText, cmdType, Nothing)
End Function
Código 10 – Executa o ExecuteScalar e seu retorno é do tipo Object.

E a da mesma forma que fizemos com o ExecuteNonQuery quando ele necessita passar parâmetros para a Query ou Stored Procedure será feito para o ExecuteScalar:

Public Overloads Function ExecuteScalar(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal ParamArray parameters() As SqlParameter) As Object
    Dim cmd As SqlCommand = CriarCommando(cmdText, cmdType, parameters)
    Dim retVal As Object
    Try
        If ManterConexao = False Then
            retVal = cmd.ExecuteScalar()
            Me.Fechar()
        Else
            retVal = cmd.ExecuteScalar()
        End If
        Return retVal
    Catch e As Exception
        Me.Fechar()
        Throw e
    End Try
End Function
Código 11 – A única diferença além do método à ser executado, é o retorno do mesmo. Nesse quase será retornado um Object.

Agora com o ExecuteReader dos dois metódos sobrecarregados (Overloads), precisaremos de um método adicional, qual chamaremos de GetDataReader que receberá como parâmetro o SqlCommand e passará pela mesma condicional para verificar se deve ou não manter a conexão com a Base de Dados.

Public Function GetDataReader(ByVal cmd As SqlCommand) As SqlDataReader
    Try
        If ManterConexao = False Then
            Return cmd.ExecuteReader(CommandBehavior.CloseConnection)
        Else
            Return cmd.ExecuteReader()
        End If
        Return retVal
    Catch e As Exception
        Me.Fechar()
        Throw e
    End Try
End Function
Código 12 – Executa o método ExecuteReader.

 

Public Overloads Function ExecuteReader(ByVal cmdText As String, ByVal cmdType As CommandType) As SqlDataReader
    Dim cmd As SqlCommand = CriarComando(cmdText, cmdType, Nothing)
    Return GetDataReader(cmd)
End Function
Public Overloads Function ExecuteReader(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal ParamArray parameters() As SqlParameter) As SqlDataReader
    Dim cmd As SqlCommand = CriarComando(cmdText, cmdType, parameters)
    Return GetDataReader(cmd)
End Function
Código 13 – A diferença entre esses dois métodos são os parâmetros que podem ou não serem necessários para a Query ou Stored Procedure. Reparem que é feito da mesma forma que nos métodos anteriores, ou seja, é criado um SqlCommand e chamamos a função CriarComando. No retorno de ambas as funções, ele chama o método GetDataReader, passando o Command como parâmetro.

Depois de todos esses passos devemos construir o método CriarComando, que como o próprio nome diz, cria e configura um SqlCommand informando o tipo (CommandType), a Query (CommandText) e os parâmetros (SqlParameter) quando necessário.

Public Function CriarComando(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal ParamArray parameters() As SqlParameter) As SqlCommand
    Dim cmd As New SqlCommand(cmdText)
    cmd.CommandType = cmdType
    If Not (parameters Is Nothing) Then
        Dim param As SqlParameter
        For Each param In parameters
            cmd.Parameters.Add(param)
        Next param
    End If
    VerificarConexao(cmd)
    Return cmd
End Function
Código 14 – Através deste método, definimos o Comando que será executado (CommandText), o Tipo de Comando (CommandType) e em seguida verificamos se há algum parâmetro necessário. Caso exista um ou mais parâmetros, é realizado um laço For..Each atribuindo cada um à coleção de parâmetros do SqlCommand.

Podemos ver que ainda existe uma última Sub Rotina (VerificarConexao) para fecharmos essa primeira parte, que tem por finalidade verificar e/ou criar a conexão com a Base de Dados.

Public Sub VerificarConexao(ByRef cmd As SqlCommand)
    If conexao Is Nothing Then
        conexao = New SqlConnection(Me.ConnectionString)
        conexao.Open()
    Else
        If conexao.State <> ConnectionState.Open Then
            conexao.Open()
        End If
    End If
    cmd.Connection = conexao
    Return
End Sub
Código 15 – Aqui apenas fazemos a verificação com relação ao estado da conexão com a Base de Dados. Depois apenas adicionamos à propriedade Connection do objeto SqlCommand que vem como parâmetro (ByRef).

Bem, depois de tudo isso, parece ser algo completamente complicado. Mas tentarei mostrar o processo através de imagens.

1.º Caso: Abrir a conexão, executar apenas UM determinado método e fechá-la logo em seguida:

Figura 1 – Mantendo a conexão aberta durante a execução de apenas um método.

2.º Caso: Abrir a conexão, executar MAIS DE UM método e fechá-la logo em seguida:

Figura 2 – Neste caso, podemos ver que a conexão se mantém durante a execução de várias métodos.

Ainda faltam três funções que serão acessadas fora da Classe: CriarParametro, GetString e GetInt32.

A função CriarParametro servirá para que possamos criar os parâmetros que serão passados para os métodos que vimos anteriormente (ExecuteNonQuery, ExecuteScalar ou ExecuteReader). Lembrando que os parâmetros são passados como ParamArray.

Public Function CriarParametro(ByVal name As String, ByVal type As SqlDbType, ByVal value As Object) As SqlParameter
    Dim param As New SqlParameter()
    param.ParameterName = name
    param.SqlDbType = type
    If value Is Nothing Then
        param.Value = DBNull.Value
    Else
        If type = SqlDbType.VarChar And value.ToString().Length = 0 Then
            param.Value = DBNull.Value
        Else
            param.Value = value
        End If
    End If
    Return param
End Function
Código 16 – A função recebe o nome, tipo e valor do parâmetro. Trata esses dados para que eles possam ser adicionados à coleção de parâmetros do comando (SqlCommand).

Para finalizar temos apenas mais dois métodos: GetString e GetInt32. Funções quais retornarão valores em Strings e Inteiros, respectivamente do SqlDataReader. Ambas funções recebem como parâmetro o SqlDataReader e um número inteiro que representará o campo que será resgatado desse mesmo SqlDataReader:

Public Shared Function GetString(ByRef dr As SqlDataReader, ByVal ordinal As Integer) As String
    If Not dr.IsDBNull(ordinal) Then
        Return dr.GetString(ordinal)
    Else
        Return Nothing
    End If
End Function
Public Shared Function GetInt32(ByRef dr As SqlDataReader, ByVal ordinal As Integer) As Integer
    If Not dr.IsDBNull(ordinal) Then
        Return dr.GetInt32(ordinal)
    Else
        Return Nothing
    End If
End Function
Código 17 – Tanto para o método GetString quanto para o método GetInt32 é verificado se o campo solicitado é ou não nulo. Se não for, seu valor é retornado, caso contrário, não será retornado nada.

Observação: Podemos também criarmos os métodos GetBoolean, GetDateTime, etc., mas nesse artigo apenas usaremos os dois métodos citados acima.

Enfim, nosso DALHelper pronto para ser colocado em ação. Criaremos então duas Tabelas na Base de Dados para testá-lo. Abaixo a estrutura das Tabelas:

Figura 3 – Estrutura das Tabelas de Exemplo.

Temos também que criarmos um Formulário para que possamos interagir com o Usuário. Abaixo o formulário que usaremos como exemplo:

Figura 4 – Usando 2 TextBox, 1 Button e 1 ListBox.

Abaixo as Stored Procedures que utilizaremos no decorrer do nosso exemplo. Em um dos casos utilizarei o código SQL diretamente.

CREATE PROCEDURE BuscarClientes

    AS

    SELECT
        ClienteID,
        Nome + ‘ – ‘ + Email AS DadosCliente
    FROM
        Clientes
    ORDER BY
        ClienteID ASC

GO

CREATE PROCEDURE AdicionarCliente

    @Nome AS Varchar(50),
    @Email AS Varchar(50)

    AS

    INSERT INTO Clientes (Nome, Email)
        VALUES (@Nome, @Email)

GO

Código 18 – A primeira Stored Procedure retorna todos os Clientes. Já a segunda será utilizada para adicionarmos um novo cliente.

Depois de nosso DALHelper pronto, as Tabelas da Base de Dados e das Stored Procedures vamos começar a codificar o Front-End. Primeiramente incluirei mais dois WinForm ao Projeto para que possamos entender os dois casos como vimos acima.

Figura 5 – Temos dois WinForms para entendermos os dois casos.

1.º Caso – Fechar a Conexão logo após executar um determinado Comando.

Vamos analisar a Sub-Rotina CarregaListBox que será responsável por carregar os dados da Base de Dados (Tabela Clientes) no ListBox:

Private Sub CarregaListBox()
    Me.lstClientes.Items.Clear()
    Dim dalHelper As New DalHelper.DB(strConexao, False)
    Dim dr As SqlDataReader
    Try
        dr = dalHelper.ExecuteReader(“BuscarClientes”, CommandType.StoredProcedure)
        While dr.Read()
            Me.lstClientes.Items.Add(dalHelper.GetInt32(dr, 0) & ” – ” & dalHelper.GetString(dr, 1))
        End While
    Catch ex As Exception
        MessageBox.Show(“Ocorreu um erro ao tentar acessar a Base de Dados”, “Base de Dados”, MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub
Código 19 – No construtor da Classe DB passamos como parâmetro a ConnectionString que está armazenada em uma constante declarada no topo do WinForm e o segundo parâmetro diz que após efetuar o comando deve ser fechada a conexão com a Base de Dados.

Podemos ver que após instanciarmos a Classe DB, podemos utilizar seus métodos e propriedades. No código acima, criamos um SqlDataReader e atribuímos à ele o retorno da função ExecuteReader que recebe como parâmetro o Nome do Stored Procedure ou Query e o CommandType, que por sua vez também retorna um SqlDataReader. Através de um laço While percorremos o SqlDataReader e adicionamos cada registro ao ListBox. Utilizamos o GetInt32 quando o campo à ser resgatado do SqlDataReader é do tipo Inteiro, já o GetString o utilizamos quando é o campo é do tipo Texto.

No evento Click do botão Adicionar devemos incluir um novo registro no Base de Dados e logo após fecharmos a conexão com a mesma. Veja o código:

Private Sub btnCadastrar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCadastrar.Click
    Dim dalHelper As New DalHelper.DB(strConexao, False)
    Dim iRegistrosAfetados As Integer = 0
    Try
        Dim param() As SqlParameter = { _
            dalHelper.CriarParametro(“@Nome”, SqlDbType.VarChar, Me.txtNome.Text.Trim), _
            dalHelper.CriarParametro(“@Email”, SqlDbType.VarChar, Me.txtEmail.Text.Trim)}

        iRegistrosAfetados = dalHelper.ExecuteNonQuery(“AdicionarCliente”, CommandType.StoredProcedure, param)
    Catch ex As Exception
        MessageBox.Show(“Ocorreu um erro ao tentar acessar a Base de Dados”, “Base de Dados”, MessageBoxButtons.OK, MessageBoxIcon.Error)
    Finally
        If iRegistrosAfetados = 1 Then
            Call Me.CarregaListBox()
            MessageBox.Show(“Registro incluído com sucesso.”)
        Else
            MessageBox.Show(“Ocorreu um erro ao tentar incluir o registro.”)
        End If
    End Try
End Sub

Código 20 – Ao clicarmos no botão para adicionarmos um novo registro na Base de Dados, instanciamos a Classe DB e novamente informamos à ela a ConnectionString e dizemos através do segundo parâmetro que após a execução, deve ser fechada a conexão com a Base de Dados, criamos uma variável chamada param() que será um Array de SqlParameter e utilizamos o método ExecuteNonQuery.

Com o Array podemos incluir quantos parâmetros forem necessários. Basta utilizar o método CriarParametro e informar o Nome, o Tipo de Dados e o Valor, separando cada um por uma vírgula ( , ). No caso desse exemplo, os dois parâmetros são do Tipo Varchar e recebem os TextBox que estão no WinForm.

O método ExecuteNonQuery retornará o número de registros afetados pela instrução SQL. No bloco Finally verificamos se a variável iRegistrosAfetados for igual a 1 é porque o registro foi incluído com sucesso, caso contrário houve uma falha.

2.º Caso – Manter a Conexão Aberta durante a execução de vários Comandos.

Agora iremos alterar a rotina que adiciona um novo registro. Vamos manter a conexão aberta durante a executação de duas instruções SQL. Para isso, após a execução da Stored Procedure AdicionarCliente, vamos adicionar à Tabela Logs um novo registro e para isso utilizaremos a mesma conexão, apenas mudando o CommandType:

Private Sub btnCadastrar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCadastrar.Click
    Dim dalHelper As New DalHelper.DB(strConexao, True)
    Dim iRegistrosAfetados As Integer = 0
    Try
        Dim param() As SqlParameter = { _
            dalHelper.CriarParametro(“@Nome”, SqlDbType.VarChar, Me.txtNome.Text.Trim), _
            dalHelper.CriarParametro(“@Email”, SqlDbType.VarChar, Me.txtEmail.Text.Trim)}

        iRegistrosAfetados = dalHelper.ExecuteNonQuery(“AdicionarCliente”, CommandType.StoredProcedure, param)
        dalHelper.ExecuteNonQuery(“INSERT INTO Logs (Data) VALUES (GetDate())”, CommandType.Text)
    Catch ex As Exception
        MessageBox.Show(“Ocorreu um erro ao tentar acessar a Base de Dados”, “Base de Dados”, MessageBoxButtons.OK, MessageBoxIcon.Error)
    Finally
        dalHelper.Fechar()
        If iRegistrosAfetados = 1 Then
            MessageBox.Show(“Registro incluído com sucesso.”)
            Call Me.CarregaListBox()
        Else
            MessageBox.Show(“Ocorreu um erro ao tentar incluir o registro.”)
        End If
    End Try
End Sub

Código 21 – A diferença aqui se encontra no segundo parâmetro que é informado ao construtor da Classe DB, que nesse caso é True, que diz que depois de executar o comando, a conexão deve permanecer aberta.

Chamo a atenção para a linha onde temos:

        dalHelper.ExecuteNonQuery(“INSERT INTO Logs (Data) VALUES (GetDate())”, CommandType.Text)
Código 22 – Utilizando instruções SQL diretamente.

Neste caso informamos a Query diretamente, ou seja, sem Stored Procedures. O procedimento é o mesmo, apenas temos que mudar o CommandType para CommandType.Text. A criação de parâmetros continua sendo da mesma forma das Stored Procedures, ou seja, utilizando o método CriarParametro.

Um dos principais cuidados que devemos ter quando mantermos a conexão aberta, é que após finalizarmos todo o processo, devemos fechar a conexão com a Base de Dados explicitamente através do método Fechar().

Observação: Procure não deixar a String de Conexão com a Base de Dados em Hard-Code, ou seja, deixá-la juntamente com o código, pois isso implicaria em cada vez que mudar a Base de Dados/String de Conexão, recompilar a aplicação.

Conclusão: Utilizando o DALHelper escrevemos muito menos código do que quando utilizamos as declarações de SqlCommand, SqlParameter, SqlCommand, etc. normalmente, pois todos esses códigos ADO.NET já estão encapsulados dentro do DALHelper. Podemos também definir o Tipo de Comando (CommandType), manter a conexão aberta ou não após a execução de um comando, entre outras funcionalidades/facilidades.

DALHelper.zip (47.35 kb)

Explorando o GridView – 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">        					

E para recuperar os valores destas chaves utilizamos o código abaixo:

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

* 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.

GridView.zip (674.49 kb)

Explorando o GridView – 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:

Figura 1 – 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:

Figura 2 – Editando a coluna TemplateField.

 

Figura 3 – Selecionando a seção que queremos customizar.

 

Figura 4 – 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:

Figura 5 – 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.

GridView.zip (674.49 kb)

Explorando o GridView – 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:

 

Figura 1 – 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.

Figura 2 – 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:

Figura 3 – 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:

Figura 4 – 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:

    function ConfirmaExclusao(){
        return confirm('Deseja realmente excluir este registro?');
    }

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:

Figura 5 – 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:

Figura 6 – 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:

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();
        }
    }
}

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.

GridView.zip (674.49 kb)

Explorando o GridView – 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:

Figura 1 – 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:

Figura 2 – Definindo a coluna do tipo CommandField.

 

Figura 3 – 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:

Figura 4 – 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:

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();
        }
    }
}

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:

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

 

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();
        }
    }
}

É 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).

GridView.zip (674.49 kb)

Explorando o GridView – 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:

Figura 1Smart 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:

Figura 2 – 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:

Figura 3 – 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.

GridView.zip (674.49 kb)

Explorando o GridView – 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>

 

Figura 1 – 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:

Figura 2 – 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.

GridView.zip (674.49 kb)

Explorando o GridView – Sumarizando

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:

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");
}

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:

Figura 1 – GridView com a coluna sumarizada.

GridView.zip (674.49 kb)