Criação de uma interface do usuário de classificação personalizada (VB)
por Scott Mitchell
Ao exibir uma longa lista de dados classificados, pode ser muito útil agrupar dados relacionados introduzindo linhas separadoras. Neste tutorial, veremos como criar essa interface do usuário de classificação.
Introdução
Ao exibir uma longa lista de dados classificados em que há apenas alguns valores diferentes na coluna classificada, um usuário final pode achar difícil discernir onde, exatamente, ocorrem os limites de diferença. Por exemplo, há 81 produtos no banco de dados, mas apenas nove opções de categoria diferentes (oito categorias exclusivas mais a opção NULL
). Considere o caso de um usuário interessado em examinar os produtos que se enquadram na categoria Frutos do Mar. Em uma página que lista todos os produtos em um único GridView, o usuário pode decidir que sua melhor opção é classificar os resultados por categoria, o que agrupará todos os produtos frutos do mar juntos. Depois de classificar por categoria, o usuário precisa procurar por onde os produtos agrupados em frutos do mar começam e terminam. Como os resultados são ordenados em ordem alfabética pelo nome da categoria que encontra os produtos frutos do mar não é difícil, mas ainda requer a verificação atenta da lista de itens na grade.
Para ajudar a realçar os limites entre grupos classificados, muitos sites empregam uma interface do usuário que adiciona um separador entre esses grupos. Separadores como os mostrados na Figura 1 permitem que um usuário encontre mais rapidamente um grupo específico e identifique seus limites, bem como verifique quais grupos distintos existem nos dados.
Figura 1: Cada grupo de categorias está claramente identificado (clique para exibir a imagem em tamanho real)
Neste tutorial, veremos como criar essa interface do usuário de classificação.
Etapa 1: Criando um GridView padrão e classificável
Antes de explorarmos como aumentar o GridView para fornecer a interface de classificação aprimorada, vamos primeiro criar um GridView padrão e classificável que lista os produtos. Comece abrindo a CustomSortingUI.aspx
página na PagingAndSorting
pasta . Adicione um GridView à página, defina sua ID
propriedade como ProductList
e associe-a a um novo ObjectDataSource. Configure o ObjectDataSource para usar o ProductsBLL
método da classe s GetProducts()
para selecionar registros.
Em seguida, configure o GridView de modo que ele contenha apenas o ProductName
, CategoryName
, SupplierName
e UnitPrice
BoundFields e o CheckBoxField descontinuado. Por fim, configure o GridView para dar suporte à classificação marcando a caixa de seleção Habilitar Classificação na marca inteligente gridView (ou definindo sua AllowSorting
propriedade true
como ). Depois de fazer essas adições à CustomSortingUI.aspx
página, a marcação declarativa deve ser semelhante à seguinte:
<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
TypeName="ProductsBLL"></asp:ObjectDataSource>
Reserve um momento para ver nosso progresso até agora em um navegador. A Figura 2 mostra o GridView classificável quando seus dados são classificados por categoria em ordem alfabética.
Figura 2: Os dados de GridView classificáveis são ordenados por categoria (clique para exibir a imagem em tamanho real)
Etapa 2: Explorando técnicas para adicionar as linhas separadoras
Com o GridView genérico e classificável concluído, tudo o que resta é ser capaz de adicionar as linhas separadora no GridView antes de cada grupo classificado exclusivo. Mas como essas linhas podem ser injetadas no GridView? Essencialmente, precisamos iterar pelas linhas do GridView, determinar onde as diferenças ocorrem entre os valores na coluna classificada e, em seguida, adicionar a linha separadora apropriada. Ao pensar sobre esse problema, parece natural que a solução esteja em algum lugar no manipulador de eventos gridView RowDataBound
. Como discutimos no tutorial Formatação personalizada baseada em dados , esse manipulador de eventos é comumente usado ao aplicar a formatação em nível de linha com base nos dados da linha. No entanto, o RowDataBound
manipulador de eventos não é a solução aqui, pois as linhas não podem ser adicionadas ao GridView programaticamente desse manipulador de eventos. A coleção gridView Rows
, na verdade, é somente leitura.
Para adicionar linhas adicionais ao GridView, temos três opções:
- Adicionar essas linhas de separador de metadados aos dados reais associados ao GridView
- Depois que o GridView tiver sido associado aos dados, adicione instâncias adicionais
TableRow
à coleção de controles gridView - Crie um controle de servidor personalizado que estenda o controle GridView e substitua os métodos responsáveis pela construção da estrutura gridView s
Criar um controle de servidor personalizado seria a melhor abordagem se essa funcionalidade fosse necessária em muitas páginas da Web ou em vários sites. No entanto, isso implicaria um pouco de código e uma exploração completa nas profundezas do funcionamento interno do GridView. Portanto, não consideraremos essa opção para este tutorial.
As outras duas opções adicionando linhas separadores aos dados reais que estão sendo associados ao GridView e manipulando a coleção de controles gridView após sua associação - atacam o problema de forma diferente e merecem uma discussão.
Adicionando linhas ao limite de dados ao GridView
Quando o GridView está associado a uma fonte de dados, ele cria um GridViewRow
para cada registro retornado pela fonte de dados. Portanto, podemos injetar as linhas separadoras necessárias adicionando registros separadores à fonte de dados antes de associá-la ao GridView. A Figura 3 ilustra esse conceito.
Figura 3: Uma técnica envolve a adição de linhas separadora à fonte de dados
Uso os registros separadores de termos entre aspas porque não há registro separador especial; em vez disso, devemos de alguma forma sinalizar que um registro específico na fonte de dados serve como um separador em vez de uma linha de dados normal. Para nossos exemplos, estamos associando uma ProductsDataTable
instância ao GridView, que é composto por ProductRows
. Podemos sinalizar um registro como uma linha separadora definindo sua CategoryID
propriedade -1
como (já que esse valor não podia existir normalmente).
Para utilizar essa técnica, precisamos executar as seguintes etapas:
- Recuperar programaticamente os dados a serem associados ao GridView (uma
ProductsDataTable
instância) - Classificar os dados com base nas propriedades e
SortDirection
doSortExpression
GridView - Iterar pelo
ProductsRows
noProductsDataTable
, procurando onde estão as diferenças na coluna classificada - Em cada limite de grupo, insira uma instância de registro
ProductsRow
separador na DataTable, uma que aCategoryID
tenha definida-1
como (ou qualquer designação que tenha sido decidida para marcar um registro como um registro separador ) - Depois de injetar as linhas do separador, associe programaticamente os dados ao GridView
Além dessas cinco etapas, também precisamos fornecer um manipulador de eventos para o evento GridView.RowDataBound
Aqui, marcar cada DataRow
e determinar se era uma linha separadora, uma cuja CategoryID
configuração era -1
. Nesse caso, provavelmente gostaríamos de ajustar sua formatação ou o texto exibido nas células.
Usar essa técnica para injetar os limites do grupo de classificação requer um pouco mais de trabalho do que o descrito acima, pois você também precisa fornecer um manipulador de eventos para o evento GridView Sorting
e acompanhar os SortExpression
valores e SortDirection
.
Manipulando a coleção de controles GridView após ela ter sido databound
Em vez de enviar mensagens aos dados antes de associá-los ao GridView, podemos adicionar as linhas do separador depois que os dados tiverem sido associados ao GridView. O processo de associação de dados cria a hierarquia de controle do GridView, que, na realidade, é simplesmente uma Table
instância composta por uma coleção de linhas, cada uma delas composta por uma coleção de células. Especificamente, a coleção de controles GridView contém um Table
objeto em sua raiz, um GridViewRow
(que é derivado da TableRow
classe ) para cada registro no DataSource
associado ao GridView e um TableCell
objeto em cada GridViewRow
instância para cada campo de dados no DataSource
.
Para adicionar linhas separadoras entre cada grupo de classificação, podemos manipular diretamente essa hierarquia de controle depois que ela for criada. Podemos ter certeza de que a hierarquia de controle do GridView foi criada pela última vez no momento em que a página está sendo renderizada. Portanto, essa abordagem substitui o Page
método da classe s Render
, momento em que a hierarquia de controle final do GridView é atualizada para incluir as linhas separadoras necessárias. A Figura 4 ilustra esse processo.
Figura 4: Uma técnica alternativa manipula a hierarquia de controle gridView (clique para exibir a imagem em tamanho real)
Para este tutorial, usaremos essa última abordagem para personalizar a experiência do usuário de classificação.
Observação
O código que estou apresentando neste tutorial baseia-se no exemplo fornecido na entrada de blog do Teemu Keiski , Reproduzindo um Bit com o Agrupamento de Classificação gridView.
Etapa 3: Adicionar as linhas separadoras à hierarquia de controle gridView
Como só queremos adicionar as linhas separadoras à hierarquia de controle do GridView depois que sua hierarquia de controle tiver sido criada e criada pela última vez nessa visita à página, queremos executar essa adição no final do ciclo de vida da página, mas antes que a hierarquia de controle GridView real tenha sido renderizada em HTML. O ponto mais recente possível no qual podemos fazer isso é o Page
evento de classe s Render
, que podemos substituir em nossa classe code-behind usando a seguinte assinatura de método:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Add code to manipulate the GridView control hierarchy
MyBase.Render(writer)
End Sub
Quando o Page
método original Render
da classe é invocado base.Render(writer)
, cada um dos controles na página será renderizado, gerando a marcação com base em sua hierarquia de controle. Portanto, é imperativo que ambos chamemos base.Render(writer)
, para que a página seja renderizada e manipulemos a hierarquia de controle do GridView antes de chamar base.Render(writer)
, para que as linhas separadoras tenham sido adicionadas à hierarquia de controle do GridView antes de ela ser renderizada.
Para injetar os cabeçalhos do grupo de classificação, primeiro precisamos garantir que o usuário tenha solicitado que os dados sejam classificados. Por padrão, o conteúdo do GridView não é classificado e, portanto, não precisamos inserir nenhum cabeçalho de classificação de grupo.
Observação
Se você quiser que o GridView seja classificado por uma coluna específica quando a página for carregada pela primeira vez, chame o método GridView na Sort
primeira visita à página (mas não em postbacks subsequentes). Para fazer isso, adicione essa chamada no Page_Load
manipulador de eventos em um if (!Page.IsPostBack)
condicional. Consulte as informações do tutorial Paginação e Classificação de Dados do Relatório para obter mais informações sobre o Sort
método .
Supondo que os dados foram classificados, nossa próxima tarefa é determinar por qual coluna os dados foram classificados e, em seguida, examinar as linhas em busca de diferenças nos valores dessa coluna. O código a seguir garante que os dados foram classificados e localiza a coluna pela qual os dados foram classificados:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' Determine the index and HeaderText of the column that
'the data is sorted by
Dim sortColumnIndex As Integer = -1
Dim sortColumnHeaderText As String = String.Empty
For i As Integer = 0 To ProductList.Columns.Count - 1
If ProductList.Columns(i).SortExpression.CompareTo( _
ProductList.SortExpression) = 0 Then
sortColumnIndex = i
sortColumnHeaderText = ProductList.Columns(i).HeaderText
Exit For
End If
Next
' TODO: Scan the rows for differences in the sorted column�s values
End Sub
Se o GridView ainda não tiver sido classificado, a propriedade gridView não SortExpression
terá sido definida. Portanto, só queremos adicionar as linhas do separador se essa propriedade tiver algum valor. Se isso acontecer, precisaremos determinar o índice da coluna pela qual os dados foram classificados. Isso é feito executando um loop pela coleção gridView, Columns
pesquisando a coluna cuja SortExpression
propriedade é igual à propriedade gridView s SortExpression
. Além do índice da coluna s, também capturamos a HeaderText
propriedade , que é usada ao exibir as linhas do separador.
Com o índice da coluna pelo qual os dados são classificados, a etapa final é enumerar as linhas do GridView. Para cada linha, precisamos determinar se o valor da coluna classificada é diferente do valor da coluna classificada da linha anterior. Nesse caso, precisamos injetar uma nova GridViewRow
instância na hierarquia de controle. Isso é feito com o seguinte código:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' ... Code for finding the sorted column index removed for brevity ...
' Reference the Table the GridView has been rendered into
Dim gridTable As Table = CType(ProductList.Controls(0), Table)
' Enumerate each TableRow, adding a sorting UI header if
' the sorted value has changed
Dim lastValue As String = String.Empty
For Each gvr As GridViewRow In ProductList.Rows
Dim currentValue As String = gvr.Cells(sortColumnIndex).Text
If lastValue.CompareTo(currentValue) <> 0 Then
' there's been a change in value in the sorted column
Dim rowIndex As Integer = gridTable.Rows.GetRowIndex(gvr)
' Add a new sort header row
Dim sortRow As New GridViewRow(rowIndex, rowIndex, _
DataControlRowType.DataRow, DataControlRowState.Normal)
Dim sortCell As New TableCell()
sortCell.ColumnSpan = ProductList.Columns.Count
sortCell.Text = String.Format("{0}: {1}", _
sortColumnHeaderText, currentValue)
sortCell.CssClass = "SortHeaderRowStyle"
' Add sortCell to sortRow, and sortRow to gridTable
sortRow.Cells.Add(sortCell)
gridTable.Controls.AddAt(rowIndex, sortRow)
' Update lastValue
lastValue = currentValue
End If
Next
End If
MyBase.Render(writer)
End Sub
Esse código começa referenciando programaticamente o Table
objeto encontrado na raiz da hierarquia de controle gridView e criando uma variável de cadeia de caracteres chamada lastValue
. lastValue
é usado para comparar o valor da coluna classificada da linha atual com o valor da linha anterior. Em seguida, a coleção GridView é Rows
enumerada e, para cada linha, o valor da coluna classificada é armazenado na currentValue
variável .
Observação
Para determinar o valor da coluna classificada da linha específica, uso a propriedade da célula s Text
. Isso funciona bem para BoundFields, mas não funcionará conforme desejado para TemplateFields, CheckBoxFields e assim por diante. Veremos como considerar campos alternativos do GridView em breve.
As currentValue
variáveis e lastValue
são comparadas. Se forem diferentes, precisamos adicionar uma nova linha separadora à hierarquia de controle. Isso é feito determinando o índice do GridViewRow
na coleção do Rows
objeto, criando instâncias e GridViewRow
TableCell
e, em seguida, adicionando e TableCell
GridViewRow
à hierarquia de Table
controle.
Observe que a linha separadora s lone TableCell
é formatada de modo que ela abrange toda a largura do GridView, é formatada usando a SortHeaderRowStyle
classe CSS e tem sua Text
propriedade de modo que mostre o nome do grupo de classificação (como Categoria ) e o valor do grupo (como Bebidas ). Por fim, lastValue
é atualizado para o valor de currentValue
.
A classe CSS usada para formatar a linha SortHeaderRowStyle
de cabeçalho do grupo de classificação precisa ser especificada no Styles.css
arquivo . Fique à vontade para usar as configurações de estilo que lhe agradem; Usei o seguinte:
.SortHeaderRowStyle
{
background-color: #c00;
text-align: left;
font-weight: bold;
color: White;
}
Com o código atual, a interface de classificação adiciona cabeçalhos de grupo de classificação ao classificar por qualquer BoundField (consulte a Figura 5, que mostra uma captura de tela ao classificar por fornecedor). No entanto, ao classificar por qualquer outro tipo de campo (como CheckBoxField ou TemplateField), os cabeçalhos do grupo de classificação não são encontrados (consulte a Figura 6).
Figura 5: a interface de classificação inclui cabeçalhos de grupo de classificação ao classificar por BoundFields (clique para exibir a imagem em tamanho real)
Figura 6: Os cabeçalhos de grupo de classificação estão ausentes ao classificar um CheckBoxField (clique para exibir a imagem em tamanho real)
O motivo pelo qual os cabeçalhos do grupo de classificação estão ausentes ao classificar por um CheckBoxField é porque o código atualmente usa apenas a TableCell
propriedade s Text
para determinar o valor da coluna classificada para cada linha. Para CheckBoxFields, a TableCell
propriedade s Text
é uma cadeia de caracteres vazia; em vez disso, o valor está disponível por meio de um controle Web CheckBox que reside na TableCell
coleção s Controls
.
Para lidar com tipos de campo diferentes de BoundFields, precisamos aumentar o código em que a currentValue
variável é atribuída a marcar para a existência de uma CheckBox na TableCell
coleção sControls
. Em vez de usar currentValue = gvr.Cells(sortColumnIndex).Text
, substitua esse código pelo seguinte:
Dim currentValue As String = String.Empty
If gvr.Cells(sortColumnIndex).Controls.Count > 0 Then
If TypeOf gvr.Cells(sortColumnIndex).Controls(0) Is CheckBox Then
If CType(gvr.Cells(sortColumnIndex).Controls(0), CheckBox).Checked Then
currentValue = "Yes"
Else
currentValue = "No"
End If
' ... Add other checks here if using columns with other
' Web controls in them (Calendars, DropDownLists, etc.) ...
End If
Else
currentValue = gvr.Cells(sortColumnIndex).Text
End If
Esse código examina a coluna TableCell
classificada da linha atual para determinar se há controles na Controls
coleção. Se houver e o primeiro controle for um CheckBox, a currentValue
variável será definida como Sim ou Não, dependendo da propriedade checkbox s Checked
. Caso contrário, o valor será obtido da TableCell
propriedade s Text
. Essa lógica pode ser replicada para manipular a classificação de qualquer TemplateFields que possa existir no GridView.
Com a adição de código acima, os cabeçalhos do grupo de classificação agora estão presentes ao classificar pelo CheckBoxField Descontinuado (consulte a Figura 7).
Figura 7: Os cabeçalhos de grupo de classificação agora estão presentes ao classificar um CheckBoxField (clique para exibir a imagem em tamanho real)
Observação
Se você tiver produtos com NULL
valores de banco de dados para os CategoryID
campos , SupplierID
ou UnitPrice
, esses valores aparecerão como cadeias de caracteres vazias no GridView por padrão, o que significa que o texto da linha do separador para esses produtos com NULL
valores será lido como Categoria: (ou seja, não há nenhum nome após Categoria: como com Categoria: Bebidas ). Se você quiser que um valor seja exibido aqui, poderá definir a propriedade BoundFields NullDisplayText
como o texto que deseja exibir ou adicionar uma instrução condicional no método Render ao atribuir o currentValue
à propriedade s da linha separadorText
.
Resumo
O GridView não inclui muitas opções internas para personalizar a interface de classificação. No entanto, com um pouco de código de baixo nível, é possível ajustar a hierarquia de controle do GridView para criar uma interface mais personalizada. Neste tutorial, vimos como adicionar uma linha separadora de grupo de classificação para um GridView classificável, que identifica mais facilmente os grupos distintos e os limites desses grupos. Para obter exemplos adicionais de interfaces de classificação personalizadas, marcar a entrada do blog A Few ASP.NET 2.0 GridView Sorting Tips and Tricks de Scott Guthrie.
Programação feliz!
Sobre o autor
Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.