Compartilhar via


Mestre/Detalhes Usando uma Lista com Marcadores de Registros Mestres com um DataList de Detalhes (VB)

por Scott Mitchell

Baixar PDF

Neste tutorial, compactaremos o relatório mestre/detalhado de duas páginas do tutorial anterior em uma única página, mostrando uma lista com marcadores de nomes de categorias no lado esquerdo da tela e os produtos da categoria selecionada à direita da tela.

Introdução

No tutorial anterior, vimos como separar um relatório mestre/detalhado em duas páginas. Na página mestre, usamos um Controle Repeater para renderizar uma lista de categorias com marcadores. Cada nome de categoria era um hiperlink que, quando clicado, levava o usuário para a página de detalhes, onde uma DataList de duas colunas mostrava os produtos pertencentes à categoria selecionada.

Neste tutorial, vamos compactar o tutorial de duas páginas em uma única página, mostrando uma lista com marcadores contendo os nomes das categorias no lado esquerdo da tela, com cada nome de categoria apresentado como um LinkButton. Clicar em um dos nomes de categoria LinkButtons induz um postback e associa os produtos da categoria selecionada a uma DataList de duas colunas à direita da tela. Além de exibir o nome de cada categoria, o Repetidor à esquerda mostra quantos produtos totais existem para uma determinada categoria (consulte a Figura 1).

O nome da categoria e o número total de produtos são exibidos à esquerda

Figura 1: O nome da categoria e o número total de produtos são exibidos à esquerda (clique para exibir a imagem em tamanho real)

Passo 1: Exibindo um repetidor na parte esquerda da tela

Para este tutorial, precisamos que a lista com marcadores de categorias apareça à esquerda dos produtos da categoria selecionada. O conteúdo de uma página da Web pode ser posicionado usando tags de parágrafo de elementos padrão do HTML, espaços não quebráveis, <table>, e assim por diante, ou por meio de folhas de estilo em cascata (CSS). Todos os nossos tutoriais até agora usaram técnicas CSS para posicionamento. Quando criamos a interface do usuário de navegação em nossa página mestra no tutorial Páginas Mestras e Navegação do Site, usamos posicionamento absoluto, indicando o deslocamento preciso de pixels para a lista de navegação e o conteúdo principal. Alternativamente, o CSS pode ser usado para posicionar um elemento à direita ou à esquerda de outro por meio de flutuação. Podemos fazer com que a lista com marcadores de categorias apareça à esquerda dos produtos da categoria selecionada ao posicionar o Repeater à esquerda do DataList.

Abra a CategoriesAndProducts.aspx página da DataListRepeaterFiltering pasta e adicione à página um Repetidor e uma Lista de Dados. Defina o Repetidor como IDCategories e o DataList como CategoryProducts. Vá para a visualização de origem e coloque os controles Repeater e DataList em seus próprios elementos <div>. Ou seja, coloque o Repetidor dentro de um <div> elemento primeiro e, em seguida, a Lista de Dados em seu próprio <div> elemento diretamente após o Repetidor. Seu código neste ponto deve ser semelhante ao seguinte:

<div>
    <asp:Repeater ID="Categories" runat="server">
    </asp:Repeater>
</div>
<div>
    <asp:DataList ID="CategoryProducts" runat="server">
    </asp:DataList>
</div>

Para flutuar o Repetidor à esquerda da DataList, precisamos usar o float atributo de estilo CSS, assim:

<div>
    Repeater
</div>
<div>
    DataList
</div>

O float: left; posiciona o primeiro elemento <div> à esquerda do segundo. As configurações width e padding-right indicam os primeiros <div> s width e quanto preenchimento é adicionado entre o conteúdo do elemento <div> e sua margem direita. Para obter mais informações sobre elementos flutuantes em CSS, confira o Floatutorial.

Em vez de especificar a configuração de estilo diretamente por meio do atributo do primeiro elemento <p>, vamos em vez disso criar uma nova classe CSS em Styles.css chamada FloatLeft:

.FloatLeft
{
    float: left;
    width: 33%;
    padding-right: 10px;
}

Então podemos substituir o <div> pelo <div class="FloatLeft">.

Depois de adicionar a classe CSS e configurar a marcação na CategoriesAndProducts.aspx página, vá para o Designer. Você deve ver o Repetidor flutuando à esquerda da Lista de Dados (embora agora ambos apareçam apenas como caixas cinzas, já que ainda não configuramos suas fontes de dados ou modelos).

O repetidor é flutuado à esquerda da lista de dados

Figura 2: O repetidor flutua à esquerda da lista de dados (clique para exibir a imagem em tamanho real)

Etapa 2: Determinar o número de produtos para cada categoria

Com a marcação ao redor dos controles Repeater e DataList concluída, estamos prontos para associar os dados da categoria ao controle Repeater. No entanto, como mostra a lista com marcadores de categorias na Figura 1, além do nome de cada categoria, também precisamos exibir o número de produtos associados à categoria. Para acessar essas informações, podemos:

  • Determine essas informações da classe code-behind da página ASP.NET. Dado um categoryID específico, podemos determinar o número de produtos associados chamando o método GetProductsByCategoryID(categoryID) da classe ProductsBLL. Esse método retorna um objeto ProductsDataTable cuja propriedade Count indica quantos ProductsRow s existem, que é o número de produtos para o categoryID especificado. Podemos criar um manipulador de eventos ItemDataBound para o Repetidor que, para cada categoria vinculada ao Repetidor, chama o método GetProductsByCategoryID(categoryID) da classe ProductsBLL e inclui sua contagem na saída.
  • Atualize o CategoriesDataTable no Typed DataSet para incluir uma NumberOfProducts coluna. Podemos então atualizar o GetCategories() método no CategoriesDataTable para incluir essas informações ou, alternativamente, deixar GetCategories() como está e criar um novo CategoriesDataTable método chamado GetCategoriesAndNumberOfProducts().

Vamos explorar essas duas técnicas. A primeira abordagem é mais simples de implementar, pois não precisamos atualizar a camada de acesso a dados; no entanto, requer mais comunicações com o banco de dados. A chamada para o método GetProductsByCategoryID(categoryID) da classe ProductsBLL no manipulador de eventos ItemDataBound adiciona uma chamada extra ao banco de dados para cada categoria exibida no Repetidor. Com esta técnica, há chamadas de banco de dados N + 1, onde N é o número de categorias exibidas no Repetidor. Com a segunda abordagem, a contagem de produtos é retornada com informações sobre cada categoria do método da classe CategoriesBLL s GetCategories() (ou GetCategoriesAndNumberOfProducts()), resultando em apenas uma consulta ao banco de dados.

Determinando o número de produtos no manipulador de eventos ItemDataBound

Determinar o número de produtos para cada categoria no manipulador de eventos do ItemDataBound Repetidor não requer nenhuma modificação em nossa Camada de Acesso a Dados existente. Todas as modificações podem ser feitas diretamente na CategoriesAndProducts.aspx página. Comece adicionando um novo ObjectDataSource, nomeado CategoriesDataSource, por meio da tag inteligente do Repetidor. Em seguida, configure o CategoriesDataSource ObjectDataSource para recuperar dados do método GetCategories() da classe CategoriesBLL.

Configurar o ObjectDataSource para usar o método GetCategories() da classe CategoriesBLL

Figura 3: Configurar o ObjectDataSource para usar o método da CategoriesBLL classe (clique para exibir a GetCategories() imagem em tamanho real)

Cada item no Categories Repetidor precisa ser clicável e, quando clicado, fazer com que a CategoryProducts Lista de Dados exiba esses produtos para a categoria selecionada. Isso pode ser feito tornando cada categoria um hiperlink, vinculando de volta a essa mesma página (CategoriesAndProducts.aspx), mas passando o CategoryID pela querystring, assim como vimos no tutorial anterior. A vantagem dessa abordagem é que uma página que exibe produtos de uma categoria específica pode ser marcada e indexada por um mecanismo de pesquisa.

Como alternativa, podemos tornar cada categoria um LinkButton, que é a abordagem que usaremos para este tutorial. O LinkButton é renderizado no navegador do usuário como um hiperlink, mas, quando clicado, induz um postback; no postback, o DataList ObjectDataSource precisa ser atualizado para exibir os produtos pertencentes à categoria selecionada. Para este tutorial, usar um hiperlink faz mais sentido do que usar um LinkButton; no entanto, pode haver outros cenários em que o uso de um LinkButton é mais vantajoso. Embora a abordagem de hiperlink seja ideal para este exemplo, vamos explorar usando o LinkButton. Como veremos, o uso de um LinkButton apresenta alguns desafios que, de outra forma, não surgiriam com um hiperlink. Portanto, o uso de um LinkButton neste tutorial destacará esses desafios e ajudará a fornecer soluções para os cenários em que talvez queiramos usar um LinkButton em vez de um hiperlink.

Observação

Você é incentivado a repetir este tutorial usando um controle HyperLink ou o elemento <a> em lugar do LinkButton.

A marcação a seguir mostra a sintaxe declarativa para o Repetidor e o ObjectDataSource. Observe que os modelos do Repeater renderizam uma lista com marcadores, com cada item como um LinkButton.

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory"></asp:LinkButton></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Observação

Para este tutorial, o Repetidor deve ter seu estado de exibição habilitado (observe a omissão da sintaxe EnableViewState="False" declarativa do Repetidor). Na etapa 3, criaremos um manipulador de eventos para o evento do Repetidor ItemCommand no qual atualizaremos a coleção do DataList ObjectDataSource SelectParameters . No entanto, o Repetidor s ItemCommandnão será acionado se o estado de exibição estiver desabilitado.

O LinkButton com valor de propriedade ID, referente a ViewCategory, não tem a propriedade Text definida. Se quiséssemos apenas exibir o nome da categoria, teríamos definido a propriedade Text declarativamente, por meio da sintaxe de vinculação de dados, da seguinte forma:

<asp:LinkButton runat="server" ID="ViewCategory"
    Text='<%# Eval("CategoryName") %>' />

No entanto, queremos mostrar o nome da categoria e o número de produtos pertencentes a essa categoria. Essas informações podem ser recuperadas do manipulador de eventos do ItemDataBound Repetidor ao fazer uma chamada para o método da classe ProductBLL e determinando quantos registros são retornados no ProductsDataTable resultante, como ilustra o código a seguir:

Protected Sub Categories_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
    ' Make sure we're working with a data item...
    If e.Item.ItemType = ListItemType.Item OrElse _
        e.Item.ItemType = ListItemType.AlternatingItem Then
        ' Reference the CategoriesRow instance bound to this RepeaterItem
        Dim category As Northwind.CategoriesRow = _
            CType(CType(e.Item.DataItem, System.Data.DataRowView).Row, _
                Northwind.CategoriesRow)
        ' Determine how many products are in this category
        Dim productsAPI As New NorthwindTableAdapters.ProductsTableAdapter
        Dim productCount As Integer = _
            productsAPI.GetProductsByCategoryID(category.CategoryID).Count
        ' Reference the ViewCategory LinkButton and set its Text property
        Dim ViewCategory As LinkButton = _
            CType(e.Item.FindControl("ViewCategory"), LinkButton)
        ViewCategory.Text = _
            String.Format("{0} ({1:N0})", category.CategoryName, productCount)
    End If
End Sub

Começamos garantindo que estamos trabalhando com um item de dados (um cujo ItemType é Item ou AlternatingItem) e, em seguida, referenciamos a instância CategoriesRow que acabou de ser vinculada ao RepeaterItem atual. Em seguida, determinamos o número de produtos para essa categoria criando uma instância da ProductsBLL classe, chamando seu GetCategoriesByProductID(categoryID) método e determinando o número de registros retornados usando a Count propriedade. Por fim, o ViewCategory LinkButton no ItemTemplate é referenciado e sua Text propriedade é definida como CategoryName (NumberOfProductsInCategory), onde NumberOfProductsInCategory está formatado como um número com zero casas decimais.

Observação

Como alternativa, poderíamos ter adicionado uma função de formatação à classe code-behind do código da página ASP.NET que aceita os valores CategoryName e CategoryID de uma categoria e retorna o CategoryName concatenado com o número de produtos na categoria (conforme determinado ao chamar o método GetCategoriesByProductID(categoryID)). Os resultados dessa função de formatação podem ser atribuídos declarativamente à propriedade Text do LinkButton, substituindo a necessidade do manipulador de eventos ItemDataBound. Consulte os tutoriais Usando TemplateFields no Controle GridView ou Formatando a Lista de Dados e o Repetidor com Base em Dados para obter mais informações sobre como usar funções de formatação.

Depois de adicionar esse manipulador de eventos, reserve um momento para testar a página por meio de um navegador. Observe como cada categoria é listada em uma lista com marcadores, exibindo o nome da categoria e o número de produtos associados à categoria (consulte a Figura 4).

O nome e o número de produtos de cada categoria são exibidos

Figura 4: O nome e o número de produtos de cada categoria são exibidos (clique para exibir a imagem em tamanho real)

Atualizando oCategoriesDataTableeCategoriesTableAdapterpara incluir o número de produtos para cada categoria

Em vez de determinar o número de produtos para cada categoria, pois ela está vinculada ao Repetidor, podemos simplificar esse processo ajustando o CategoriesDataTable e CategoriesTableAdapter na Camada de Acesso a Dados para incluir essas informações nativamente. Para conseguir isso, devemos adicionar uma nova coluna em CategoriesDataTable para conter o número de produtos associados. Para adicionar uma nova coluna a uma DataTable, abra o Conjunto de Dados Tipado (App_Code\DAL\Northwind.xsd), clique com o botão direito do mouse na DataTable a ser modificada e escolha Adicionar / Coluna. Adicione uma nova coluna ao CategoriesDataTable (consulte a Figura 5).

Adicionar uma nova coluna ao CategoriesDataSource

Figura 5: Adicionar uma nova coluna ao CategoriesDataSource (Clique para ver a imagem em tamanho real)

Isso adicionará uma nova coluna chamada Column1, que você pode alterar simplesmente digitando um nome diferente. Renomeie esta nova coluna para NumberOfProducts. Em seguida, precisamos configurar as propriedades dessa coluna. Clique na nova coluna e vá para a janela Propriedades. Altere a propriedade da DataType coluna de System.String para System.Int32 e defina a ReadOnly propriedade como True, conforme mostrado na Figura 6.

Defina as propriedades de DataType e ReadOnly da nova coluna

Figura 6: Defina as propriedades de DataType e ReadOnly da nova coluna

Embora agora CategoriesDataTable tenha uma NumberOfProducts coluna, seu valor não é definido por nenhuma das consultas TableAdapter correspondentes. Podemos atualizar o GetCategories() método para retornar essas informações se quisermos que essas informações sejam retornadas sempre que as informações da categoria forem recuperadas. Se, no entanto, precisarmos apenas pegar o número de produtos associados para as categorias em casos raros (como apenas para este tutorial), podemos deixar GetCategories() como está e criar um novo método que retorne essas informações. Vamos usar esta última abordagem, criando um novo método chamado GetCategoriesAndNumberOfProducts().

Para adicionar esse novo GetCategoriesAndNumberOfProducts() método, clique com o botão direito do CategoriesTableAdapter mouse e escolha Nova consulta. Isso abre o Assistente de Configuração de Consulta TableAdapter, que usamos várias vezes em tutoriais anteriores. Para esse método, inicie o assistente indicando que a consulta usa uma instrução SQL ad hoc que retorna linhas.

Criar o método usando uma instrução SQL ad hoc

Figura 7: Criar o método usando uma instrução SQL ad hoc (clique para exibir a imagem em tamanho real)

A instrução SQL retorna linhas

Figura 8: A instrução SQL retorna linhas (clique para exibir a imagem em tamanho real)

A próxima tela do assistente nos solicita a consulta a ser usada. Para retornar cada categoria CategoryID, CategoryName e Description, juntamente com o número de produtos associados à categoria, use a seguinte instrução SELECT:

SELECT CategoryID, CategoryName, Description,
       (SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
            as NumberOfProducts
FROM Categories c

Especifique a consulta a ser usada

Figura 9: Especificar a consulta a ser usada (clique para exibir a imagem em tamanho real)

Observe que a subconsulta que calcula o número de produtos associados à categoria possui o alias NumberOfProducts. Essa correspondência de nomenclatura faz com que o valor retornado por essa subconsulta seja associado à CategoriesDataTable coluna s NumberOfProducts .

Depois de inserir essa consulta, a última etapa é escolher o nome do novo método. Use FillWithNumberOfProducts e GetCategoriesAndNumberOfProducts para os padrões Preencher um DataTable e Retornar um DataTable, respectivamente.

Nomeie os novos métodos do TableAdapter FillWithNumberOfProducts e GetCategoriesAndNumberOfProducts

Figura 10: Nomeie os métodos FillWithNumberOfProducts do New TableAdapter e GetCategoriesAndNumberOfProducts (clique para exibir a imagem em tamanho real)

Neste ponto, a Camada de Acesso a Dados foi estendida para incluir o número de produtos por categoria. Como toda a nossa camada de apresentação roteia todas as chamadas para a DAL por meio de uma camada lógica de negócios separada, precisamos adicionar um método correspondente GetCategoriesAndNumberOfProducts à CategoriesBLL classe:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetCategoriesAndNumberOfProducts() As Northwind.CategoriesDataTable
    Return Adapter.GetCategoriesAndNumberOfProducts()
End Function

Com o DAL e o BLL concluídos, estamos prontos para vincular esses dados ao Categories Repetidor em CategoriesAndProducts.aspx! Se você já criou um ObjectDataSource para o Repetidor da seção Determinando o Número de Produtos no ItemDataBound Manipulador de Eventos, exclua esse ObjectDataSource e remova a configuração da propriedade do Repetidor DataSourceID ; também desconecte o evento do ItemDataBound Repetidor do manipulador de eventos removendo a Handles Categories.OnItemDataBound sintaxe na classe code-behind ASP.NET.

Com o Repetidor de volta em seu estado original, adicione um novo ObjectDataSource nomeado CategoriesDataSource por meio da marca inteligente do Repetidor. Configure o ObjectDataSource para usar a classe CategoriesBLL, mas em vez de fazê-lo usar o método GetCategories(), faça com que ele use GetCategoriesAndNumberOfProducts() (consulte a Figura 11).

Configurar o ObjectDataSource para usar o método GetCategoriesAndNumberOfProducts

Figura 11: Configurar o ObjectDataSource para usar o método (clique para exibir a GetCategoriesAndNumberOfProducts imagem em tamanho real)

Em seguida, atualize o ItemTemplate para que a propriedade Text do LinkButton seja atribuída declarativamente usando a sintaxe de vinculação de dados e inclua os campos de dados CategoryName e NumberOfProducts. A marcação declarativa completa para o Repeater e o CategoriesDataSource ObjectDataSource segue:

<asp:Repeater ID="Categories" runat="server" DataSourceID="CategoriesDataSource">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><asp:LinkButton runat="server" ID="ViewCategory"
                Text='<%# String.Format("{0} ({1:N0})", _
                    Eval("CategoryName"), Eval("NumberOfProducts")) %>' />
        </li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategoriesAndNumberOfProducts" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

A saída renderizada ao atualizar o DAL para incluir uma NumberOfProducts coluna é a mesma que ao usar a abordagem do ItemDataBound manipulador de eventos (consulte a Figura 4 para ver uma captura de tela do Repetidor que mostra os nomes das categorias e o número de produtos).

Etapa 3: Exibindo os produtos da categoria selecionada

Neste ponto, temos o Categories Repetidor exibindo a lista de categorias junto com o número de produtos em cada categoria. O Repetidor utiliza um LinkButton para cada categoria que, ao ser clicado, causa um postback, e, nesse momento, precisamos exibir esses produtos para a categoria selecionada na CategoryProducts DataList.

Um desafio que enfrentamos é como fazer com que a DataList exiba apenas os produtos para a categoria selecionada. No tutorial Mestre/Detalhes usando um GridView mestre selecionável com um Details DetailsView, vimos como criar um GridView cujas linhas podem ser selecionadas, com os detalhes da linha selecionada sendo exibidos em um DetailsView na mesma página. O ObjectDataSource do GridView retornou informações sobre todos os produtos usando o método sProductsBLL, GetProducts() enquanto o ObjectDataSource do DetailsView recuperou informações sobre o produto selecionado usando o GetProductsByProductID(productID) método. O productID valor do parâmetro foi fornecido declarativamente associando-o ao valor da propriedade GridView SelectedValue . Infelizmente, o Repetidor não tem uma SelectedValue propriedade e não pode servir como uma fonte de parâmetro.

Observação

Este é um daqueles desafios que aparecem ao usar o LinkButton em um Repetidor. Se tivéssemos usado um hiperlink para passar CategoryID através da querystring, poderíamos usar esse campo QueryString como fonte para o valor do parâmetro.

Antes de nos preocuparmos com a falta de uma SelectedValue propriedade para o Repetidor, vamos primeiro vincular o DataList a um ObjectDataSource e especificar seu ItemTemplate.

Na smart tag da DataList, escolha adicionar um novo ObjectDataSource chamado CategoryProductsDataSource e configure para utilizar o método GetProductsByCategoryID(categoryID) da classe ProductsBLL. Como o DataList deste tutorial oferece uma interface de somente leitura, você pode configurar as listas suspensas nas guias INSERT, UPDATE e DELETE como (None).

Configurar o ObjectDataSource para usar o método GetProductsByCategoryID(categoryID) da classe ProductsBLL

Figura 12: Configurar o ObjectDataSource para usar o método da classe ProductsBLLGetProductsByCategoryID(categoryID)(clique para visualizar a imagem em tamanho completo)

Como o GetProductsByCategoryID(categoryID) método espera um parâmetro de entrada (categoryID), o assistente Configurar fonte de dados nos permite especificar a fonte do parâmetro. Se as categorias tivessem sido listadas em um GridView ou em um DataList, definiríamos a lista suspensa de origem do parâmetro como Control e o ControlID como o ID do controle Web de dados. No entanto, como o Repetidor não tem uma SelectedValue propriedade, ele não pode ser usado como uma fonte de parâmetro. Se você verificar, verá que a lista suspensa ControlID contém apenas um controle ID``CategoryProducts, o ID da DataList.

Por enquanto, defina a lista suspensa Origem do parâmetro como Nenhum. Acabaremos atribuindo programaticamente esse valor de parâmetro quando uma categoria LinkButton for clicada no Repetidor.

Não especifique uma fonte de parâmetro para o parâmetro categoryID

Figura 13: Não especificar uma fonte de parâmetro para o parâmetro (clique para exibir a categoryID imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio gera automaticamente o DataList s ItemTemplate. Substitua este padrão ItemTemplate pelo modelo que usamos no tutorial anterior e defina a propriedade RepeatColumns de DataList como 2. Depois de fazer essas alterações, a marcação declarativa para sua DataList e seu ObjectDataSource associado deve ter a seguinte aparência:

<asp:DataList ID="CategoryProducts" runat="server" DataKeyField="ProductID"
    DataSourceID="CategoryProductsDataSource" RepeatColumns="2"
    EnableViewState="False">
    <ItemTemplate>
        <h5><%# Eval("ProductName") %></h5>
        <p>
            Supplied by <%# Eval("SupplierName") %><br />
            <%# Eval("UnitPrice", "{0:C}") %>
        </p>
    </ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="CategoryProductsDataSource"
    OldValuesParameterFormatString="original_{0}"  runat="server"
    SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
    <SelectParameters>
        <asp:Parameter Name="categoryID" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

Atualmente, o parâmetro do CategoryProductsDataSource ObjectDataSource categoryID nunca é definido, e assim, nenhum produto é exibido quando a página é visualizada. O que precisamos fazer é definir esse valor de parâmetro com base na CategoryID categoria clicada no Repetidor. Isso apresenta dois desafios: primeiro, como determinamos quando um LinkButton do Repetidor foi clicado ItemTemplate; e segundo, como podemos determinar o CategoryID da categoria correspondente em que o LinkButton foi clicado?

O LinkButton, assim como os controles Button e ImageButton, tem um Click evento e um Command evento. O Click evento foi projetado para simplesmente observar que o LinkButton foi clicado. Às vezes, no entanto, além de observar que o LinkButton foi clicado, também precisamos passar algumas informações extras para o manipulador de eventos. Se esse for o caso, as propriedades do LinkButton CommandName e CommandArgument podem receber essas informações extras. Em seguida, quando o LinkButton é clicado, seu Command evento é acionado (em vez de seu Click evento) e o manipulador de eventos recebe os valores das propriedades CommandName e CommandArgument.

Quando um Command evento é gerado de dentro de um modelo no Repetidor, o evento ItemCommand do Repetidor é acionado e são passados os valores CommandName e CommandArgument do LinkButton clicado (ou Button ou ImageButton). Portanto, para determinar quando uma categoria LinkButton no Repetidor foi clicada, precisamos fazer o seguinte:

  1. Defina a propriedade CommandName do LinkButton no ItemTemplate do Repeater para um valor (eu usei ListProducts). Ao definir esse CommandName valor, o evento do LinkButton Command é acionado quando o LinkButton é clicado.
  2. Defina a propriedade do LinkButton para o valor do item atual CommandArgumentCategoryID.
  3. Crie um manipulador de eventos para o ItemCommand evento do Repetidor. No manipulador de CategoryProductsDataSource eventos, defina o parâmetro CategoryID do ObjectDataSource para o valor fornecido por CommandArgument.

A marcação a seguir ItemTemplate para o Repetidor de Categorias implementa as etapas 1 e 2. Observe como o valor CommandArgument é vinculado ao item de dados CategoryID usando a sintaxe de vinculação de dados:

<ItemTemplate>
    <li>
        <asp:LinkButton CommandName="ListProducts"  runat="server"
            CommandArgument='<%# Eval("CategoryID") %>' ID="ViewCategory"
            Text='<%# string.Format("{0} ({1:N0})", _
                Eval("CategoryName"), Eval("NumberOfProducts")) %>'>
        </asp:LinkButton>
    </li>
</ItemTemplate>

Sempre que criar um ItemCommand manipulador de eventos, é prudente sempre verificar primeiro o valor de entrada CommandName , pois qualquerCommand evento gerado por qualquer Button, LinkButton ou ImageButton dentro do Repetidor fará com que o ItemCommand evento seja acionado. Embora atualmente tenhamos apenas um desses LinkButton, no futuro nós (ou outro desenvolvedor em nossa equipe) podemos adicionar controles Web de botão adicionais ao Repeater que, quando clicados, disparam o mesmo ItemCommand manipulador de eventos. Portanto, é melhor sempre verificar a CommandName propriedade e prosseguir com sua lógica programática apenas se ela corresponder ao valor esperado.

Depois de garantir que o valor passado em CommandName seja igual a ListProducts, o manipulador de eventos então atribui o parâmetro CategoryID do ObjectDataSource CategoryProductsDataSource ao valor passado em CommandArgument. Essa modificação no ObjectDataSource automaticamente faz com que o DataList realize a nova associação com a fonte de dados, mostrando os produtos para a categoria recém-selecionada.

Protected Sub Categories_ItemCommand(source As Object, e As RepeaterCommandEventArgs) _
    Handles Categories.ItemCommand
    ' If it's the "ListProducts" command that has been issued...
    If String.Compare(e.CommandName, "ListProducts", True) = 0 Then
        ' Set the CategoryProductsDataSource ObjectDataSource's CategoryID parameter
        ' to the CategoryID of the category that was just clicked (e.CommandArgument)...
        CategoryProductsDataSource.SelectParameters("CategoryID").DefaultValue = _
            e.CommandArgument.ToString()
    End If
End Sub

Com essas adições, nosso tutorial está completo! Reserve um momento para testá-lo em um navegador. A Figura 14 mostra a tela ao visitar a página pela primeira vez. Como uma categoria ainda não foi selecionada, nenhum produto é exibido. Clicar em uma categoria, como Produtos Frescos, exibe esses produtos na categoria Produto em uma visualização de duas colunas (consulte a Figura 15).

Nenhum produto é exibido ao visitar a página pela primeira vez

Figura 14: Nenhum produto é exibido ao visitar a página pela primeira vez (clique para exibir a imagem em tamanho real)

Clicando na categoria

Figura 15: Clicar na categoria Produzir lista os produtos correspondentes à direita (clique para exibir a imagem em tamanho real)

Resumo

Como vimos neste tutorial e no anterior, os relatórios mestre/detalhados podem ser distribuídos em duas páginas ou consolidados em uma. No entanto, a exibição de um relatório mestre/detalhes em uma única página apresenta alguns desafios sobre a melhor forma de fazer o layout dos registros mestre e detalhados na página. No tutorial Mestre/Detalhe Usando um GridView Mestre Selecionável com um DetailsView de Detalhes, fizemos com que os registros de detalhes aparecessem acima dos registros mestres; neste tutorial, usamos técnicas de CSS para que os registros mestres flutuassem à esquerda dos detalhes.

Além de exibir relatórios mestres/detalhes, também tivemos a oportunidade de explorar como recuperar o número de produtos associados a cada categoria, bem como executar a lógica do lado do servidor quando um LinkButton (ou Button ou ImageButton) é clicado de dentro de um Repetidor.

Este tutorial conclui nosso exame de relatórios mestre/detalhe com o DataList e o Repeater. Nosso próximo conjunto de tutoriais ilustrará como adicionar recursos de edição e exclusão ao controle DataList.

Boa programação!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Sobre o autor

Scott Mitchell, autor de sete livros asp/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor, instrutor e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 horas. Ele pode ser alcançado em mitchell@4GuysFromRolla.com.

Agradecimentos especiais a

Esta série de tutoriais foi revisada por muitos revisores úteis. O revisor principal deste tutorial foi Zack Jones. Interessado em revisar meus próximos artigos do MSDN? Se assim for, deixe-me uma linha em mitchell@4GuysFromRolla.com.