Compartilhar via


Atualizar e excluir dados binários existentes (VB)

por Scott Mitchell

Baixar PDF

Em tutoriais anteriores, vimos como o controle GridView simplifica a edição e a exclusão de dados de texto. Neste tutorial, vemos como o controle GridView também possibilita editar e excluir dados binários, independentemente de esses dados binários serem salvos no banco de dados ou armazenados no sistema de arquivos.

Introdução

Nos últimos três tutoriais, adicionamos um pouco de funcionalidade para trabalhar com dados binários. Começamos adicionando uma BrochurePath coluna à Categories tabela e atualizamos a arquitetura adequadamente. Também adicionamos métodos camada de acesso a dados e camada de lógica de negócios para trabalhar com a coluna existente Picture da tabela Categorias, que contém o conteúdo binário de um arquivo de imagem. Criamos páginas da Web para apresentar os dados binários em um GridView um link de download para o folheto, com a imagem da categoria mostrada em um <img> elemento e adicionamos um DetailsView para permitir que os usuários adicionem uma nova categoria e carreguem seus dados de folheto e imagem.

Tudo o que resta a ser implementado é a capacidade de editar e excluir categorias existentes, o que realizaremos neste tutorial usando os recursos internos de edição e exclusão do GridView. Ao editar uma categoria, o usuário poderá, opcionalmente, carregar uma nova imagem ou fazer com que a categoria continue a usar a existente. Para o folheto, eles podem optar por usar o folheto existente, carregar um novo folheto ou indicar que a categoria não tem mais um folheto associado a ele. Vamos começar!

Etapa 1: Atualizando a camada de acesso a dados

O DAL tem métodos , Updatee gerados Insertautomaticamente, mas esses métodos foram gerados com base na CategoriesTableAdapter consulta s main, que não inclui a PictureDelete coluna. Portanto, os Insert métodos e Update não incluem parâmetros para especificar os dados binários para a imagem da categoria. Como fizemos no tutorial anterior, precisamos criar um novo método TableAdapter para atualizar a Categories tabela ao especificar dados binários.

Abra o Conjunto de Dados Digitado e, no Designer, clique com o botão direito do CategoriesTableAdapter mouse no cabeçalho s e escolha Adicionar Consulta no menu de contexto para iniciar o Assistente de Configuração de Consulta tableAdapter. Esse assistente começa perguntando como a consulta TableAdapter deve acessar o banco de dados. Escolha Usar instruções SQL e clique em Avançar. A próxima etapa solicita que o tipo de consulta seja gerado. Como estamos criando uma consulta para adicionar um novo registro à Categories tabela, escolha ATUALIZAR e clique em Avançar.

Selecione a opção UPDATE

Figura 1: selecione a opção UPDATE (Clique para exibir a imagem em tamanho real)

Agora precisamos especificar a UPDATE instrução SQL. O assistente sugere automaticamente uma instrução UPDATE correspondente à consulta main do TableAdapter (aquela que atualiza os CategoryNamevalores , Descriptione BrochurePath ). Altere a instrução para que a Picture coluna seja incluída junto com um @Picture parâmetro, da seguinte forma:

UPDATE [Categories] SET 
    [CategoryName] = @CategoryName, 
    [Description] = @Description, 
    [BrochurePath] = @BrochurePath ,
    [Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))

A tela final do assistente nos pede para nomear o novo método TableAdapter. Insira UpdateWithPicture e clique em Concluir.

Nomeie o novo método TableAdapter UpdateWithPicture

Figura 2: nomeie o novo método UpdateWithPicture TableAdapter (clique para exibir a imagem em tamanho real)

Etapa 2: Adicionar os métodos de camada de lógica de negócios

Além de atualizar o DAL, precisamos atualizar a BLL para incluir métodos para atualizar e excluir uma categoria. Esses são os métodos que serão invocados da Camada de Apresentação.

Para excluir uma categoria, podemos usar o CategoriesTableAdapter método gerado automaticamente Delete . Adicione o seguinte método à classe CategoriesBLL:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteCategory(ByVal categoryID As Integer) As Boolean
    Dim rowsAffected As Integer = Adapter.Delete(categoryID)
    ' Return true if precisely one row was deleted, otherwise false
    Return rowsAffected = 1
End Function

Para este tutorial, vamos criar dois métodos para atualizar uma categoria : um que espera os dados de imagem binária e invoca o UpdateWithPicture método que acabamos de adicionar ao CategoriesTableAdapter e outro que aceita apenas os CategoryNamevalores , Descriptione BrochurePath e e usa CategoriesTableAdapter a instrução gerada Update automaticamente da classe s. A lógica por trás do uso de dois métodos é que, em algumas circunstâncias, um usuário pode querer atualizar a imagem da categoria junto com seus outros campos, nesse caso, o usuário terá que carregar a nova imagem. Os dados binários da imagem carregada podem ser usados na instrução UPDATE . Em outros casos, o usuário pode estar interessado apenas em atualizar, digamos, o nome e a descrição. Mas se a UPDATE instrução espera os dados binários para a Picture coluna também, também precisamos fornecer essas informações. Isso exigiria uma viagem extra ao banco de dados para trazer de volta os dados de imagem do registro que está sendo editado. Portanto, queremos dois UPDATE métodos. A Camada lógica de negócios determinará qual deles usar com base em se os dados de imagem são fornecidos ao atualizar a categoria.

Para facilitar isso, adicione dois métodos à CategoriesBLL classe , ambos chamados UpdateCategory. O primeiro deve aceitar três String s, uma Byte matriz e um Integer como seus parâmetros de entrada; o segundo, apenas três String s e um Integer. Os String parâmetros de entrada são para o nome, a descrição e o caminho do arquivo de folheto da categoria, a Byte matriz é para o conteúdo binário da imagem da categoria e identifica o IntegerCategoryID do registro a ser atualizado. Observe que a primeira sobrecarga invocará a segunda se a matriz passada Byte for Nothing:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, False)> _
Public Function UpdateCategory(categoryName As String, description As String, _
    brochurePath As String, picture() As Byte, categoryID As Integer) As Boolean
    
    ' If no picture is specified, use other overload
    If picture Is Nothing Then
        Return UpdateCategory(categoryName, description, brochurePath, categoryID)
    End If
    ' Update picture, as well
    Dim rowsAffected As Integer = Adapter.UpdateWithPicture _
        (categoryName, description, brochurePath, picture, categoryID)
    ' Return true if precisely one row was updated, otherwise false
    Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateCategory(categoryName As String, description As String, _
    brochurePath As String, categoryID As Integer) As Boolean
    Dim rowsAffected As Integer = Adapter.Update _
        (categoryName, description, brochurePath, categoryID)
    ' Return true if precisely one row was updated, otherwise false
    Return rowsAffected = 1
End Function

Etapa 3: Copiar sobre a funcionalidade Inserir e Exibir

No tutorial anterior , criamos uma página chamada UploadInDetailsView.aspx que listava todas as categorias em um GridView e fornecemos um DetailsView para adicionar novas categorias ao sistema. Neste tutorial, estenderemos o GridView para incluir a edição e a exclusão do suporte. Em vez de continuar a trabalhar com UploadInDetailsView.aspxo , vamos colocar as alterações deste tutorial na UpdatingAndDeleting.aspx página da mesma pasta, ~/BinaryData. Copie e cole a marcação declarativa e o código de UploadInDetailsView.aspx para UpdatingAndDeleting.aspx.

Comece abrindo a UploadInDetailsView.aspx página. Copie toda a sintaxe declarativa dentro do <asp:Content> elemento, conforme mostrado na Figura 3. Em seguida, abra UpdatingAndDeleting.aspx e cole essa marcação dentro de seu <asp:Content> elemento. Da mesma forma, copie o código da UploadInDetailsView.aspx classe code-behind da página para UpdatingAndDeleting.aspx.

Copiar a Marcação Declarativa do UploadInDetailsView.aspx

Figura 3: Copiar a Marcação Declarativa de UploadInDetailsView.aspx (Clique para exibir a imagem em tamanho real)

Depois de copiar a marcação declarativa e o código, visite UpdatingAndDeleting.aspx. Você deve ver a mesma saída e ter a mesma experiência do usuário com UploadInDetailsView.aspx a página do tutorial anterior.

Etapa 4: adicionando suporte à exclusão ao ObjectDataSource e gridView

Como discutimos novamente no tutorial Uma Visão Geral de Inserção, Atualização e Exclusão de Dados , o GridView fornece recursos internos de exclusão e esses recursos podem ser habilitados no tick de uma caixa de seleção se a fonte de dados subjacente da grade der suporte à exclusão. Atualmente, o ObjectDataSource ao qual GridView está associado (CategoriesDataSource) não dá suporte à exclusão.

Para corrigir isso, clique na opção Configurar Fonte de Dados da marca inteligente ObjectDataSource para iniciar o assistente. A primeira tela mostra que ObjectDataSource está configurado para funcionar com a CategoriesBLL classe . Pressione Avançar. Atualmente, apenas as propriedades e SelectMethod ObjectDataSource InsertMethod são especificadas. No entanto, o assistente preencheu automaticamente as listas suspensas nas guias UPDATE e DELETE com os UpdateCategory métodos e DeleteCategory , respectivamente. Isso ocorre porque, na CategoriesBLL classe, marcamos esses métodos usando o DataObjectMethodAttribute como os métodos padrão para atualizar e excluir.

Por enquanto, defina a lista suspensa da guia UPDATE como (Nenhum), mas deixe a lista suspensa da guia DELETE definida como DeleteCategory. Retornaremos a esse assistente na Etapa 6 para adicionar suporte de atualização.

Configurar o ObjectDataSource para usar o método DeleteCategory

Figura 4: configurar o ObjectDataSource para usar o DeleteCategory método (clique para exibir a imagem em tamanho real)

Observação

Ao concluir o assistente, o Visual Studio pode perguntar se você deseja atualizar campos e chaves, o que regenerará os campos de controles da Web de dados. Escolha Não, pois escolher Sim substituirá todas as personalizações de campo que você possa ter feito.

O ObjectDataSource agora incluirá um valor para sua DeleteMethod propriedade, bem como um DeleteParameter. Lembre-se de que, ao usar o assistente para especificar os métodos, o Visual Studio define a propriedade original_{0}ObjectDataSource como OldValuesParameterFormatString , o que causa problemas com as invocações de método de atualização e exclusão. Portanto, desmarque completamente essa propriedade ou redefina-a para o padrão, {0}. Se você precisar atualizar sua memória nesta propriedade ObjectDataSource, consulte o tutorial Uma Visão geral da inserção, atualização e exclusão de dados .

Depois de concluir o assistente e corrigir o OldValuesParameterFormatString, a marcação declarativa do ObjectDataSource deve ser semelhante à seguinte:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="{0}" SelectMethod="GetCategories" 
    TypeName="CategoriesBLL" InsertMethod="InsertWithPicture" 
    DeleteMethod="DeleteCategory">
    <InsertParameters>
        <asp:Parameter Name="categoryName" Type="String" />
        <asp:Parameter Name="description" Type="String" />
        <asp:Parameter Name="brochurePath" Type="String" />
        <asp:Parameter Name="picture" Type="Object" />
    </InsertParameters>
    <DeleteParameters>
        <asp:Parameter Name="categoryID" Type="Int32" />
    </DeleteParameters>
</asp:ObjectDataSource>

Depois de configurar o ObjectDataSource, adicione recursos de exclusão ao GridView marcando a caixa de seleção Habilitar Exclusão da marca inteligente GridView. Isso adicionará um CommandField ao GridView cuja ShowDeleteButton propriedade está definida como True.

Habilitar o suporte para exclusão no GridView

Figura 5: Habilitar o suporte para exclusão no GridView (clique para exibir a imagem em tamanho real)

Tire um momento para testar a funcionalidade de exclusão. Há uma chave estrangeira entre as Products tabelas e CategoryID as Categories tabelas, CategoryIDportanto, você receberá uma exceção de violação de restrição de chave estrangeira se tentar excluir qualquer uma das oito primeiras categorias. Para testar essa funcionalidade, adicione uma nova categoria, fornecendo um folheto e uma imagem. Minha categoria de teste, mostrada na Figura 6, inclui um arquivo de folheto de teste chamado Test.pdf e uma imagem de teste. A Figura 7 mostra o GridView após a adição da categoria de teste.

Adicionar uma categoria de teste com um folheto e uma imagem

Figura 6: Adicionar uma categoria de teste com um folheto e uma imagem (clique para exibir a imagem em tamanho real)

Depois de inserir a categoria de teste, ela é exibida no GridView

Figura 7: depois de inserir a categoria de teste, ela é exibida no GridView (Clique para exibir a imagem em tamanho real)

No Visual Studio, atualize o Gerenciador de Soluções. Agora você deve ver um novo arquivo na ~/Brochures pasta Test.pdf (consulte a Figura 8).

Em seguida, clique no link Excluir na linha Categoria de Teste, fazendo com que a página seja acionada e o CategoriesBLL método s da DeleteCategory classe seja acionado. Isso invocará o método da DAL s Delete , fazendo com que a instrução apropriada DELETE seja enviada ao banco de dados. Em seguida, os dados são recuperados para o GridView e a marcação é enviada de volta para o cliente com a Categoria de Teste não mais presente.

Embora o fluxo de trabalho de exclusão tenha removido com êxito o registro categoria de teste da Categories tabela, ele não removeu seu arquivo de folheto do sistema de arquivos do servidor Web. Atualize o Gerenciador de Soluções e você verá que Test.pdf ainda está na ~/Brochures pasta .

O arquivo Test.pdf não foi excluído do sistema de arquivos do servidor Web

Figura 8: O Test.pdf arquivo não foi excluído do sistema de arquivos do servidor Web

Etapa 5: Removendo o arquivo de folheto da categoria excluída

Uma das desvantagens de armazenar dados binários externos ao banco de dados é que etapas extras devem ser executadas para limpo esses arquivos quando o registro de banco de dados associado é excluído. GridView e ObjectDataSource fornecem eventos que são acionados antes e depois que o comando delete é executado. Na verdade, precisamos criar manipuladores de eventos para os eventos pré e pós-ação. Antes que o Categories registro seja excluído, precisamos determinar o caminho do arquivo PDF, mas não queremos excluir o PDF antes que a categoria seja excluída caso haja alguma exceção e a categoria não seja excluída.

O evento GridView é RowDeleting acionado antes que o comando delete do ObjectDataSource seja invocado, enquanto seu RowDeleted evento é acionado depois. Crie manipuladores de eventos para esses dois eventos usando o seguinte código:

' A page variable to "remember" the deleted category's BrochurePath value
Private deletedCategorysPdfPath As String = Nothing
Protected Sub Categories_RowDeleting(sender As Object, e As GridViewDeleteEventArgs) _
    Handles Categories.RowDeleting
    
    ' Determine the PDF path for the category being deleted...
    Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
    Dim categoryAPI As New CategoriesBLL()
    Dim categoriesData As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categoriesData(0)
    If category.IsBrochurePathNull() Then
        deletedCategorysPdfPath = Nothing
    Else
        deletedCategorysPdfPath = category.BrochurePath
    End If
End Sub
Protected Sub Categories_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
    Handles Categories.RowDeleted
    
    ' Delete the brochure file if there were no problems deleting the record
    If e.Exception Is Nothing Then
        DeleteRememberedBrochurePath()
    End If
End Sub

RowDeleting No manipulador de eventos, o CategoryID da linha que está sendo excluída é capturado da coleção GridView, DataKeys que pode ser acessada nesse manipulador de eventos por meio da e.Keys coleção. Em seguida, a CategoriesBLL classe s GetCategoryByCategoryID(categoryID) é invocada para retornar informações sobre o registro que está sendo excluído. Se o objeto retornado CategoriesDataRow tiver um valor diferente,NULL``BrochurePath ele será armazenado na variável deletedCategorysPdfPath de página para que o arquivo possa ser excluído no RowDeleted manipulador de eventos.

Observação

Em vez de recuperar os BrochurePath detalhes do Categories registro que está sendo excluído no RowDeleting manipulador de eventos, poderíamos ter adicionado o BrochurePath à propriedade GridView e DataKeyNames acessado o valor do registro por meio da e.Keys coleção. Isso aumentaria ligeiramente o tamanho do estado de exibição do GridView, mas reduziria a quantidade de código necessária e salvaria uma viagem ao banco de dados.

Depois que o comando de exclusão subjacente do ObjectDataSource for invocado, o manipulador de eventos gridView RowDeleted será acionado. Se não houver exceções na exclusão dos dados e houver um valor para deletedCategorysPdfPath, o PDF será excluído do sistema de arquivos. Observe que esse código extra não é necessário para limpo os dados binários da categoria associados à sua imagem. Isso ocorre porque os dados de imagem são armazenados diretamente no banco de dados, portanto, excluir a Categories linha também exclui os dados de imagem dessa categoria.

Depois de adicionar os dois manipuladores de eventos, execute esse caso de teste novamente. Ao excluir a categoria, seu PDF associado também é excluído.

Atualizar dados binários associados a um registro existente fornece alguns desafios interessantes. O restante deste tutorial se aprofunda na adição de recursos de atualização ao folheto e à imagem. A Etapa 6 explora técnicas para atualizar as informações do folheto enquanto a Etapa 7 analisa a atualização da imagem.

Etapa 6: Atualizando um folheto de categoria

Conforme discutido no tutorial Uma visão geral de inserção, atualização e exclusão de dados, o GridView oferece suporte interno à edição em nível de linha que pode ser implementado pelo tique de uma caixa de seleção se sua fonte de dados subjacente estiver configurada adequadamente. Atualmente, o CategoriesDataSource ObjectDataSource ainda não está configurado para incluir o suporte de atualização, portanto, vamos adicioná-lo.

Clique no link Configurar Fonte de Dados do assistente objectDataSource e prossiga para a segunda etapa. Devido ao DataObjectMethodAttribute usado no CategoriesBLL, a lista suspensa UPDATE deve ser preenchida automaticamente com a UpdateCategory sobrecarga que aceita quatro parâmetros de entrada (para todas as colunas, menos Picture). Altere isso para que ele use a sobrecarga com cinco parâmetros.

Configurar o ObjectDataSource para usar o método UpdateCategory que inclui um parâmetro para imagem

Figura 9: Configurar o ObjectDataSource para usar o método para o UpdateCategory qual inclui um parâmetro Picture (clique para exibir a imagem em tamanho real)

O ObjectDataSource agora incluirá um valor para sua UpdateMethod propriedade, bem como s correspondentes UpdateParameter . Conforme observado na Etapa 4, o Visual Studio define a propriedade original_{0} objectDataSource como OldValuesParameterFormatString ao usar o assistente Configurar Fonte de Dados. Isso causará problemas com as invocações de método de atualização e exclusão. Portanto, limpe essa propriedade completamente ou redefina-a para o padrão, {0}.

Depois de concluir o assistente e corrigir o OldValuesParameterFormatString, a marcação declarativa do ObjectDataSource deve ser semelhante à seguinte:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="{0}" SelectMethod="GetCategories" 
    TypeName="CategoriesBLL" InsertMethod="InsertWithPicture" 
    DeleteMethod="DeleteCategory" UpdateMethod="UpdateCategory">
    <InsertParameters>
        <asp:Parameter Name="categoryName" Type="String" />
        <asp:Parameter Name="description" Type="String" />
        <asp:Parameter Name="brochurePath" Type="String" />
        <asp:Parameter Name="picture" Type="Object" />
    </InsertParameters>
    <DeleteParameters>
        <asp:Parameter Name="categoryID" Type="Int32" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="categoryName" Type="String" />
        <asp:Parameter Name="description" Type="String" />
        <asp:Parameter Name="brochurePath" Type="String" />
        <asp:Parameter Name="picture" Type="Object" />
        <asp:Parameter Name="categoryID" Type="Int32" />
    </UpdateParameters>
</asp:ObjectDataSource>

Para ativar os recursos de edição internos do GridView, marcar a opção Habilitar Edição da marca inteligente GridView. Isso definirá a propriedade TrueCommandField como ShowEditButton , resultando na adição de um botão Editar (e botões Atualizar e Cancelar para a linha que está sendo editada).

Configurar o GridView para dar suporte à edição

Figura 10: Configurar o GridView para dar suporte à edição (clique para exibir a imagem em tamanho real)

Visite a página por meio de um navegador e clique em um dos botões Editar da linha. O CategoryName e Description BoundFields são renderizados como caixas de texto. O BrochurePath TemplateField não tem um EditItemTemplate, portanto, ele continua a mostrar seu ItemTemplate link para o folheto. O Picture ImageField é renderizado como um TextBox cuja Text propriedade é atribuída ao valor de ImageField, DataImageUrlField nesse caso CategoryID.

O GridView não tem uma interface de edição para BrochurePath

Figura 11: O GridView não tem uma interface de edição para BrochurePath (clique para exibir a imagem em tamanho real)

Personalizando aBrochurePathinterface de edição s

Precisamos criar uma interface de edição para o BrochurePath TemplateField, uma que permita que o usuário:

  • Deixe o folheto da categoria no estado em que se encontra,
  • Atualizar o folheto da categoria carregando um novo folheto ou
  • Remova o folheto da categoria completamente (caso a categoria não tenha mais um folheto associado).

Também precisamos atualizar a Picture interface de edição do ImageField, mas chegaremos a isso na Etapa 7.

Na marca inteligente GridView, clique no link Editar Modelos e selecione TemplateField BrochurePath s EditItemTemplate na lista suspensa. Adicione um controle Web RadioButtonList a este modelo, definindo sua ID propriedade BrochureOptions como e sua AutoPostBack propriedade como True. Na janela Propriedades, clique nas reticências na Items propriedade , que exibirá o ListItem Editor Coleção. Adicione as três opções a seguir com Value s 1, 2 e 3, respectivamente:

  • Usar folheto atual
  • Remover folheto atual
  • Carregar novo folheto

Defina a primeira ListItem propriedade s Selected como True.

Adicionar três ListItems ao RadioButtonList

Figura 12: Adicionar três ListItem s ao RadioButtonList

Abaixo do RadioButtonList, adicione um controle FileUpload chamado BrochureUpload. Defina sua Visible propriedade como False.

Adicionar um controle RadioButtonList e FileUpload ao EditItemTemplate

Figura 13: Adicionar um controle RadioButtonList e FileUpload ao EditItemTemplate (Clique para exibir a imagem em tamanho real)

Este RadioButtonList fornece as três opções para o usuário. A ideia é que o controle FileUpload seja exibido somente se a última opção, Carregar novo folheto, estiver selecionada. Para fazer isso, crie um manipulador de eventos para o evento RadioButtonList SelectedIndexChanged e adicione o seguinte código:

Protected Sub BrochureOptions_SelectedIndexChanged _
    (sender As Object, e As EventArgs)
    
    ' Get a reference to the RadioButtonList and its Parent
    Dim BrochureOptions As RadioButtonList = _
        CType(sender, RadioButtonList)
    Dim parent As Control = BrochureOptions.Parent
    ' Now use FindControl("controlID") to get a reference of the 
    ' FileUpload control
    Dim BrochureUpload As FileUpload = _
        CType(parent.FindControl("BrochureUpload"), FileUpload)
    ' Only show BrochureUpload if SelectedValue = "3"
    BrochureUpload.Visible = (BrochureOptions.SelectedValue = "3")
End Sub

Como os controles RadioButtonList e FileUpload estão dentro de um modelo, precisamos escrever um pouco de código para acessar programaticamente esses controles. O SelectedIndexChanged manipulador de eventos recebe uma referência do RadioButtonList no sender parâmetro de entrada. Para obter o controle FileUpload, precisamos obter o controle pai do RadioButtonList e usar o FindControl("controlID") método de lá. Depois que tivermos uma referência aos controles RadioButtonList e FileUpload, a propriedade s do Visible controle FileUpload será definida True como somente se RadioButtonList for SelectedValue igual a 3, que é o Value para Carregar novo folheto ListItem.

Com esse código em vigor, reserve um momento para testar a interface de edição. Clique no botão Editar de uma linha. Inicialmente, a opção Usar folheto atual deve ser selecionada. Alterar o índice selecionado causa um postback. Se a terceira opção estiver selecionada, o controle FileUpload será exibido, caso contrário, ficará oculto. A Figura 14 mostra a interface de edição quando o botão Editar é clicado pela primeira vez; A Figura 15 mostra a interface após a opção Carregar novo folheto ser selecionada.

Inicialmente, a opção Usar folheto atual é Selecionada

Figura 14: Inicialmente, a opção Usar folheto atual é Selecionada (Clique para exibir a imagem em tamanho real)

Escolher a opção Carregar novo folheto exibe o controle FileUpload

Figura 15: Escolher a opção Carregar novo folheto exibe o controle FileUpload (clique para exibir a imagem em tamanho real)

Salvando o arquivo de folheto e atualizando aBrochurePathcoluna

Quando o botão Atualização do GridView é clicado, seu RowUpdating evento é acionado. O comando de atualização objectDataSource é invocado e, em seguida, o evento GridView é RowUpdated acionado. Assim como acontece com o fluxo de trabalho de exclusão, precisamos criar manipuladores de eventos para ambos os eventos. RowUpdating No manipulador de eventos, precisamos determinar qual ação deve ser tomada com base no SelectedValue de BrochureOptions RadioButtonList:

  • Se for SelectedValue 1, queremos continuar usando a mesma BrochurePath configuração. Portanto, precisamos definir o parâmetro ObjectDataSource para brochurePath o valor existente BrochurePath do registro que está sendo atualizado. O parâmetro ObjectDataSource s brochurePath pode ser definido usando e.NewValues["brochurePath"] = value.
  • Se o SelectedValue for 2, queremos definir o valor NULLdo BrochurePath registro como . Isso pode ser feito definindo o parâmetro ObjectDataSource como brochurePath , o que resulta em um banco de dados NULL sendo usado na UPDATE instrução .Nothing Se houver um arquivo de folheto existente que está sendo removido, precisamos excluir o arquivo existente. No entanto, só queremos fazer isso se a atualização for concluída sem gerar uma exceção.
  • Se for SelectedValue 3, queremos garantir que o usuário tenha carregado um arquivo PDF e, em seguida, salve-o no sistema de arquivos e atualize o valor da coluna do BrochurePath registro. Além disso, se houver um arquivo de folheto existente que está sendo substituído, precisamos excluir o arquivo anterior. No entanto, só queremos fazer isso se a atualização for concluída sem gerar uma exceção.

As etapas necessárias para serem concluídas quando o RadioButtonList s SelectedValue for 3 são praticamente idênticas às usadas pelo manipulador de eventos DetailsView ItemInserting . Esse manipulador de eventos é executado quando um novo registro de categoria é adicionado do controle DetailsView que adicionamos no tutorial anterior. Portanto, cabe a nós refatorar essa funcionalidade em métodos separados. Especificamente, mudei a funcionalidade comum para dois métodos:

  • ProcessBrochureUpload(FileUpload, out bool) aceita como entrada uma instância de controle FileUpload e um valor booliano de saída que especifica se a operação de exclusão ou edição deve continuar ou se deve ser cancelada devido a algum erro de validação. Esse método retorna o caminho para o arquivo salvo ou null se nenhum arquivo foi salvo.
  • DeleteRememberedBrochurePath excluirá o arquivo especificado pelo caminho na variável deletedCategorysPdfPath de página se deletedCategorysPdfPath não nullfor .

O código para esses dois métodos segue. Observe a similaridade entre ProcessBrochureUpload e o manipulador de eventos detailsView ItemInserting do tutorial anterior. Neste tutorial, atualizei os manipuladores de eventos detailsView para usar esses novos métodos. Baixe o código associado a este tutorial para ver as modificações nos manipuladores de eventos detailsView.

Private Function ProcessBrochureUpload _
    (BrochureUpload As FileUpload, CancelOperation As Boolean) As String
    
    CancelOperation = False    ' by default, do not cancel operation
    If BrochureUpload.HasFile Then
        ' Make sure that a PDF has been uploaded
        If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
            ".pdf", True) <> 0 Then
            
            UploadWarning.Text = _
                "Only PDF documents may be used for a category's brochure."
            UploadWarning.Visible = True
            CancelOperation = True
            Return Nothing
        End If
        Const BrochureDirectory As String = "~/Brochures/"
        Dim brochurePath As String = BrochureDirectory + BrochureUpload.FileName
        Dim fileNameWithoutExtension As String = _
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
        Dim iteration As Integer = 1
        While System.IO.File.Exists(Server.MapPath(brochurePath))
            brochurePath = String.Concat(BrochureDirectory, _
                fileNameWithoutExtension, "-", iteration, ".pdf")
            iteration += 1
        End While
        ' Save the file to disk and set the value of the brochurePath parameter
        BrochureUpload.SaveAs(Server.MapPath(brochurePath))
        Return brochurePath
    Else
        ' No file uploaded
        Return Nothing
    End If
End Function
Private Sub DeleteRememberedBrochurePath()
    ' Is there a file to delete?
    If deletedCategorysPdfPath IsNot Nothing Then
        System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath))
    End If
End Sub

Os manipuladores de RowUpdating eventos e RowUpdated GridView usam os ProcessBrochureUpload métodos e DeleteRememberedBrochurePath , como mostra o código a seguir:

Protected Sub Categories_RowUpdating _
    (sender As Object, e As GridViewUpdateEventArgs) _
    Handles Categories.RowUpdating
    
    ' Reference the RadioButtonList
    Dim BrochureOptions As RadioButtonList = _
        CType(Categories.Rows(e.RowIndex).FindControl("BrochureOptions"), _
            RadioButtonList)
    ' Get BrochurePath information about the record being updated
    Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
    Dim categoryAPI As New CategoriesBLL()
    Dim categoriesData As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categoriesData(0)
    If BrochureOptions.SelectedValue = "1" Then
        ' Use current value for BrochurePath
        If category.IsBrochurePathNull() Then
            e.NewValues("brochurePath") = Nothing
        Else
            e.NewValues("brochurePath") = category.BrochurePath
        End If
    ElseIf BrochureOptions.SelectedValue = "2" Then
        ' Remove the current brochure (set it to NULL in the database)
        e.NewValues("brochurePath") = Nothing
    ElseIf BrochureOptions.SelectedValue = "3" Then
        ' Reference the BrochurePath FileUpload control
        Dim BrochureUpload As FileUpload = _
            CType(categories.Rows(e.RowIndex).FindControl("BrochureUpload"), _
                FileUpload)
        ' Process the BrochureUpload
        Dim cancelOperation As Boolean = False
        e.NewValues("brochurePath") = _
            ProcessBrochureUpload(BrochureUpload, cancelOperation)
        e.Cancel = cancelOperation
    Else
        ' Unknown value!
        Throw New ApplicationException( _
            String.Format("Invalid BrochureOptions value, {0}", _
                BrochureOptions.SelectedValue))
    End If
    If BrochureOptions.SelectedValue = "2" OrElse _
        BrochureOptions.SelectedValue = "3" Then
        
        ' "Remember" that we need to delete the old PDF file
        If (category.IsBrochurePathNull()) Then
            deletedCategorysPdfPath = Nothing
        Else
            deletedCategorysPdfPath = category.BrochurePath
        End If
    End If
End Sub
Protected Sub Categories_RowUpdated _
    (sender As Object, e As GridViewUpdatedEventArgs) _
    Handles Categories.RowUpdated
    
    ' If there were no problems and we updated the PDF file, 
    ' then delete the existing one
    If e.Exception Is Nothing Then
        DeleteRememberedBrochurePath()
    End If
End Sub

Observe como o RowUpdating manipulador de eventos usa uma série de instruções condicionais para executar a ação apropriada com base no BrochureOptions valor da propriedade RadioButtonList SelectedValue .

Com esse código em vigor, você pode editar uma categoria e fazer com que ela use seu folheto atual, não use nenhum folheto ou carregue um novo. Vá em frente e experimente. Defina pontos de interrupção nos RowUpdating manipuladores de eventos e RowUpdated para ter uma noção do fluxo de trabalho.

Etapa 7: Carregando uma nova imagem

A Picture interface de edição imagefield é renderizada como uma caixa de texto preenchida com o valor de sua DataImageUrlField propriedade. Durante o fluxo de trabalho de edição, o GridView passa um parâmetro para o ObjectDataSource com o nome do parâmetro s o valor da propriedade ImageField s DataImageUrlField e o valor do parâmetro s o valor inserido na caixa de texto na interface de edição. Esse comportamento é adequado quando a imagem é salva como um arquivo no sistema de arquivos e contém DataImageUrlField a URL completa da imagem. Com essas circunstâncias, a interface de edição exibe a URL da imagem na caixa de texto, que o usuário pode alterar e ter salvado de volta no banco de dados. Concedida, essa interface padrão não permite que o usuário carregue uma nova imagem, mas permite que ele altere a URL da imagem do valor atual para outra. Para este tutorial, no entanto, a interface de edição padrão do ImageField não é suficiente porque os Picture dados binários estão sendo armazenados diretamente no banco de dados e a DataImageUrlField propriedade contém apenas o CategoryID.

Para entender melhor o que acontece em nosso tutorial quando um usuário edita uma linha com um ImageField, considere o seguinte exemplo: um usuário edita uma linha com 10, fazendo com CategoryID que ImageField Picture seja renderizado como uma caixa de texto com o valor 10. Imagine que o usuário altere o valor nesta caixa de texto para 50 e clique no botão Atualizar. Um postback ocorre e o GridView cria inicialmente um parâmetro chamado CategoryID com o valor 50. No entanto, antes que o GridView envie esse parâmetro (e os CategoryName parâmetros e Description ), ele adiciona os valores da DataKeys coleção. Portanto, ele substitui o CategoryID parâmetro pelo valor subjacente CategoryID da linha atual, 10. Resumindo, a interface de edição do ImageField não afeta o fluxo de trabalho de edição deste tutorial porque os nomes da propriedade ImageField e DataImageUrlField do valor da grade s DataKey são um no mesmo.

Embora o ImageField facilite a exibição de uma imagem com base nos dados do banco de dados, não queremos fornecer uma caixa de texto na interface de edição. Em vez disso, queremos oferecer um controle FileUpload que o usuário final pode usar para alterar a imagem da categoria. Ao contrário do BrochurePath valor , para esses tutoriais, decidimos exigir que cada categoria tenha uma imagem. Portanto, não precisamos permitir que o usuário indique que não há nenhuma imagem associada que o usuário possa carregar uma nova imagem ou deixar a imagem atual como está.

Para personalizar a interface de edição do ImageField, precisamos convertê-la em um TemplateField. Na marca inteligente GridView, clique no link Editar Colunas, selecione ImageField e clique no link Converter este campo em um TemplateField.

Converter o ImageField em um TemplateField

Figura 16: Converter o ImageField em um TemplateField

Converter o ImageField em um TemplateField dessa maneira gera um TemplateField com dois modelos. Como mostra a sintaxe declarativa a seguir, o ItemTemplate contém um controle web image cuja ImageUrl propriedade é atribuída usando a sintaxe de associação de dados com base nas propriedades e DataImageUrlFormatString s DataImageUrlField ImageField. O EditItemTemplate contém um TextBox cuja Text propriedade está associada ao valor especificado pela DataImageUrlField propriedade .

<asp:TemplateField>
    <EditItemTemplate>
        <asp:TextBox ID="TextBox1" runat="server" 
            Text='<%# Eval("CategoryID") %>'></asp:TextBox>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Image ID="Image1" runat="server" 
            ImageUrl='<%# Eval("CategoryID", 
                "DisplayCategoryPicture.aspx?CategoryID={0}") %>' />
    </ItemTemplate>
</asp:TemplateField>

Precisamos atualizar o EditItemTemplate para usar um controle FileUpload. Na marca inteligente GridView, clique no link Editar Modelos e selecione TemplateField Picture s EditItemTemplate na lista suspensa. No modelo, você deve ver um TextBox remover isso. Em seguida, arraste um controle FileUpload da Caixa de Ferramentas para o modelo, definindo-o IDPictureUploadcomo . Adicione também o texto Para alterar a imagem da categoria, especifique uma nova imagem. Para manter a mesma imagem da categoria, deixe o campo vazio para o modelo também.

Adicionar um controle FileUpload ao EditItemTemplate

Figura 17: Adicionar um controle FileUpload ao EditItemTemplate (Clique para exibir a imagem em tamanho real)

Depois de personalizar a interface de edição, exiba seu progresso em um navegador. Ao exibir uma linha no modo somente leitura, a imagem da categoria é mostrada como era antes, mas clicar no botão Editar renderiza a coluna de imagem como texto com um controle FileUpload.

A interface de edição inclui um controle FileUpload

Figura 18: a interface de edição inclui um controle FileUpload (clique para exibir a imagem em tamanho real)

Lembre-se de que ObjectDataSource está configurado para chamar o CategoriesBLL método da classe s UpdateCategory que aceita como entrada os dados binários para a imagem como uma Byte matriz. No entanto, se essa matriz for Nothing, a sobrecarga alternativa UpdateCategory será chamada, o que emitirá a UPDATE instrução SQL que não modifica a Picture coluna, deixando a imagem atual da categoria intacta. Portanto, no manipulador de eventos gridView RowUpdating , precisamos referenciar programaticamente o PictureUpload controle FileUpload e determinar se um arquivo foi carregado. Se um não tiver sido carregado, não queremos especificar um valor para o picture parâmetro . Por outro lado, se um arquivo foi carregado no PictureUpload controle FileUpload, queremos garantir que ele seja um arquivo JPG. Se for, podemos enviar seu conteúdo binário para o ObjectDataSource por meio do picture parâmetro .

Assim como acontece com o código usado na Etapa 6, grande parte do código necessário aqui já existe no manipulador de eventos DetailsView.ItemInserting Portanto, refatorei a funcionalidade comum em um novo método, ValidPictureUploade atualizei o ItemInserting manipulador de eventos para usar esse método.

Adicione o código a seguir ao início do manipulador de eventos GridView RowUpdating . É importante que esse código venha antes do código que salva o arquivo de folheto, pois não queremos salvar o folheto no sistema de arquivos do servidor Web se um arquivo de imagem inválido for carregado.

' Reference the PictureUpload FileUpload
Dim PictureUpload As FileUpload = _
    CType(categories.Rows(e.RowIndex).FindControl("PictureUpload"), _
        FileUpload)
If PictureUpload.HasFile Then
    ' Make sure the picture upload is valid
    If ValidPictureUpload(PictureUpload) Then
        e.NewValues("picture") = PictureUpload.FileBytes
    Else
        ' Invalid file upload, cancel update and exit event handler
        e.Cancel = True
        Exit Sub
    End If
End If

O ValidPictureUpload(FileUpload) método usa um controle FileUpload como seu único parâmetro de entrada e verifica a extensão do arquivo carregado para garantir que o arquivo carregado seja um JPG; ele só será chamado se um arquivo de imagem for carregado. Se nenhum arquivo for carregado, o parâmetro picture não será definido e, portanto, usará seu valor padrão de Nothing. Se uma imagem tiver sido carregada e ValidPictureUpload retornar True, o picture parâmetro será atribuído aos dados binários da imagem carregada; se o método retornar False, o fluxo de trabalho de atualização será cancelado e o manipulador de eventos será encerrado.

O ValidPictureUpload(FileUpload) código do método, que foi refatorado do manipulador de eventos DetailsView ItemInserting , segue:

Private Function ValidPictureUpload(ByVal PictureUpload As FileUpload) As Boolean
    ' Make sure that a JPG has been uploaded
    If String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
        ".jpg", True) <> 0 AndAlso _
        String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
        ".jpeg", True) <> 0 Then
        
        UploadWarning.Text = _
            "Only JPG documents may be used for a category's picture."
        UploadWarning.Visible = True
        Return False
    Else
        Return True
    End If
End Function

Etapa 8: Substituindo as imagens de categorias originais por JPGs

Lembre-se de que as imagens originais de oito categorias são arquivos bitmap encapsulados em um cabeçalho OLE. Agora que adicionamos a capacidade de editar uma imagem de registro existente, reserve um momento para substituir esses bitmaps por JPGs. Se você quiser continuar a usar as imagens de categoria atuais, poderá convertê-las em JPGs executando as seguintes etapas:

  1. Salve as imagens de bitmap no disco rígido. Visite a UpdatingAndDeleting.aspx página no navegador e, para cada uma das oito primeiras categorias, clique com o botão direito do mouse na imagem e escolha salvar a imagem.
  2. Abra a imagem no editor de imagens de sua escolha. Você pode usar Microsoft Paint, por exemplo.
  3. Salve o bitmap como uma imagem JPG.
  4. Atualize a imagem da categoria por meio da interface de edição, usando o arquivo JPG.

Depois de editar uma categoria e carregar a imagem JPG, a imagem não será renderizada no navegador porque a DisplayCategoryPicture.aspx página está tirando os primeiros 78 bytes das imagens das oito primeiras categorias. Corrija isso removendo o código que executa a remoção do cabeçalho OLE. Depois de fazer isso, o DisplayCategoryPicture.aspx``Page_Load manipulador de eventos deve ter apenas o seguinte código:

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim categoryID As Integer = _
        Convert.ToInt32(Request.QueryString("CategoryID"))
    ' Get information about the specified category
    Dim categoryAPI As New CategoriesBLL()
    Dim categories As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categories(0)
    ' For new categories, images are JPGs...
    ' Output HTTP headers providing information about the binary data
    Response.ContentType = "image/jpeg"
    ' Output the binary data
    Response.BinaryWrite(category.Picture)
End Sub

Observação

As UpdatingAndDeleting.aspx interfaces de inserção e edição da página podem usar um pouco mais de trabalho. O CategoryName e Description BoundFields em DetailsView e GridView devem ser convertidos em TemplateFields. Como CategoryName não permite NULL valores, um RequiredFieldValidator deve ser adicionado. E o Description TextBox provavelmente deve ser convertido em uma TextBox de várias linhas. Eu deixo esses toques finais como um exercício para você.

Resumo

Este tutorial conclui nossa análise de como trabalhar com dados binários. Neste tutorial e nos três anteriores, vimos como os dados binários podem ser armazenados no sistema de arquivos ou diretamente no banco de dados. Um usuário fornece dados binários para o sistema selecionando um arquivo de seu disco rígido e carregando-o no servidor Web, onde ele pode ser armazenado no sistema de arquivos ou inserido no banco de dados. ASP.NET 2.0 inclui um controle FileUpload que torna o fornecimento de uma interface tão fácil quanto arrastar e soltar. No entanto, conforme observado no tutorial Carregando arquivos , o controle FileUpload só é adequado para uploads de arquivos relativamente pequenos, o ideal é não exceder um megabyte. Também exploramos como associar dados carregados ao modelo de dados subjacente, bem como editar e excluir os dados binários de registros existentes.

Nosso próximo conjunto de tutoriais explora várias técnicas de cache. O cache fornece um meio de melhorar o desempenho geral de um aplicativo, tirando os resultados de operações caras e armazenando-os em um local que pode ser acessado mais rapidamente.

Programação feliz!

Sobre o autor

Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.

Agradecimentos Especiais

Esta série de tutoriais foi revisada por muitos revisores úteis. A revisora principal deste tutorial foi Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.