Compartilhar via


Adicionar uma coluna de GridView de botões de opção (C#)

por Scott Mitchell

Baixar PDF

Este tutorial aborda como adicionar uma coluna de botões de opção a um controle GridView para fornecer ao usuário um modo mais intuitivo de selecionar uma única linha do GridView.

Introdução

O controle GridView oferece uma grande quantidade de funcionalidades internas. Ele inclui vários campos diferentes para exibir texto, imagens, hiperlinks e botões. Ele dá suporte a modelos para personalização adicional. Com alguns cliques do mouse, é possível fazer um GridView em que cada linha pode ser selecionada por meio de um botão ou habilitar a edição ou exclusão de recursos. Apesar da infinidade de recursos fornecidos, muitas vezes haverá situações em que recursos adicionais e sem suporte precisarão ser adicionados. Neste tutorial e nos próximos dois, examinaremos como aprimorar a funcionalidade do GridView para incluir recursos adicionais.

Este tutorial e o próximo se concentram em aprimorar o processo de seleção de linhas. Conforme examinado no Mestre/Detalhe usando um Selectable Master GridView com um Details DetailView, podemos adicionar um CommandField ao GridView que inclui um botão Selecionar. Quando clicado, ocorre um postback e a propriedade SelectedIndex do GridView é atualizada para o índice da linha cujo botão Selecionar foi clicado. No tutorial Master/Detail Usando um GridView Selecionável como Mestre com um DetailView de Detalhes, vimos como utilizar esse recurso para exibir os detalhes da linha selecionada no GridView.

Embora o botão Selecionar funcione em muitas situações, ele pode não funcionar tão bem para outras pessoas. Em vez de usar um botão, dois outros elementos de interface do usuário são comumente usados para seleção: o botão de opção e a caixa de seleção. Podemos modificar o GridView para que, em vez de um botão de seleção, cada linha contenha um botão de opção ou uma caixa de seleção. Em cenários em que o usuário só pode selecionar um dos registros do GridView, o botão de rádio pode ser preferido ao botão Selecionar. Em situações em que o usuário pode selecionar múltiplos registros, como em um aplicativo de e-mail na web, onde o usuário talvez queira selecionar várias mensagens para excluir, a caixa de seleção oferece uma funcionalidade que não está disponível nas interfaces de usuário dos botões Selecionar ou de opção.

Este tutorial analisa como adicionar uma coluna de botões de rádio ao GridView. O tutorial em andamento explora o uso de caixas de seleção.

Etapa 1: Criando as páginas da Web de Aprimoramento do GridView

Antes de começarmos a aprimorar o GridView para incluir uma coluna de botões de opção, vamos primeiro reservar um tempo para criar as páginas ASP.NET em nosso projeto de website que serão necessários para este tutorial e os próximos dois. Comece adicionando uma nova pasta chamada EnhancedGridView. Em seguida, adicione as seguintes páginas ASP.NET a essa pasta, certificando-se de associar cada página à Site.master página mestra:

  • Default.aspx
  • RadioButtonField.aspx
  • CheckBoxField.aspx
  • InsertThroughFooter.aspx

Adicionar as páginas de ASP.NET para os tutoriais do SqlDataSource-Related

Figura 1: Adicionar as páginas de ASP.NET para os tutoriais do SqlDataSource-Related

Como nas outras pastas, Default.aspx na EnhancedGridView pasta listará os tutoriais em sua seção. Lembre-se de que o SectionLevelTutorialListing.ascx Controle de Usuário fornece essa funcionalidade. Portanto, adicione esse Controle de Usuário arrastando-o Default.aspx do Gerenciador de Soluções para o modo de exibição Design da página.

Adicione o controle de usuário SectionLevelTutorialListing.ascx ao Default.aspx

Figura 2: Adicionar o controle de SectionLevelTutorialListing.ascx usuário (Default.aspxclique para exibir a imagem em tamanho real)

Por fim, adicione essas quatro páginas como entradas ao Web.sitemap arquivo. Especificamente, adicione a seguinte marcação após o Uso do Controle SqlDataSource <siteMapNode>:

<siteMapNode 
    title="Enhancing the GridView" 
    url="~/EnhancedGridView/Default.aspx" 
    description="Augment the user experience of the GridView control.">
    <siteMapNode 
        url="~/EnhancedGridView/RadioButtonField.aspx" 
        title="Selection via a Radio Button Column" 
        description="Explore how to add a column of radio buttons in the GridView." />
    <siteMapNode 
        url="~/EnhancedGridView/CheckBoxField.aspx" 
        title="Selection via a Checkbox Column" 
        description="Select multiple records in the GridView by using a column of 
            checkboxes." />
    <siteMapNode 
        url="~/EnhancedGridView/InsertThroughFooter.aspx" 
        title="Add New Records through the Footer" 
        description="Learn how to allow users to add new records through the 
            GridView's footer." />
</siteMapNode>

Após a atualização Web.sitemap, reserve um momento para visualizar o site de tutoriais por meio de um navegador. O menu à esquerda agora inclui itens para os tutoriais de edição, inserção e exclusão.

O mapa do site agora inclui entradas para aprimorar os tutoriais do GridView

Figura 3: O mapa do site agora inclui entradas para aprimorar os tutoriais do GridView

Etapa 2: Exibindo os fornecedores em um GridView

Para este tutorial, vamos criar um GridView que lista os fornecedores dos Estados Unidos, com cada linha do GridView fornecendo um botão de rádio. Depois de selecionar um fornecedor por meio do botão de seleção, o usuário pode visualizar os produtos do fornecedor clicando em um botão. Embora essa tarefa possa soar trivial, há uma série de sutilezas que a tornam particularmente complicada. Antes de nos aprofundarmos nessas sutilezas, vamos primeiro obter um GridView listando os fornecedores.

Comece abrindo a página RadioButtonField.aspx na pasta EnhancedGridView ao arrastar um GridView do Painel de Ferramentas para a área de design. Defina o GridView como IDSuppliers e, a partir da sua smart tag, escolha criar uma nova fonte de dados. Especificamente, crie um ObjectDataSource chamado SuppliersDataSource que extraia seus dados do SuppliersBLL objeto.

Criar um novo ObjectDataSource chamado SuppliersDataSource

Figura 4: Criar um Novo ObjectDataSource Nomeado SuppliersDataSource (Clique para exibir imagem em tamanho real)

Captura de tela da janela Configurar Fonte de Dados – SuppliersDataSource com o objeto de negócios SuppliersBLL selecionado e o botão Avançar realçado.

Figura 5: Configurar o ObjectDataSource para usar a classe (clique para exibir a SuppliersBLL imagem em tamanho completo)

Como só queremos listar os fornecedores nos EUA, escolha o método GetSuppliersByCountry(country) na lista suspensa na guia SELECT.

Captura de tela da janela Configurar Fonte de Dados – SuppliersDataSource com a guia SELECT aberta. A opção de método GetSupplierByCountry está selecionada e o botão Avançar está realçado.

Figura 6: Configurar o ObjectDataSource para usar a classe (clique para exibir a SuppliersBLL imagem em tamanho completo)

Na guia UPDATE, selecione a opção (Nenhum) e clique em Avançar.

Captura de tela da janela Configurar Fonte de Dados – SuppliersDataSource com a guia UPDATE aberta. A opção de método (Nenhum) está selecionada e o botão Avançar está realçado.

Figura 7: Configurar o ObjectDataSource para usar a SuppliersBLL classe (clique para exibir a imagem em tamanho real)

Como o GetSuppliersByCountry(country) método aceita um parâmetro, o assistente Configurar Fonte de Dados nos solicita a origem desse parâmetro. Para especificar um valor embutido em código ( EUA, neste exemplo), deixe a lista suspensa de origem do parâmetro definida como Nenhum e insira o valor padrão na caixa de texto. Clique em "Finalizar" para completar o assistente.

Usar EUA como o valor padrão para o parâmetro de país

Figura 8: Usar EUA como o valor padrão para o country parâmetro (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente de configuração, o GridView incluirá um BoundField para cada um dos campos de dados do fornecedor. Remova todos, exceto o CompanyName, Citye Country BoundFields, e renomeie a CompanyName propriedade BoundFields HeaderText como Supplier. Depois de fazer isso, a sintaxe declarativa GridView e ObjectDataSource deve ser semelhante à seguinte.

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
    DataKeyNames="SupplierID" DataSourceID="SuppliersDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliersByCountry" TypeName="SuppliersBLL">
    <SelectParameters>
        <asp:Parameter DefaultValue="USA" Name="country" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

Para este tutorial, vamos permitir que o usuário exiba os produtos do fornecedor selecionado na mesma página da lista de fornecedores ou em uma página diferente. Para acomodar isso, adicione dois controles Web de botão à página. Configurei os ID dos dois Botões para ListProducts e SendToProducts, com a ideia de que, quando ListProducts for clicado, ocorrerá um postback e os produtos do fornecedor selecionado serão listados na mesma página, mas quando SendToProducts for clicado, o usuário será rapidamente redirecionado para outra página que lista os produtos.

A Figura 9 mostra o Suppliers GridView e os dois controles web de botão quando exibidos por meio de um navegador.

Esses fornecedores dos EUA têm suas informações de nome, cidade e país listadas

Figura 9: Os fornecedores dos EUA têm suas informações de nome, cidade e país listadas (clique para exibir a imagem em tamanho real)

Etapa 3: Adicionando uma coluna de botões de opção

Neste ponto, o Suppliers GridView tem três BoundFields exibindo o nome da empresa, a cidade e o país de cada fornecedor nos EUA. No entanto, ainda falta uma coluna de botões de opção. Infelizmente, o GridView não inclui um campo RadioButton embutido, caso contrário, poderíamos apenas adicioná-lo à grade e estar pronto. Em vez disso, podemos adicionar um TemplateField e configurá-lo ItemTemplate para renderizar um botão de opção, o que resulta em um botão de opção para cada linha do GridView.

Inicialmente, podemos supor que a interface do usuário desejada possa ser implementada adicionando um controle Web RadioButton ao ItemTemplate de um TemplateField. Embora isso realmente adicione um único botão de opção a cada linha do GridView, os botões de opção não podem ser agrupados e, portanto, não são mutuamente exclusivos. Ou seja, um usuário final pode selecionar vários botões de opção simultaneamente no GridView.

Embora o uso de um TemplateField de controles Web RadioButton não ofereça a funcionalidade necessária, vamos implementar essa abordagem, pois vale a pena examinar por que os botões de rádio resultantes não estão agrupados. Comece adicionando um TemplateField ao Suppliers GridView, tornando-o o campo mais à esquerda. Em seguida, na tag inteligente do GridView, clique no link Editar Modelos e arraste um controle Web de RadioButton da Caixa de Ferramentas para o TemplateField ItemTemplate (consulte a Figura 10). Defina a propriedade do RadioButton ID para RowSelector e a propriedade GroupName para SuppliersGroup.

Adicionar um controle Web RadioButton ao ItemTemplate

Figura 10: Adicionar um controle Web RadioButton à ItemTemplate(Clique para exibir a imagem em tamanho real)

Depois de fazer essas adições por meio do Designer, a marcação do GridView deve ser semelhante à seguinte:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
    DataKeyNames="SupplierID" DataSourceID="SuppliersDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:RadioButton ID="RowSelector" runat="server" 
                    GroupName="SuppliersGroup" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
    </Columns>
</asp:GridView>

A propriedade RadioButton GroupName é usada para agrupar uma série de botões de rádio. Todos os controles RadioButton com o mesmo GroupName valor são considerados agrupados; somente um botão de opção pode ser selecionado em um grupo de cada vez. A propriedade GroupName especifica o valor do atributo name para o botão de opção renderizado. O navegador examina os atributos dos botões de opção name para determinar seus agrupamentos.

Com o controle Web RadioButton adicionado ao ItemTemplate, visite esta página por meio de um navegador e clique nos botões de opção nas linhas da grade. Observe como os botões de rádio não estão agrupados, permitindo selecionar todas as linhas, como mostra a Figura 11.

Os botões de opção do GridView não estão agrupados

Figura 11: Os botões de opção do GridView não são agrupados (clique para exibir a imagem em tamanho real)

O motivo pelo qual os botões de opção não estão agrupados é que seus atributos renderizados name são diferentes, apesar de terem a mesma configuração de propriedade GroupName. Para ver essas diferenças, faça uma verificação do código-fonte no navegador e examine a marcação do botão de rádio.

<input id="ctl00_MainContent_Suppliers_ctl02_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl02$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl03_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl03$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl04_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl04$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl05_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl05$SuppliersGroup" 
    type="radio" value="RowSelector" />

Observe como os atributos name e id não correspondem exatamente aos valores especificados na janela de propriedades, mas são precedidos por vários outros valores ID. Os valores adicionais ID adicionados à frente dos atributos renderizados id e name são os IDs dos controles pai dos botões de opção, que controlam os GridViewRows ID, os GridView ID, os controles de conteúdo ID e os formulários da Web ID. Esses ID são adicionados para que cada controle Web renderizado no GridView tenha valores id e name exclusivos.

Cada controle renderizado precisa de um identificador diferente name e id porque é assim que o navegador identifica de forma exclusiva cada controle no lado do cliente e como informa ao servidor web qual ação ou alteração ocorreu no retorno de dados. Por exemplo, imagine que queríamos executar algum código do lado do servidor sempre que um estado verificado do RadioButton fosse alterado. Poderíamos fazer isso definindo a propriedade AutoPostBack do RadioButton para true e criando um manipulador de eventos para o evento CheckChanged. No entanto, se os valores name e id renderizados para todos os botões de opção fossem iguais, no postback não seria possível determinar qual RadioButton específico foi clicado.

O resumo é que não é possível criar uma coluna de botões de opção em um GridView usando o controle Web RadioButton. Em vez disso, devemos usar técnicas bastante arcaicas para garantir que a marcação apropriada seja injetada em cada linha gridView.

Observação

Assim como o controle Web RadioButton, o controle HTML do botão de rádio, quando adicionado a um modelo, incluirá o atributo exclusivo name, tornando os botões de rádio na grade não agrupados. Se você não estiver familiarizado com controles HTML, fique à vontade para desconsiderar essa observação, pois os controles HTML raramente são usados, especialmente em ASP.NET 2.0. Porém, se você estiver interessado em aprender mais, consulte a entrada no blog de K. Scott Allen sobre Controles Web e Controles HTML.

Usando um controle literal para injetar código de botão de opção

Para agrupar corretamente todos os botões de opção dentro do GridView, precisamos injetar manualmente o código dos botões de opção no ItemTemplate. Cada botão de opção precisa do mesmo atributo name, mas deve ter um atributo exclusivo id (caso queiramos acessar um botão de opção por meio de um script do cliente). Depois que um usuário selecionar um botão de rádio e submeter a página novamente, o navegador enviará de volta o valor do atributo value do botão de rádio selecionado. Portanto, cada botão de opção precisará de um atributo value exclusivo. Por fim, no postback, precisamos garantir que o atributo checked seja adicionado ao botão de opção selecionado. Caso contrário, depois que o usuário fizer uma seleção e enviar novamente, os botões de opção retornarão ao seu estado padrão (todos não selecionados).

Há duas abordagens que podem ser tomadas para injetar marcação de baixo nível em um modelo. Uma das abordagens é fazer uma combinação de marcação e chamadas para métodos de formatação definidos na classe de código subjacente (code-behind). Essa técnica foi discutida pela primeira vez no tutorial Usando TemplateFields no controle GridView . No nosso caso, pode ser algo parecido com:

<input type="radio" id='<%# GetUniqueRadioButtonID(...) %>' 
    name='SuppliersGroup' value='<%# GetRadioButtonValue(...) %>' ... />

Aqui, GetUniqueRadioButton e GetRadioButtonValue seriam métodos definidos na classe code-behind que retornavam os valores de atributos apropriados id e value para cada botão de rádio. Essa abordagem funciona bem para atribuir os id atributos e value , mas fica aquém ao precisar especificar o valor do checked atributo porque a sintaxe de associação de dados só é executada quando os dados são associados pela primeira vez ao GridView. Portanto, se o GridView tiver o estado de exibição habilitado, os métodos de formatação serão acionados somente quando a página for carregada pela primeira vez (ou quando o GridView for explicitamente recuperado para a fonte de dados) e, portanto, a função que define o checked atributo não será chamada no postback. É um problema bastante sutil e um pouco além do escopo deste artigo, então vou deixá-lo nisso. No entanto, eu encorajo você a tentar usar a abordagem acima e seguir até o ponto em que você ficar preso. Embora esse exercício não o aproxime de uma versão de trabalho, ele ajudará a promover uma compreensão mais profunda do GridView e do ciclo de vida de vinculação de dados.

A outra abordagem para injetar marcação personalizada de baixo nível em um modelo e a abordagem que usaremos para este tutorial é adicionar um controle Literal ao modelo. Em seguida, no manipulador de eventos RowCreated ou RowDataBound do GridView, o controle Literal pode ser acessado programaticamente, e sua propriedade Text pode ser definida como a marcação a ser emitida.

Comece removendo o RadioButton do TemplateField s ItemTemplate, substituindo-o por um controle Literal. Defina o controle literal s ID para RadioButtonMarkup.

Adicionar um controle literal ao ItemTemplate

Figura 12: Adicionar um controle literal ao ItemTemplate (Clique para ver a imagem em tamanho real)

Em seguida, crie um manipulador de eventos para o evento GridView RowCreated . O RowCreated evento é acionado uma vez para cada linha adicionada, independentemente de os dados estarem sendo recuperados ou não para o GridView. Isso significa que, mesmo em um postback quando os dados são recarregados do estado de exibição, o evento RowCreated ainda é disparado, e é por isso que optamos por usá-lo em vez de RowDataBound (que é disparado somente quando os dados são diretamente vinculados ao controle Web de dados).

Nesse manipulador de eventos, só queremos continuar se estivermos lidando com uma linha de dados. Para cada linha de dados, queremos referenciar programaticamente o RadioButtonMarkup controle Literal e definir sua Text propriedade para a marcação a ser emitida. Como mostra o código a seguir, a marcação emitida cria um botão de opção que tem um atributo name definido como SuppliersGroup, um atributo id definido como RowSelectorX, onde X é o índice da linha do GridView, e um atributo value definido como o índice da linha do GridView.

protected void Suppliers_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Grab a reference to the Literal control
        Literal output = (Literal)e.Row.FindControl("RadioButtonMarkup");
        // Output the markup except for the "checked" attribute
        output.Text = string.Format(
            @"<input type="radio" name="SuppliersGroup" " +
            @"id="RowSelector{0}" value="{0}" />", e.Row.RowIndex);
    }
}

Quando uma linha do GridView é selecionada e ocorre um postback, estamos interessados no SupplierID do fornecedor selecionado. Portanto, pode-se pensar que o valor de cada botão de opção deve ser o verdadeiro SupplierID (em vez do índice da linha do GridView). Embora isso possa funcionar em determinadas circunstâncias, seria um risco de segurança aceitar e processar cegamente uma SupplierID. Nosso GridView, por exemplo, lista apenas os fornecedores nos EUA. No entanto, se o SupplierID for transmitido diretamente do botão de opção, o que pode impedir um usuário travesso de manipular o SupplierID enviado de volta no postback? Usando o índice de linha como o value e, em seguida, obtendo o SupplierID no postback da coleção DataKeys, podemos garantir que o usuário esteja usando apenas um dos valores SupplierID associados a uma das linhas do GridView.

Depois de adicionar esse código do manipulador de eventos, tire um minuto para testar a página em um navegador. Primeiro, observe que apenas um botão de opção na tabela pode ser selecionado por vez. No entanto, ao selecionar um botão de opção e clicar em um dos botões, ocorrerá um postback e todos os botões de opção retornarão ao estado inicial (ou seja, no postback, o botão de opção selecionado não estará mais selecionado). Para corrigir isso, precisamos ampliar o RowCreated manipulador de eventos para que ele inspecione o índice do botão de opção selecionado enviado pelo postback e adicione o atributo checked="checked" à marcação gerada quando o índice da linha corresponder.

Quando ocorre um postback, o navegador envia de volta o name e o value do botão de opção selecionado. O valor pode ser recuperado programaticamente usando Request.Form["name"]. A Request.Form propriedade fornece um NameValueCollection que representa as variáveis de formulário. As variáveis de formulário são os nomes e valores dos campos de formulário na página da Web e são enviadas de volta pelo navegador da Web sempre que um postback ocorre. Como o atributo renderizado name dos botões de opção no GridView é SuppliersGroup, quando a página da Web é enviada de volta, o navegador enviará SuppliersGroup=valueOfSelectedRadioButton de volta para o servidor da Web (juntamente com os outros campos de formulário). Essas informações podem ser acessadas da Request.Form propriedade usando: Request.Form["SuppliersGroup"].

Como precisaremos determinar o índice do botão de opção selecionado não apenas no RowCreated manipulador de eventos, mas também nos Click manipuladores de eventos para os controles Web de Botão, vamos adicionar uma SuppliersSelectedIndex propriedade à classe code-behind que retorna -1 se nenhum botão de opção tiver sido selecionado e o índice do botão de opção selecionado se um dos botões de opção estiver selecionado.

private int SuppliersSelectedIndex
{
    get
    {
        if (string.IsNullOrEmpty(Request.Form["SuppliersGroup"]))
            return -1;
        else
            return Convert.ToInt32(Request.Form["SuppliersGroup"]);
    }
}

Com essa propriedade adicionada, sabemos que devemos adicionar a marcação checked="checked" no manipulador de eventos RowCreated quando SuppliersSelectedIndex for igual a e.Row.RowIndex. Atualize o manipulador de eventos para incluir essa lógica:

protected void Suppliers_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Grab a reference to the Literal control
        Literal output = (Literal)e.Row.FindControl("RadioButtonMarkup");
        // Output the markup except for the "checked" attribute
        output.Text = string.Format(
            @"<input type="radio" name="SuppliersGroup" " +
            @"id="RowSelector{0}" value="{0}"", e.Row.RowIndex);
        // See if we need to add the "checked" attribute
        if (SuppliersSelectedIndex == e.Row.RowIndex)
            output.Text += @" checked="checked"";
        // Add the closing tag
        output.Text += " />";
    }
}

Com essa alteração, o botão de opção selecionado permanece selecionado após um postback. Agora que temos a capacidade de especificar qual botão de opção está selecionado, podemos alterar o comportamento para que, quando a página for visitada pela primeira vez, o primeiro botão de opção da linha GridView seja selecionado (em vez de não ter botões de opção selecionados por padrão, que é o comportamento atual). Para que o primeiro botão de rádio seja selecionado por padrão, basta alterar a instrução if (SuppliersSelectedIndex == e.Row.RowIndex) para o seguinte: if (SuppliersSelectedIndex == e.Row.RowIndex || (!Page.IsPostBack && e.Row.RowIndex == 0)).

Neste ponto, adicionamos uma coluna de botões de opção agrupados ao GridView que permite que uma única linha GridView seja selecionada e lembrada entre postbacks. Nossas próximas etapas são exibir os produtos fornecidos pelo fornecedor selecionado. Na Etapa 4, veremos como redirecionar o usuário para outra página, enviando o item selecionado SupplierID. Na Etapa 5, veremos como exibir os produtos do fornecedor selecionado em um GridView na mesma página.

Observação

Em vez de usar um TemplateField (o foco desta longa Etapa 3), poderíamos criar uma classe personalizada DataControlField que renderiza a interface e a funcionalidade do usuário apropriadas. A DataControlField classe é a classe base da qual os campos BoundField, CheckBoxField, TemplateField e outros campos internos GridView e DetailsView derivam. Criar uma classe personalizada DataControlField significaria que a coluna de botões de opção poderia ser adicionada apenas usando sintaxe declarativa e também facilitaria significativamente a replicação da funcionalidade em outras páginas da Web e em outros aplicativos Web.

Se você já criou controles personalizados e compilados em ASP.NET, no entanto, você sabe que fazer isso requer uma boa quantidade de trabalho e carrega consigo uma série de sutilezas e casos de borda que devem ser cuidadosamente tratados. Portanto, abriremos mão da implementação de uma coluna de botões de opção como uma classe personalizada DataControlField por enquanto e manteremos a opção TemplateField. Talvez tenhamos a chance de explorar a criação, o uso e a implantação de classes personalizadas DataControlField em um tutorial futuro!

Etapa 4: Exibindo os produtos do fornecedor selecionado em uma página separada

Depois que o usuário tiver selecionado uma linha GridView, precisamos mostrar os produtos do fornecedor selecionado. Em algumas circunstâncias, talvez queiramos exibir esses produtos em uma página separada, em outras, talvez prefira fazê-lo na mesma página. Primeiro, vamos examinar como exibir os produtos em uma página separada; Na Etapa 5, examinaremos a adição de um GridView para RadioButtonField.aspx exibir os produtos do fornecedor selecionado.

Atualmente, há dois controles Web de botão na página ListProducts e SendToProducts. Quando o SendToProducts Botão é clicado, queremos enviar o usuário para ~/Filtering/ProductsForSupplierDetails.aspx. Esta página foi criada no tutorial Filtragem mestre/detalhe entre duas páginas e exibe os produtos para o fornecedor cujo SupplierID é passado pelo campo de querystring chamado SupplierID.

Para fornecer essa funcionalidade, crie um manipulador de eventos para o evento do botão SendToProductsClick. Na Etapa 3, nós adicionamos a propriedade SuppliersSelectedIndex, que retorna o índice da linha com o botão de opção selecionado. O correspondente SupplierID pode ser recuperado da coleção GridView DataKeys e então o usuário pode ser enviado para ~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID usando Response.Redirect("url").

protected void SendToProducts_Click(object sender, EventArgs e)
{
    // Send the user to ~/Filtering/ProductsForSupplierDetails.aspx
    int supplierID = 
        Convert.ToInt32(Suppliers.DataKeys[SuppliersSelectedIndex].Value);
    Response.Redirect(
        "~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=" 
        + supplierID);
    }
}

Esse código funciona maravilhosamente, contanto que um dos botões de opção seja selecionado no GridView. Se, inicialmente, o GridView não tiver nenhum botão de opção selecionado e o usuário clicar no botão SendToProducts, SuppliersSelectedIndex será -1, o que causará uma exceção, pois -1 está fora do intervalo de índice da coleção DataKeys. Isso não é um problema, no entanto, se você decidiu atualizar o RowCreated manipulador de eventos conforme discutido na Etapa 3 para ter o primeiro botão de opção no GridView inicialmente selecionado.

Para acomodar um SuppliersSelectedIndex valor de -1, adicione um controle Web Label na página acima do GridView. Defina sua ID propriedade para ChooseSupplierMsg, sua CssClass propriedade para Warning, suas EnableViewState e Visible propriedades para false, e sua Text propriedade para, "Escolha, por favor, um fornecedor na grade." A classe Warning CSS exibe o texto em uma fonte vermelha, itálica, em negrito, grande e é definida em Styles.css. Ao definir as propriedades EnableViewState e Visible como false, o Rótulo não é renderizado, exceto para os postbacks em que a propriedade Visible do controle é programaticamente definida como true.

Adicionar um controle Web de rótulo acima do GridView

Figura 13: Adicionar um controle Web de rótulo acima do GridView (clique para exibir a imagem em tamanho real)

Em seguida, melhore o Click manipulador de eventos para exibir o ChooseSupplierMsg Rótulo se SuppliersSelectedIndex for menor que zero e redirecione o usuário para ~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID, caso contrário.

protected void SendToProducts_Click(object sender, EventArgs e)
{
    // make sure one of the radio buttons has been selected
    if (SuppliersSelectedIndex < 0)
        ChooseSupplierMsg.Visible = true;
    else
    {
        // Send the user to ~/Filtering/ProductsForSupplierDetails.aspx
        int supplierID = 
            Convert.ToInt32(Suppliers.DataKeys[SuppliersSelectedIndex].Value);
        Response.Redirect(
            "~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=" 
            + supplierID);
    }
}

Visite a página em um navegador e clique no SendToProducts botão antes de selecionar um fornecedor no GridView. Como mostra a Figura 14, isso exibe o ChooseSupplierMsg rótulo. Em seguida, selecione um fornecedor e clique no SendToProducts botão. Isso levará você a uma página que lista os produtos fornecidos pelo fornecedor selecionado. A Figura 15 mostra a página ProductsForSupplierDetails.aspx quando o fornecedor Bigfoot Breweries foi selecionado.

O rótulo ChooseSupplierMsg será exibido se nenhum fornecedor estiver selecionado

Figura 14: O ChooseSupplierMsg rótulo será exibido se nenhum fornecedor estiver selecionado (clique para exibir a imagem em tamanho real)

Os produtos do fornecedor selecionado são exibidos no ProductsForSupplierDetails.aspx

Figura 15: Os produtos do fornecedor selecionado são exibidos ProductsForSupplierDetails.aspx (clique para exibir a imagem em tamanho real)

Etapa 5: Exibindo os produtos do fornecedor selecionado na mesma página

Na Etapa 4, vimos como enviar o usuário para outra página da Web para exibir os produtos do fornecedor selecionado. Como alternativa, os produtos do fornecedor selecionado podem ser exibidos na mesma página. Para ilustrar isso, adicionaremos outro GridView para RadioButtonField.aspx exibir os produtos do fornecedor selecionado.

Como só queremos que este GridView de produtos seja exibido depois que um fornecedor tiver sido selecionado, adicione um controle Web de painel abaixo do GridView, configurando o seu atributo Suppliers como ID e sua propriedade ProductsBySupplierPanel como Visible. No Painel, adicione o texto Produtos para o Fornecedor Selecionado, seguido por um GridView chamado ProductsBySupplier. Na smart tag do componente GridView, escolha vinculá-la a um novo ObjectDataSource chamado ProductsBySupplierDataSource.

Associar o ProductsBySupplier GridView a um Novo ObjectDataSource

Figura 16: Associar o ProductsBySupplier GridView a um Novo ObjectDataSource (Clique para exibir a imagem em tamanho real)

Em seguida, configure o ObjectDataSource para usar a ProductsBLL classe. Como só queremos recuperar os produtos fornecidos pelo fornecedor selecionado, especifique que o ObjectDataSource deve invocar o GetProductsBySupplierID(supplierID) método para recuperar seus dados. Selecione a opção (Nenhum) nas listas suspensas das guias UPDATE, INSERT e DELETE.

Configurar o ObjectDataSource para usar o método GetProductsBySupplierID(supplierID)

Figura 17: Configurar o ObjectDataSource para usar o GetProductsBySupplierID(supplierID) método (clique para exibir a imagem em tamanho real)

Definir as listas de Drop-Down como (Nenhuma) nas guias UPDATE, INSERT e DELETE

Figura 18: Defina as listas de Drop-Down como (Nenhuma) nas guias UPDATE, INSERT e DELETE (Clique para exibir a imagem em tamanho real)

Depois de configurar as guias SELECT, UPDATE, INSERT e DELETE, clique em Avançar. Como o GetProductsBySupplierID(supplierID) método espera um parâmetro de entrada, o assistente Criar Fonte de Dados solicita que especifiquemos a origem do valor do parâmetro.

Temos algumas opções aqui para especificar a origem do valor do parâmetro. Poderíamos usar o objeto de parâmetro padrão e atribuir programaticamente o valor da propriedade SuppliersSelectedIndex à propriedade do parâmetro DefaultValue no manipulador de eventos do ObjectDataSource Selecting. Retorne ao tutorial Configuração Programática dos Valores de Parâmetro do ObjectDataSource para uma revisão sobre como atribuir valores aos parâmetros do ObjectDataSource de forma programática.

Como alternativa, podemos usar um ControlParameter e fazer referência à Suppliers propriedade GridView SelectedValue (consulte a Figura 19). A propriedade GridView SelectedValue retorna o DataKey valor correspondente à SelectedIndex propriedade. Para que essa opção funcione, precisamos definir programaticamente a propriedade GridView SelectedIndex como a linha selecionada quando o ListProducts botão é clicado. Como um benefício adicional, ao definir o SelectedIndex, o registro selecionado assumirá o SelectedRowStyle definido no Tema DataWebControls (um plano de fundo amarelo).

Usar um ControlParameter para especificar SelectedValue do GridView como a origem do parâmetro

Figura 19: Usar um ControlParameter para especificar SelectedValue do GridView como a origem do parâmetro (clique para exibir a imagem em tamanho real)

Ao concluir o assistente, o Visual Studio adicionará automaticamente campos para os campos de dados do produto. Remova todos, exceto os ProductName, CategoryName e UnitPrice BoundFields, e altere as propriedades HeaderText para Produto, Categoria e Preço. Configure o UnitPrice BoundField para que seu valor seja formatado como uma moeda. Depois de fazer essas alterações, a marcação declarativa do Painel, GridView e ObjectDataSource deve ser semelhante à seguinte:

<asp:Panel runat="server" ID="ProductsBySupplierPanel" Visible="False">
    <h3>
        Products for the Selected Supplier</h3>
    <p>
        <asp:GridView ID="ProductsBySupplier" runat="server" 
            AutoGenerateColumns="False" DataKeyNames="ProductID"
            DataSourceID="ProductsBySupplierDataSource" EnableViewState="False">
            <Columns>
                <asp:BoundField DataField="ProductName" HeaderText="Product" 
                    SortExpression="ProductName" />
                <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                    ReadOnly="True" SortExpression="CategoryName" />
                <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
                    HeaderText="Price" HtmlEncode="False" 
                    SortExpression="UnitPrice" />
            </Columns>
        </asp:GridView>
        <asp:ObjectDataSource ID="ProductsBySupplierDataSource" runat="server" 
            OldValuesParameterFormatString="original_{0}"
            SelectMethod="GetProductsBySupplierID" TypeName="ProductsBLL">
            <SelectParameters>
                <asp:ControlParameter ControlID="Suppliers" Name="supplierID" 
                    PropertyName="SelectedValue" Type="Int32" />
            </SelectParameters>
        </asp:ObjectDataSource>
    </p>
</asp:Panel>

Para concluir este exercício, precisamos definir a propriedade do GridView SelectedIndex para SelectedSuppliersIndex e a propriedade do Painel ProductsBySupplierPanel para Visible quando o botão true for clicado. Para fazer isso, crie um manipulador de eventos para o evento do controle Web do botão ListProducts e adicione o seguinte código:

protected void ListProducts_Click(object sender, EventArgs e)
{
    // make sure one of the radio buttons has been selected
    if (SuppliersSelectedIndex < 0)
    {
        ChooseSupplierMsg.Visible = true;
        ProductsBySupplierPanel.Visible = false;
    }
    else
    {
        // Set the GridView's SelectedIndex
        Suppliers.SelectedIndex = SuppliersSelectedIndex;
        // Show the ProductsBySupplierPanel panel
        ProductsBySupplierPanel.Visible = true;
    }
}

Se um fornecedor não tiver sido selecionado no GridView, o ChooseSupplierMsg Rótulo será exibido e o ProductsBySupplierPanel Painel ficará oculto. Caso contrário, se um fornecedor tiver sido selecionado, o ProductsBySupplierPanel será exibido e a propriedade SelectedIndex do GridView será atualizada.

A Figura 20 mostra os resultados depois que o fornecedor das Cervejarias Bigfoot foi selecionado e o botão Mostrar Produtos na Página foi clicado.

Os produtos fornecidos pelas cervejarias Bigfoot estão listados na mesma página

Figura 20: Os produtos fornecidos pelas cervejarias Bigfoot estão listados na mesma página (clique para exibir a imagem em tamanho real)

Resumo

Conforme discutido no Tutorial Mestre/Detalhe Usando um GridView Mestre Selecionável com um DetailView de Detalhes, os registros podem ser selecionados em um GridView usando um CommandField cuja ShowSelectButton propriedade está definida como true. Mas o CommandField exibe seus botões como botões de push regulares, links ou imagens. Uma interface de usuário alternativa para seleção de linha é fornecer um botão de opção ou caixa de seleção em cada linha do GridView. Neste tutorial, examinamos como adicionar uma coluna de botões de opção.

Infelizmente, adicionar uma coluna de botões de opção não é tão direto e simples quanto se poderia esperar. Não há nenhum RadioButtonField interno que possa ser adicionado ao clicar em um botão e usar o controle Web RadioButton em um TemplateField apresenta seu próprio conjunto de problemas. No final, para fornecer essa interface, precisamos criar uma classe personalizada DataControlField ou recorrer a injetar o HTML apropriado em um TemplateField durante o RowCreated evento.

Tendo explorado como adicionar uma coluna de botões de opção, vamos direcionar nossa atenção para adicionar uma coluna de caixas de seleção. Com uma coluna de caixas de seleção, um usuário pode selecionar uma ou mais linhas gridView e, em seguida, executar alguma operação em todas as linhas selecionadas (como selecionar um conjunto de emails de um cliente de email baseado na Web e, em seguida, optar por excluir todos os emails selecionados). No próximo tutorial, veremos como adicionar essa coluna.

Divirta-se programando!

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 independente, treinador e escritor. 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 David Suru. Interessado em revisar meus próximos artigos do MSDN? Se assim for, deixe-me uma linha em mitchell@4GuysFromRolla.com.