Mestre/detalhes usando uma lista com marcadores de registros mestre com um DataList de detalhes (VB)

por Scott Mitchell

Baixar PDF

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

Introdução

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

Neste tutorial, compactaremos o tutorial de duas páginas em uma única página, mostrando uma lista com marcadores de nomes de categoria no lado esquerdo da tela com cada nome de categoria renderizado como um LinkButton. Clicar em um dos nomes de categoria LinkButtons induz um postback e associa os produtos da categoria selecionada a um 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)

Etapa 1: Exibindo um repetidor na parte esquerda da tela

Para este tutorial, precisamos fazer com que a lista com marcadores de categorias apareça à esquerda dos produtos da categoria selecionada. O conteúdo em uma página da Web pode ser posicionado usando marcas de parágrafo de elementos HTML padrão, espaços sem interrupção, <table> s e assim por diante ou por meio de técnicas CSS (folha de estilos em cascata). Todos os nossos tutoriais até agora usaram técnicas de CSS para posicionamento. Quando criamos a interface do usuário de navegação em nossa página master no tutorial Páginas Mestras e Navegação no Site, usamos o posicionamento absoluto, indicando o deslocamento preciso de pixels para a lista de navegação e o conteúdo main. Como alternativa, o CSS pode ser usado para posicionar um elemento à direita ou à esquerda de outro por meio de flutuante. Podemos fazer com que a lista com marcadores de categorias apareça à esquerda dos produtos da categoria selecionada flutuando o Repetidor à esquerda do DataList

Abra a CategoriesAndProducts.aspx página da DataListRepeaterFiltering pasta e adicione à página um Repeater e um DataList. Defina o Repeater s ID como Categories e os DataLists como CategoryProducts. Vá para o modo de exibição Origem e coloque os controles Repeater e DataList em seus próprios <div> elementos. Ou seja, coloque o Repeater dentro de um <div> elemento primeiro e, em seguida, o DataList em seu próprio <div> elemento diretamente após o Repeater. Sua marcação neste ponto deve ser semelhante à seguinte:

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

Para flutuar o Repeater à esquerda do DataList, precisamos usar o float atributo de estilo CSS, da seguinte forma:

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

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

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

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

Em seguida, podemos substituir o <div> por <div class="FloatLeft">.

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

O Repetidor é flutuado para a esquerda da DataList

Figura 2: o repetidor é flutuado para a esquerda da DataList (clique para exibir a imagem em tamanho real)

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

Com a marcação ao redor de Repeater e DataList concluída, estamos prontos para associar os dados de 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 na classe code-behind da página ASP.NET. Considerando um determinado categoryID , podemos determinar o número de produtos associados chamando o ProductsBLL método da classe s GetProductsByCategoryID(categoryID) . Esse método retorna um ProductsDataTable objeto cuja Count propriedade indica quantos ProductsRow s existem, que é o número de produtos para o especificado categoryID. Podemos criar um ItemDataBound manipulador de eventos para o Repeater que, para cada categoria associada ao Repeater, chama o ProductsBLL método da classe s GetProductsByCategoryID(categoryID) e inclui sua contagem na saída.
  • Atualize o CategoriesDataTable no Conjunto de Dados Tipado para incluir uma NumberOfProducts coluna. Em seguida, podemos atualizar o GetCategories() método no CategoriesDataTable para incluir essas informações ou, como alternativa, 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, ele requer mais comunicações com o banco de dados. A chamada para o ProductsBLL método da classe s GetProductsByCategoryID(categoryID) no ItemDataBound manipulador de eventos adiciona uma chamada de banco de dados extra para cada categoria exibida no Repeater. Com essa técnica, há N + 1 chamadas de banco de dados, em que 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 CategoriesBLL método de classe s GetCategories() (ou GetCategoriesAndNumberOfProducts()), resultando, assim, em apenas uma viagem 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 chamado CategoriesDataSource por meio da marca inteligente Repeater. Em seguida, configure o CategoriesDataSource ObjectDataSource para que ele recupere seus dados do CategoriesBLL método da classe s GetCategories() .

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

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

Cada item no Categories Repetidor precisa ser clicável e, quando clicado, fazer com que DataList CategoryProducts exiba esses produtos para a categoria selecionada. Isso pode ser feito tornando cada categoria um hiperlink, vinculando-se novamente 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 ObjectDataSource da DataList precisa ser atualizado para exibir os produtos que pertencem à 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 esses 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 ou <a> elemento HyperLink em vez do LinkButton.

A marcação a seguir mostra a sintaxe declarativa para o Repeater e o ObjectDataSource. Observe que os modelos do Repetidor 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 do EnableViewState="False" da sintaxe declarativa do Repetidor). Na etapa 3, criaremos um manipulador de eventos para o evento Repeater ItemCommand no qual atualizaremos a coleção ObjectDataSource da SelectParameters DataList. No entanto, o Repetidor ItemCommandnão será acionado se o estado de exibição estiver desabilitado.

O LinkButton com o valor da ID propriedade de ViewCategory não tem sua Text propriedade definida. Se quiséssemos apenas exibir o nome da categoria, teríamos definido a propriedade Text declarativamente, por meio da sintaxe de associaçã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 que pertencem a essa categoria. Essas informações podem ser recuperadas do manipulador de eventos do ItemDataBound Repetidor fazendo uma chamada para o ProductBLL método da GetCategoriesByProductID(categoryID) classe e determinando quantos registros são retornados no resultante ProductsDataTable, 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 (aquele cujo é ou ) e, em seguida, referenciamos a CategoriesRow instância que acabou de ser associada ao atualRepeaterItem.AlternatingItemItemItemType 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 é referências e sua Text propriedade é definida como CategoryName (NumberOfProductsInCategory), em que NumberOfProductsInCategory é 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 da página ASP.NET que aceita uma categoria s CategoryName e valores e CategoryID retorna o CategoryName concatenado com o número de produtos na categoria (conforme determinado pela chamada do GetCategoriesByProductID(categoryID) método ). Os resultados dessa função de formatação podem ser atribuídos declarativamente à propriedade Text do LinkButton, substituindo a necessidade do ItemDataBound manipulador de eventos. Consulte os tutoriais Usando TemplateFields no controle GridView ou Formatação dos tutoriais DataList e Repeater Based Upon Data 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).

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

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

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

Em vez de determinar o número de produtos para cada categoria, pois ele está associado 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 para CategoriesDataTable 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 exibir a imagem em tamanho real)

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

Definir as propriedades DataType e ReadOnly da nova coluna

Figura 6: Definir as DataType propriedades e ReadOnly da nova coluna

Embora o CategoriesDataTable agora 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 de categoria forem recuperadas. Se, no entanto, só precisarmos pegar o número de produtos associados para as categorias em instâncias raras (como apenas para este tutorial), então podemos deixar GetCategories() como estão e criar um novo método que retorne essas informações. Vamos usar essa ú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 no 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 o uso da consulta. Para retornar os campos , CategoryNamee Description de cada categoriaCategoryID, juntamente com o número de produtos associados à categoria, use a seguinte SELECT instrução:

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

Especificar 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 é alias como 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 uma DataTable e Retornar uma DataTable, respectivamente.

Nomeie os métodos New TableAdapter s FillWithNumberOfProducts e GetCategoriesAndNumberOfProducts

Figura 10: nomear os novos métodos FillWithNumberOfProducts TableAdapter s 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 o 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 a BLL concluídos, estamos prontos para associar esses dados ao Categories Repetidor no CategoriesAndProducts.aspx! Se você já tiver criado um ObjectDataSource para o Repeater da seção Determinando o número de produtos no ItemDataBound manipulador de eventos, exclua este ObjectDataSource e remova a configuração da propriedade Repeater s DataSourceID ; também remova o evento Repeater do ItemDataBound manipulador de eventos removendo a Handles Categories.OnItemDataBound sintaxe na classe code-behind ASP.NET.

Com o Repeater de volta em seu estado original, adicione um novo ObjectDataSource chamado CategoriesDataSource por meio da marca inteligente Repeater. Configure o ObjectDataSource para usar a CategoriesBLL classe , mas em vez de tê-la usando o GetCategories() método , faça com que ela use GetCategoriesAndNumberOfProducts() em vez disso (consulte a Figura 11).

Configurar o ObjectDataSource para usar o método GetCategoriesAndNumberOfProducts

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

Em seguida, atualize o ItemTemplate para que a propriedade linkButton seja Text atribuída declarativamente usando a sintaxe de vinculação de dados e inclua os CategoryName campos de dados 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 atualizando o DAL para incluir uma NumberOfProducts coluna é a mesma que usar a abordagem do ItemDataBound manipulador de eventos (consulte a Figura 4 para ver uma captura de tela do Repetidor mostrando os nomes das categorias e o número de produtos).

Etapa 3: Exibindo os produtos das categorias selecionadas

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

Um desafio enfrentado por nós é como fazer com que a DataList exiba apenas esses produtos para a categoria selecionada. No tutorial Mestre/Detalhes usando um Selectable Master GridView com detalhesExibição de detalhes, vimos como criar um GridView cujas linhas poderiam 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 ProductsBLL método s 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 s SelectedValue . Infelizmente, o Repeater não tem uma SelectedValue propriedade e não pode servir como uma origem de parâmetro.

Observação

Esse é um desses desafios que aparecem ao usar o LinkButton em um Repeater. Se tivéssemos usado um hiperlink para passar o CategoryID por meio da querystring, poderíamos usar esse campo QueryString como a origem do valor do parâmetro.

No entanto, antes de nos preocuparmos com a falta de uma SelectedValue propriedade para o Repeater, vamos primeiro associar o DataList a um ObjectDataSource e especificar seu ItemTemplate.

Na marca inteligente DataList, opte por adicionar um novo ObjectDataSource chamado CategoryProductsDataSource e configurá-lo para usar o ProductsBLL método da classe s GetProductsByCategoryID(categoryID) . Como o DataList neste tutorial oferece uma interface somente leitura, fique à vontade para definir as listas suspensas nas guias INSERT, UPDATE e DELETE como (Nenhum).

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

Figura 12: Configurar o ObjectDataSource para usar ProductsBLL o método da GetProductsByCategoryID(categoryID) classe (clique para exibir a imagem em tamanho real)

Como o GetProductsByCategoryID(categoryID) método espera um parâmetro de entrada (categoryID), o assistente Configurar Fonte de Dados nos permite especificar a origem do parâmetro. Se as categorias tivessem sido listadas em um GridView ou dataList, definiríamos a lista suspensa Origem do parâmetro como Control e ControlID para o ID do controle da Web de dados. No entanto, como o Repeater não tem uma SelectedValue propriedade, ele não pode ser usado como uma fonte de parâmetro. Se você marcar, descobrirá que a lista suspensa ControlID contém apenas um controle ID``CategoryProducts, o ID de 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 origem de parâmetro para o parâmetro categoryID

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

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio gera automaticamente a DataList s ItemTemplate. Substitua esse padrão ItemTemplate pelo modelo que usamos no tutorial anterior; também, defina a propriedade DataList como RepeatColumns 2. Depois de fazer essas alterações, a marcação declarativa para seu DataList e seu ObjectDataSource associado deve ser semelhante ao seguinte:

<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 CategoryProductsDataSource parâmetro objectDataSource nunca categoryID é definido, portanto, nenhum produto é exibido ao exibir a página. O que precisamos fazer é ter esse valor de parâmetro definido com base na CategoryID categoria clicada no Repetidor. Isso apresenta dois desafios: primeiro, como determinar quando um LinkButton no Repeater s ItemTemplate foi clicado; e segundo, como podemos determinar o CategoryID da categoria correspondente cujo LinkButton foi clicado?

O LinkButton, 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 e CommandNameCommandArgument LinkButton poderão 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 é passado os valores das CommandName propriedades e CommandArgument .

Quando um Command evento é gerado de dentro de um modelo no Repeater, o evento Repeater é ItemCommand acionado e é passado os CommandName valores 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 CommandName propriedade do LinkButton no Repeater s ItemTemplate como algum valor (usei ListProducts ). Ao definir esse CommandName valor, o evento LinkButton é Command acionado quando o LinkButton é clicado.
  2. Defina a propriedade LinkButton s CommandArgument como o valor do item atual s CategoryID.
  3. Crie um manipulador de eventos para o evento Repeater s ItemCommand . No manipulador de eventos, defina o CategoryProductsDataSource parâmetro ObjectDataSource como CategoryID o valor do passado.CommandArgument

A marcação a seguir ItemTemplate para o Repetidor de Categorias implementa as etapas 1 e 2. Observe como o CommandArgument valor é atribuído ao item de CategoryID dados usando a sintaxe de associaçã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 marcar primeiro o valor de entrada CommandName porque qualquerCommand evento gerado por qualquer Button, LinkButton ou ImageButton dentro do Repeater fará com que o ItemCommand evento seja acionado. Embora atualmente tenhamos apenas um desses LinkButton agora, no futuro , nós (ou outro desenvolvedor em nossa equipe) podemos adicionar controles web de botão adicionais ao Repetidor que, quando clicado, aciona o mesmo ItemCommand manipulador de eventos. Portanto, é melhor sempre garantir que você marcar a CommandName propriedade e prossiga apenas com sua lógica programática se corresponder ao valor esperado.

Depois de garantir que o valor passado CommandName seja igual a ListProducts, o manipulador de eventos atribui o CategoryProductsDataSource parâmetro ObjectDataSource ao CategoryID valor do passado CommandArgument. Essa modificação no ObjectDataSource s SelectParameters faz com que a DataList se reassocia à 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 Produzir, exibe esses produtos na categoria Produto em uma exibiçã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 Produzir Listas os produtos correspondentes à direita

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

Resumo

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

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

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

Programação feliz!

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 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.

Agradecimentos Especiais

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? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.