Atualizar e excluir dados binários existentes (VB)
por Scott Mitchell
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 , Update
e gerados Insert
automaticamente, mas esses métodos foram gerados com base na CategoriesTableAdapter
consulta s main, que não inclui a Picture
Delete
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.
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 CategoryName
valores , Description
e 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.
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 CategoryName
valores , Description
e 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 Integer
CategoryID
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.aspx
o , 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
.
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.
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
.
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, CategoryID
portanto, 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.
Figura 6: Adicionar uma categoria de teste com um folheto e uma imagem (clique para exibir a imagem em tamanho real)
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 .
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.
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 True
CommandField como ShowEditButton
, resultando na adição de um botão Editar (e botões Atualizar e Cancelar para a linha que está sendo editada).
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
.
Figura 11: O GridView não tem uma interface de edição para BrochurePath
(clique para exibir a imagem em tamanho real)
Personalizando aBrochurePath
interface 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
.
Figura 12: Adicionar três ListItem
s ao RadioButtonList
Abaixo do RadioButtonList, adicione um controle FileUpload chamado BrochureUpload
. Defina sua Visible
propriedade como False
.
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.
Figura 14: Inicialmente, a opção Usar folheto atual é Selecionada (Clique para exibir a imagem em tamanho real)
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 aBrochurePath
coluna
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 mesmaBrochurePath
configuração. Portanto, precisamos definir o parâmetro ObjectDataSource parabrochurePath
o valor existenteBrochurePath
do registro que está sendo atualizado. O parâmetro ObjectDataSource sbrochurePath
pode ser definido usandoe.NewValues["brochurePath"] = value
. - Se o
SelectedValue
for 2, queremos definir o valorNULL
doBrochurePath
registro como . Isso pode ser feito definindo o parâmetro ObjectDataSource comobrochurePath
, o que resulta em um banco de dadosNULL
sendo usado naUPDATE
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 doBrochurePath
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 ounull
se nenhum arquivo foi salvo.DeleteRememberedBrochurePath
excluirá o arquivo especificado pelo caminho na variáveldeletedCategorysPdfPath
de página sedeletedCategorysPdfPath
nãonull
for .
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.
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 ID
PictureUpload
como . 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.
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.
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, ValidPictureUpload
e 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:
- 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. - Abra a imagem no editor de imagens de sua escolha. Você pode usar Microsoft Paint, por exemplo.
- Salve o bitmap como uma imagem JPG.
- 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.