GridView e DetailsView

Criando campos customizados

Nota: este exemplo complementa o artigo de GridView publicado na edição 26 da MSDN Magazine.

Projetando um componente DropDownField 

Os campos de dados DropDownField são utilizados com campos de chave-estrangeira com colunas que contêm índices para dados que residem em outra tabela. Imagine uma tabela de pedidos que possui referências para o cliente que fez o pedido e o empregado. Na tabela de pedidos, não são armazenados o nome completo do empregado e do cliente, porém somente códigos que referenciam outra tabela.

No fim, uma linha na tabela de pedidos contém códigos ao invés de nomes. Isso pode ser interessante para o banco de dados, mas não é interessante para o usuário. Ao exibir esse tipo de dado, precisamos transformar esses números em nomes legíveis. Essa é somente uma parte da tarefa que poderia ser feita por um componente DropDownField. Quando em modo de edição ou inserção, o campo deveria exibir uma lista de possíveis clientes ou empregados cadastrados no BD.

 Projetei o componente DropDownField para expor as propriedades DataTextField e DataValueField, para indicar a coluna de dados usada na exibição e a coluna de dados a ser utilizada para obter o código selecionado (valor para a foreign-key). O DataTextField só é útil em um cenário em particular: quando sua query contém joins. Considere o seguinte comando SQL:

 

SELECT o.*, e.lastname FROM orders o

INNER JOIN employees e on o.employeeid=e.employeeid

WHERE o.orderid=@id

 

Uma linha desta query que é associada a um controle DetailsView contém ambos o ID do empregado que fez o pedido (tabela de pedidos - Orders) e seu último nome (tabela de empregados - Employee). Nesse caso, configuramos o DataValueField para EmployeeId e DataTextField para Lastname. Se não existisse o join, você poderia deixar o DataTextField em branco.

O componente DropDownField também possui mais três propriedades. DataSourceIDForEdit indica o controle DataSource que provê os valores a serem listados quando em modo de edição/inserção. DataValueFieldForEdit especifica o campo na fonte de dados usado para determinar os valores dos itens na lista dropdown. Finalmente, DataTextFieldForEdit determina o campo na fonte de dados usado para exibir o texto na lista.

 

Em grande parte, o código do DropDownField é igual ao do CalendarField. Uma diferença fundamental existe quando é feito o data binding. Para preparar o controle do tipo calendário, precisamos só fixar sua propriedade SelectedDate com o valor obtido a partir do registro associado. Com um DropDownField, precisamos primeiro preencher a lista dropdown e selecionar o item que corresponde ao valor do DataValueField no registro associado. O controle dropdown é ligado a uma fonte de dados através da propriedade DataSourceID.

Para data binding, isso funciona bem. Porém, fixando o DataSourceID em um controle data-bound não dispara automaticamente o processo de data binding. Se tentar acessar a coleção Items imediatamente depois de definir o  DataSourceID, receberá uma exceção de referência nula. Se chamar o método DataBind para forçar o data binding, receberá uma exceção de estouro de pilha (stack overflow) porque está chamando DataBind de dentro de um evento de DataBinding que é ativado por uma chamada a DataBind. O truque é associar um manipulador para evento DataBound do controle dropdown:

 

Dim dd As DropDownList = CType(target, DropDownList)

dataValue = LookupValueForEdit(target)

AddHandler dd.DataBound, AddressOf OnDropDownDataBound

dd.DataTextField = DataTextFieldForEdit

dd.DataValueField = DataValueFieldForEdit

dd.DataSourceID = DataSourceIDForEdit

 

O evento de DataBound é novo no ASP.NET 2.0 para controles data-bound e é disparado quando o processo de data binding está completo. Neste momento, o manipulador para o evento DataBound dispara quando a coleção Items é completamente preenchida, e assim podemos seguramente selecionar o item:

 

Sub OnDropDownDataBound(sender As Object sender, e As EventArgs)

    Dim dd As DropDownList = CType(sender, DropDownList)

    Dim li As ListItem = dd.Items.FindByValue(dataValue)

    li.Selected = True

End Sub

 

Uma das principais características do componente DropDownField é poder utilizar o valor de DataValueField para achar um campo relacionado, possivelmente em outra fonte de dados. Essa operação implica em uma consulta ao banco de dados (uma operação Inner Join) ou um acesso à cache de dados do ASP.NET. Executar uma consulta no banco de dados é uma opção que vamos descartar por boas razões: desempenho e porque quebraria a consistência e o forçaria a manter a string de conexão e código SQL em um controle de dados.

 

A única opção aceitável consiste em recuperar o campo relacionado (por exemplo, o último nome do empregado a partir do seu ID) a partir da fonte de dados usada para preencher o dropdown em modo de edição (preenchemos o dropdown quando o controle é inicializado). Para melhorar o desempenho, a fonte de dados só deve ser acessada quando estivermos em modo de edição / inserção. A cache deve estar habilitada no controle de fonte de dados e um mecanismo de dependência deve ser utilizado para atualizar os dados em cache caso ocorram mudanças.

 

E se o registro associado ao DetailsView for obtido através de um join? Nesse caso, a informação requerida já está disponível. Isso porque defini uma propriedade DataTextField opcional. Se for definida, e o campo especificado realmente existe, a fonte de dados para edição será carregada somente quando o controle container realmente entrar em modo de edição / inserção. Se DataTextField não estiver disponível, então a fonte de dados é acessada durante a inicialização, e o dropdown é criado antes e ficará disponível para os métodos internos através de uma propriedade privada. Aqui está o código que liga um campo DropDownField à coluna EmployeeId da tabela de pedidos (Orders) e à coluna LastName da tabela de empregados (Employee):

 

<msdn:DropDownField

  DataValueField = "employeeid"

  HeaderText = "Postou por"

  DataSourceIDForEdit = "EmployeeDataSource"

  DataValueFieldForEdit = "employeeid" 

  DataTextFieldForEdit = "lastname" /> 

EmployeeDataSource está definido no seguinte:

<asp:SqlDataSource ID = runat de "EmployeeDataSource" = "servidor"

ConnectionString = ' ' 

SelectCommand = “select employeeid, lastname from

empregados" />

 

O código-fonte completo dos componentes DropDownField e CalendarField está disponível no endereço para download deste artigo.

Nota: o componente DropDownField discutido aqui foi otimizado para ser usado com fontes de dados. Porém, o componente DropDownField poderia ser utilizado para permitir aos usuários selecionar qualquer valor de uma lista, por exemplo, um tipo enumerado ou qualquer coleção de dados. Para suportar esses cenários, poderíamos adicionar um propriedade chamada DataSource e programaticamente associar seu valor à propriedade DataSource do controle dropdown.

Aplicando estilos

O tipo DataControlField define algumas propriedades de estilo para cabeçalho, rodapé e itens de dados. Além disso, a propriedade ControlStyle permite aplicar estilos aos controles de entrada utilizados pelo componente:

 

<asp:BoundField DataField = "CompanyName" HeaderText = "Companhia"> 

  <ControlStyle BackColor = "red" />