Olá caro(a) leitor(a), hoje eu vou falar um pouco o que os desenvolvedores passam no momento de desenvolver software com o componente GridView e método RowData. O exemplo aqui é o que geralmente precisamos fazer, ou seja, é um exemplo prático do dia a dia. Eu vou falar primeiro o problema para depois mostrar a solução com código funcionando.
Utilizado:
Ferramenta de desenvolvimento: Visual Studio .NET
Linguagem de programação: C#
Componente: GridView
Plataforma ASP.NET
O problema
Eu tenho um grupo de valores vindos do banco de dados, isso é normal. Mas para cada linha do dado recebido tem mais outras várias linhas para exibir no próprio GridView. Quero dizer que, assim que os dados estiverem sendo preenchidos, de forma sincronizada, preciso que uma chave seja passada para buscar no banco outros valores de outra tabela.
Essa outra tabela do banco de dados podem ter mais de uma linha para a mesma chave. A chave 1 por exemplo, possui 20 linhas na tabela e a chave 2 possui 40 linhas. Cada linha dessa precisa ser agrupada, separada por vírgula e mostrada ao usuário na tela usando o componente GridView.
Eu tentei fazer com que o SELECT trouxesse tudo agrupado, mas não consegui devido meus poucos conhecimentos de banco de dados e instruções PL SQL. O SELECT já traz outros dados de outras tabelas através do INNER JOIN e devido a isso, não consegui agrupar ou transformar as linhas em tabelas agrupadas nesse SELECT.
Me lembrei que existe um método específico do GridView chamado RowDataBound, que para cada linha preenchida, ele pode passar valor para buscar outros dados e preencher dinamicamente a tela para o usuário.
Até então, isso é simples porque qualquer tutorial na Internet mostra isso. O problema foi preencher os dados de forma dinâmica para o usuário antes da tela aparecer. Além do mais, a página não pode demorar muito.
Solução
Como falado anteriormente, a solução foi usar o método RowDataBound, pegar a chave da linha específica, buscar no banco de dados, fazer um for para percorrer os dados e preencher uma string para mostrar na tela. Veja o código 1.1 mostrando o grid na tela página .aspx.
Código 1.1 - GridView
<asp:GridView runat="server" ID="gridPatrocinadora" AutoGenerateColumns="false" DataKeyNames="IdEmpresa" CellPadding="3" CellSpacing="3" Width="100%" GridLines="None" OnRowDataBound="gridPatrocinadora_RowDataBound">
<EmptyDataTemplate>
Nenhum link patrocinado encontrado.
EmptyDataTemplate>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<br />
<div class="vcard">
<div class="adv"><a target="_blank" href="EmpresaPatrocinadora?id=<%#Eval("IdEmpresa") %>&nome=<%# Eval("NomeFantasia") %>">
<span>em:span><span class="categoria"><%# _listaCategoria %>span><br /><span class="profissional"><%# Eval("NomeFantasia") %>span><br /><span class="street-address"><%# Eval("Endereco").Equals(" ") ? "Fones: " + Eval("Telefone") : Eval("Endereco")%>span><br /><span class="locality">Ribeirão Pretospan><br />
<span class="fone"><%# !Eval("Endereco").Equals(" ") ? "Fones: " + Eval("Telefone") : Eval("Endereco")%>span><br />
<span class="cep">CEP: <%# Eval("CEP")%>span><br />
div>a>
div><br />
ItemTemplate>
asp:TemplateField>
Columns>
asp:GridView>
Note que é um grid simples usando a tag e dentro dele os valores do banco de dados. Para exibir os valores eu posso usar o <%# Eval(“nome_da_coluna”)%> do banco de dados ou o que o SELECT está trazendo para mim.
Existe um valor chamado _listaCategoria responsável por listar todas as listas de categoria de uma determinada empresa patrocinadora. Essa lista, como falado anteriormente, pode ter 1 valor ou 500 valores. Eu preciso mostrar todos os valores.
Essa variável é local, ou seja, da página que está sendo preenchida dentro do método RowDataBound. Só que a variável não está exibindo de maneira correta os dados ao usuário. Os valores da primeira linha está na segunda e assim por diante. Se eu trocar o <%# por <%=, só valores da primeira linha aparecem em todas as linhas do GridView.
Para resolver esse problema eu tive que utilizar o Label, isso mesmo, um objeto Label dentro do GridView, buscar ele no preenchimento e depois preenchê-lo ao usuário. Veja como ficou o código 1.2.
Código 1.2 - GridView com Label
<asp:GridView runat="server" ID="gridPatrocinadora" AutoGenerateColumns="false" DataKeyNames="IdEmpresa"
CellPadding="3" CellSpacing="3" Width="100%" GridLines="None" OnRowDataBound="gridPatrocinadora_RowDataBound">
<EmptyDataTemplate>
Nenhum link patrocinado encontrado.
EmptyDataTemplate>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<br />
<div class="vcard">
<div class="adv"><a target="_blank" href="EmpresaPatrocinadora?id=<%#Eval("IdEmpresa") %>&nome=<%# Eval("NomeFantasia") %>">
<span>em:span><span class="categoria"><asp:Label ID="lblCategoria" runat="server"/>span><br /><span class="profissional"><%# Eval("NomeFantasia") %>span><br /><span class="street-address"><%# Eval("Endereco").Equals(" ") ? "Fones: " + Eval("Telefone") : Eval("Endereco")%>span><br /><span class="locality">Ribeirão Pretospan><br />
<span class="fone"><%# !Eval("Endereco").Equals(" ") ? "Fones: " + Eval("Telefone") : Eval("Endereco")%>span><br />
<span class="cep">CEP: <%# Eval("CEP")%>span><br />
div>a>
div><br />
ItemTemplate>
asp:TemplateField>
Columns>
asp:GridView>
O label chama lblCategoria e é com este nome que eu preciso trabalhar dentro do método RowDataBound. Só imaginei que poderia dar certo usando o label depois de muito tempo de teste e por isso resolvi postar aqui a solução para todos os leitores.
Para criar o método do GridView, basta selecionar o componente e na aba de propriedades, clique no botão Events. Lá existem vários métodos, escolha o RowDataBound. A versão antiga desse componente utilizava o DataBound. Mas vamos deixar a versão antiga pra lá e trabalhar com a versão nova. Veja o código 1.3.
Código 1.3 - Método RowDataBound.
protected void gridPatrocinadora_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
StringBuilder str = new StringBuilder();
Int32 _idEmpresa = int.Parse(gridPatrocinadora.DataKeys[e.Row.RowIndex].Values[0].ToString());
EmpresaCategoriaDTO _empresaCategoriaDTO = new EmpresaCategoriaDTO();
_empresaCategoriaDTO.idEmpresa = _idEmpresa;
EmpresaCategoriaBRL _empresaCategoriaBRL = new EmpresaCategoriaBRL();
DataTable dtGrid = _empresaCategoriaBRL.searchEmpresaCategoria(_empresaCategoriaDTO);
for (int i = 0; i < dtGrid.Rows.Count; i++)
{
if(i==dtGrid.Rows.Count)
str.Append(dtGrid.Rows[i]["NomeCategoria"].ToString());
else
str.Append(dtGrid.Rows[i]["NomeCategoria"].ToString() + ", ");
}
Label myLabel = e.Row.FindControl("lblCategoria") as Label;
myLabel.Text = str.ToString();
}
}
A primeira linha do código verifica se o tipo tem algum valor preenchido. Se for preenchido, o código entra no if. Depois declarei uma variável de StringBuilder que consome menos memória e pode agrupar valores pelo método Append.
Não posso deixar de buscar a chave da linha que estava sendo preenchida naquele momento, ou seja, bem dinâmico esse preenchimento. Note que existe uma propriedade no código 1.1 chamado DataKeyNames, essa propriedade é responsável por armazenar a chave da tabela ou através dela eu posso buscar a chave de cada linha preenchida.
Dessa forma, usei o nome do grid e o valor do row index para buscar a chave da empresa naquele momento que o valor era preenchido. Por isso do código gridPatrocinadora.DataKeys[e.Row.RowIndex].Values[0].ToString().
Com a chave na mão, basta agora buscar no banco de dados, através de métodos, as informações que preciso. O código 1.4 é responsável pela ida no banco, trazendo retorno através de um objeto DataTable.
Código 1.4 - Método que busca dados passando valores.
EmpresaCategoriaDTO _empresaCategoriaDTO = new EmpresaCategoriaDTO();
_empresaCategoriaDTO.idEmpresa = _idEmpresa;
EmpresaCategoriaBRL _empresaCategoriaBRL = new EmpresaCategoriaBRL();
DataTable dtGrid = _empresaCategoriaBRL.searchEmpresaCategoria(_empresaCategoriaDTO);
Como o objeto DataTable pode conter várias linhas, foi necessário criar um for para percorrer cada valor retornado para agrupar junto da StringBuilder. Veja o código 1.5 mostrando o for.
Código 1.5 - Percorrendo os dados
for (int i = 0; i < dtGrid.Rows.Count; i++)
{
if(i==dtGrid.Rows.Count)
str.Append(dtGrid.Rows[i]["NomeCategoria"].ToString());
else
str.Append(dtGrid.Rows[i]["NomeCategoria"].ToString() + ", ");
}
O único campo que preciso chama-se NomeCategoria. Para cada registro, percorro os dados e atribuo na variável str separando com vírgula no final.
O código 1.6 já mostra que, depois do for basta encontrar a label pelo nome e atribuir o valor pela propriedade Text. Só isso para funcionar. Cada linha do grid é preenchida de forma dinâmica ao usuário com informações relevantes nesse meu sistema. O código 1.6 mostra mais sobre como encontrar o label dentro do grid.
Código 1.6 - Encontrando label e atribuindo valores
Label myLabel = e.Row.FindControl("lblCategoria") as Label;
myLabel.Text = str.ToString();
Note que o nome do label é o mesmo dentro do grid. É feito um cast Label para atribuir o valor depois. Basta usar a propriedade Text para que todos os valores agrupados se transferissem para o texto dentro do grid. A imagem 1.0 mostra como ficou o resultado do grid.
Imagem 1.0 - Mostrando o GridView