Programação em Asp.Net - parte V
Autor: Luís Abreu
Conteúdo: Introdução à programação em Asp.Net 2.0
Ferramentas: Visual Web Dev Express/Visual C# Express
Após acabar de escrever o último artigo, reparei que cometi um erro ao longo desta série. Na verdade, só hoje reparei que não houve o artigo relativo à parte V. Por isso, achei por bem corrigir essa lacuna, convertendo este artigo no quinto da série. Nos últimos dois artigos falámos um pouco sobre o novo controlo GridView e sobre possíveis extensões/personalizações que poderiam ser feitas sobre esse controlo.
Hoje vamos continuar a falar sobre os novos controlos que irão ser disponibilizados na próxima versão da framework. Iremos começar por explorar o controlo DetailsView e terminaremos o artigo com a apresentação do controlo FormView. Ambos os controlos são semelhantes a nível de funcionalidades (isto porque ambos permitem introduzir, modificar, eliminar e visualizar registos), mas possuem algumas nuances a nível de utilização.
DetailsView: quando muitos registos confudem...
A primeira observação importante é a seguite: todos os conhecimentos apreendidos anteriormente com a GridView continuam a ser aplicáveis neste controlo. Ou melhor, os principios são praticamente os mesmos (com pequenas diferenças resultantes da forma como ambos os controlos funcionam).
O controlo DetailsView veio colmatar uma lacuna existente na versão 1.X da framework. Este controlo permite-nos visualizar/alterar registos provenientes de uma fonte de dados. Dito assim, até parece que estamos a falar novamente do controlo GridView. Contudo, e ao contrário do que acontece com a GridView, este controlo apenas permite disponibilizar informação relativa a um registo. Por outras palavras, este controlo apenas permite-nos visualizar um registo de cada vez.
Como referi anteriormente, muitas das funcionalidades disponíveis no controlo GridView encontram-se também disponiveis neste controlo. Assim, e de uma forma resumida, podemos:
- gerar automaticamente as linhas que irão ser apresentadas (sim, linhas, porque ao contrário do que acontece com o controlo GridView, o controlo DetailsView apresenta cada um dos valores (contidos num registo) numa linha; por outras palavras, cada campo de um registo é apresentado numa linha. Como seria de esperar, esta opção é controlada pela propriedade AutoGenerateRows.
- gerar (automaticamente) botões que permitem efectuar as operações que um utilizador espera poder efectuar sobre este controlo (nomeadamente, inserção, actualização e eliminação de registos). O controlo destas operações pode ser efectuado através de uma das várias propriedades existentes para esse efeito (AutoGenerateDeleteButton, AutoGenerateEditButton, AutoGenerateInsertButton).
- definir um cabeçalho para o controlo, utilizando para isso a propriedade Header (note-se que para além desta propriedade podemos também apresenta um título no interior de um elemento <caption> a que não é aplicado nenhum estilo - para tal basta definir um valor associado à propriedade Caption).
- apresentar um pager que pode ser colocado no topo do controlo, na base (ou, se preferirem, fundo) ou em ambos. A utilização do pager é controlada pela propriedade AllowPaging (valor true possibilita a visualização do pager), enquanto que a o posicionamento é definido pela propriedade Position associada ao elemento PagerSettings (que pode ser definido no interior do elemento DetailsView) .
- definir o modo por defeito do controlo. A propriedade DefaultMode permite-nos escolher um dos valores contidos na enumeração DetailsViewMode de forma a podermos definir o estado "normal" deste controlo (ou, se preferirem, o estado que é apresentado por defeito - que é definido após o fim de uma operação de edição ou de inserção de um registo).
- indicar o(s) campo(s) chave(s) através da propriedade DataKeysName.
Definição de linhas e estilos
Como vimos, a geração de linhas de forma automática é controlada através da propriedade AutoGenerateRows. Apesar de ser útil em situações em que necessitamos de mostrar todas as colunas, a verdade é que na maior parte das vezes necessitamos apenas de mostrar algumas colunas ou de controlar a visibilidade da coluna em determinadas operações (por exemplo, se um dos campos da fonte de dados for definido como numeração automática não devemos mostrar esse campo durante uma operação de edição - neste caso será necessário configurar a propriedade InsertVisible).
Não será surpresa se eu afirmar que podemos definir quais as colunas (ou melhor, linhas - esta troca de coluna por linha só pode ser efeito de uma exposição excessiva ao GridView ;) ) através dos nossos velhos conhecidos elementos derivados de DataControlField (estou a falar dos conhecidos BoundFieldControl et al apresentados previamente nos artigos relativos ao controlo GridView). O excerto seguinte (retirado da página Page.aspx contida no sample que acompanha o artigo) demonstra como podemos efectuar este tipo de operações:
<asp:DetailsView runat="server" ID="details" AllowPaging="true" AutoGenerateRows="false" CssClass="MainGrid"
DataSourceID="source" OnPreRender="OnPreRender">
<RowStyle CssClass="Item" />
<AlternatingRowStyle CssClass="AlternatingItem" />
<PagerStyle CssClass="Pager" />
<HeaderStyle CssClass="Header" />
<FooterStyle CssClass="Pager" />
<PagerSettings Mode="Numeric" />
<CommandRowStyle CssClass="CommandRow" />
<Fields>
<asp:BoundField HeaderText="Id do Aluno" DataField="IDAlunos" />
<asp:BoundField HeaderText="Nome do Aluno" DataField="Nome" />
<asp:BoundField HeaderText="IDNivel" DataField="IDNivel" />
<asp:BoundField HeaderText="Inscrito" DataField="Inscrito" />
</Fields>
</asp:DetailsView>
Comparando com o GridView, podemos ver que as diferenças são minimas: As linhas visíveis são definidas no interior do elemento Fields - em vez do elemento Columns; esta substituição deve-se apenas ao facto deste controlo definir os campos associados a um registo proveniente da fonte de dados por linha - e não por coluna, como acontece com o GridView.
O excerto anterior demonstra ainda como podemos definir eventuais estilos que devem ser aplicados aos elementos (como se pode verificar, os principios apresentados nos artigos anteriores continuam a ser aplicáveis nesta situação). Apesar de definir a estrutura dos campos em linhas, os estilos RowStyle e AlternatingRowStyle continuam a ser aplicados da mesma forma, isto é, RowStyle é aplicado a uma determinada linha e o AlternatingRowItem é aplicado à linha seguinte.
Infelizmente para nós, o controlo não nos permite definir eventuais estilos aplicáveis às células. Na minha opinião, o controlo deveria possuir uma aplicação de estilo baseada em colunas e não em linhas. O que que quero dizer com isto é que acho que seria mais apropriado definirmos o estilo aplicável à coluna que contém os cabeçalhos e outro à coluna que contém os valores porvenientes da base de dados.
Mas nem tudo está perdido! Existe um evento que (teoricamente) permite-nos definir este tipo de estilos. Estou a falar do evento ItemCreated. A aplicação de um determinado estilo durante este evento deveria ser tudo o que necessitávamos para modificar os estilos css gerados por defeito. Infelizmente, tal não acontece (devido ao que me parece ser um bug do controlo). Contudo, se o controlo apenas for utilizado em paginação (ou seja, se o controlo for utilizado apenas para consulta) e sem ser em modo callback, então é possível ultrapassar este problema através da utilização do evento OnPreRender. O excerto seguinte demonstra como podemos efectuar essa operação:
protected void OnPreRender( object sender, EventArgs args )
{
DetailsViewRow row = null;
TableCell cell = null;
for ( int i = 0; i < details.Rows.Count ; i++ )
{
row = details.Rows[i];
cell = row.Controls [0] as TableCell;
cell.CssClass = "Header";
}
}
Nada de muito complicado. A ideia é simples: percorrer cada uma das linhas, procurar a primeira célula e aplicar o estilo pretendido (queria ainda chamar a atenção para o facto deste código ser apenas utilizado para efeitos demonstrativos e de ter de ser melhorado se tiver de utilizado e produção).
Adicionando ou cancelando a adição de um novo registo
O controlo DetailsView pode também ser utilizado para permitir a introdução/edição/eliminação de registos na fonte de dados associada. O primeiro passo que deve de ser dado para permitir estas operações consiste em definir as instruções adequadas no controlo fonte de dados (se tal não acontecer, será gerada uma excepção aquando da tentativa de executar uma operação que não tenha codigo suporte - leia-se sql - na fonte de dados associada). Depois de definirmos as operações ao nível da fonte de dados, temos de permitir a realização dessas operações no próprio controlo.
Existem duas hipóteses: geração automática dos botões (controlada através das propriedades AutoGenerateXXXButton) ou introdução de uma coluna do tipo CommandField (note-se que também é possível, por exemplo, introduzir um botão num FooterTemplate que despolete uma acção com o CommandName adequado à situação - Insert para introdução, Edit para edição e Delete para eliminação; claro que a vantagem da coluna CommandField reside na forma automática com que esta coluna mostra os botões adequados à situação actual do controlo).
Definição de templates
Ao contrário do que acontece com o controlo GridView, o DetailsView apenas permite personalizar algumas zonas do controlo:
- Footer: o footer do controlo pode ser personalizado através do elemento FooterTemplate.
- Header: A construção de header pode ser efectuada através do elemento HeaderTemplate;
- Pager: como seria de esperar, a personalização do pager também pode ser efectuada através do elemento PagerTemplate.
Convém referir ainda que a estrutura do controlo está contida numa tabela. Se quiserem, podem também definir uma linha do tipo TemplateField. O controlo irá efectuar o rendering da linha, mas ao que parece este controlo não deve ser utilizado com templates nas linhas (apesar do código que acompanha este artigo não apresentar nenhum exemplo, cheguei a testar este tipo de colunas sem obter nenhum erro; contudo, já encontrei referência ao facto deste controlo não dever conter linhas do tipo template - ainda não consegui obter nenhuma resposta definitiva a esta questão, pelo que o melhor será jogar pelo seguro e não definir templates nas linhas). Se tal for necessário, então será necessário recorrer ao controlo FormView.
GridView + DetailsView = Master-detail view
Não podia terminar esta breve referência ao controlo DetailsView sem fazer referência à facilidade com que se consegue contruir uma vista do tipo master-detail utilizando os controlos GridView e DetailsView. Na verdade, grande parte da facilidade da contrução deste tipo de vista reside na utilização dos novos controlos fonte de dados. Na prática, a construção de uma vista destas resume-se à definição de:
- um controlo GridView ligado a uma fonte de dados;
- um controlo DetailsView ligado a outra fonte de dados;
- definição da propriedade FilterExpression da segunda fonte de dados e adição de um parâmetro contido no elemento FilterParameters do tipo ControlParameter que obtém o seu valor a partir da propriedade SelectedValue do controlo GridView.
O excerto seguinte mostra a "imagem" completa:
<asp:GridView runat="server" ID="grid" AutoGenerateColumns="false" AllowSorting="true" AllowPaging="true" CssClass="MainGrid" Width="98%"
DataSourceId="source" AutoGenerateDeleteButton="false" AutoGenerateEditButton="false" PageSize="3" DataKeyNames="IDAlunos">
<Columns>
<asp:BoundField HeaderText="Número Aluno" DataField="IDAlunos" ReadOnly="true" ShowHeader="true"
InsertVisible="false" ControlStyle-Width="10%" SortExpression="IDAlunos" />
<asp:BoundField HeaderText="Nome do Aluno" DataField="Nome" ControlStyle-Width="70%" SortExpression="Nome" />
<asp:BoundField HeaderText="IDNivel" DataField="IDNivel" ControlStyle-Width="10%" SortExpression="IDNivel"/>
<asp:CheckBoxField HeaderText="Inscrito" DataField="Inscrito" ControlStyle-Width="10%" />
<asp:CommandField HeaderText="Operações" DeleteText="Eliminar" ShowDeleteButton="true" ShowSelectButton="true" SelectText="Seleccionar" />
</Columns>
</asp:GridView>
<asp:DetailsView runat="server" ID="details" CssClass="MainGrid"
DataSourceID="insert" DataKeyNames="IDAlunos" EmptyDataText="---" AutoGenerateEditButton="true" >
</asp:DetailsView>
<asp:AccessDataSource DataFile="Data/Test.mdb" Runat="Server" ID="source"
SelectCommand="select IDAlunos, Nome, IDNivel, Inscrito from Alunos"
DeleteCommand="delete from Alunos where IDAlunos=@IDAlunos"
OldValuesParameterFormatString="{0}">
</asp:AccessDataSource>
<asp:AccessDataSource DataFile="Data/Test.mdb" Runat="Server" ID="insert"
SelectCommand="select IDAlunos, Nome, IDNivel, Inscrito from Alunos"
InsertCommand="insert into Alunos (Nome, IDNivel, Inscrito) values (@Nome, @IDNivel, @Inscrito)"
UpdateCommand="update Alunos set Nome=@Nome, IDNivel=@IDNivel, Inscrito=@Inscrito where IDAlunos=@IDAlunos"
OldValuesParameterFormatString="{0}" FilterExpression="IDAlunos=@IDAlunos">
<FilterParameters>
<asp:ControlParameter ControlID="grid" Name="IDAlunos" PropertyName="SelectedValue" />
</FilterParameters>
</asp:AccessDataSource>
Mais simples do que isto é dificil. Vamos então passar a controlo DetailsView.
DetailsView: o "repeater" dos formulários
Penso que o título traduz a utilização deste controlo. Na verdade, o controlo possui exactamente os mesmos principios de funcionamento associados aos controlos anteriores. A grande diferença reside no facto de neste caso ser necessário gerar o conteúdo do controlo através de templates (daí o título dado a esta secção) . Como seria de esperar, a definição dos elementos é efectuada através de um dos três tipos de templates existentes: ItemTemplate, InsertTemplate e EditTemplate.
Como seria de esperar, o controlo consegue efectuar automaticamente as operações de inserção, eliminação e edição. Para tal, é necessário definir as operações adequadas na fonte de dados. Para além disso, temos de utilizar o novo método Bind, que nos permite efectuar uma operação de binding nos dois sentidos. Este método permite-nos inicializar um controlo com um determinado valor proveniente da fonte de dados e também permite-nos obter o novo valor a partir do controlo (sendo este valor utilizado como valor passado a um eventual parâmetro da instrução associada na fonte de dados). Finalmente, temos de adicionar os botões que irão despoletar as operações necessárias. Convém salientar que, para que os botões sejam capazes de incializar essas operações têm de possuir um CommandName adequado à acção que devem inociar. A página Page6.aspx contém um exemplo da utilização deste controlo.
Conclusões finais
Ao longo deste artigo apresentei as principais características associadas aos controlos DetailsView e FormView. Estes controlos permitem-nos mostrar registos oriundos de uma fonte de dados e complementam as funcionalidades oferecidas pelo controlo GridView. Queria ainda referir que deparei-me com algumas dificuldades em relação à utilização de certas funcionalidades. Uma delas foi personalização das classes de estilo aplicadas às células (já referida anteriormente). Outra, que não foi referidade, refere-se à utilização do chamado master-detail. O código que acompanha este artigo (e que ilustra essa funcionalidade) apenas funciona com o Visual Web Express beta 1. A release de Outubro (conhecida por October CTP) não funciona de forma correcta.
O código que acompanha este artigo irá ser disponibilizado na secção de downloads do site (está a aguardar autorização). Por favor enviem-me as vossas opiniões/sugestões/críticas/correcções para progC@netmadeira.com.
Fiquem bem e boa programação! Até à próxima.
Leiam o meu blog em: http://weblogs.pontonetpt.com/luisabreu