Compartir a través de


Actualizar y eliminar los datos binarios existentes (VB)

de Scott Mitchell

Descargar PDF

En tutoriales anteriores vimos cómo el control GridView facilita la edición y eliminación de datos de texto. En este tutorial, vemos cómo el control GridView también permite editar y eliminar datos binarios, tanto si esos datos binarios se guardan en la base de datos como si se almacenan en el sistema de archivos.

Introducción

En los últimos tres tutoriales hemos agregado bastante funcionalidad para trabajar con datos binarios. Comenzamos agregando una BrochurePath columna a la Categories tabla y actualizando la arquitectura en consecuencia. También hemos agregado métodos de capa de acceso a datos y capa lógica de negocios para trabajar con la columna existente Picture de la tabla Categories, que contiene el contenido binario de un archivo de imagen. Hemos creado páginas web para presentar los datos binarios de categoría en un GridView. Se proporciona un vínculo de descarga para el folleto, con la imagen de la categoría mostrada en un elemento <img>. Además, hemos agregado un DetailsView para permitir a los usuarios agregar una nueva categoría y cargar los datos de su folleto e imagen.

Todo lo que queda por implementar es la capacidad de editar y eliminar categorías existentes, lo que realizaremos en este tutorial mediante la edición y eliminación integradas de GridView. Al editar una categoría, el usuario podrá cargar opcionalmente una imagen nueva o hacer que la categoría siga usando la existente. Para el folleto, pueden optar por utilizar el folleto existente, cargar un nuevo folleto o indicar que la categoría ya no tiene un folleto asociado. ¡Comencemos!

Paso 1: Actualización de la capa de acceso a datos

El DAL ha generado automáticamente los métodos Insert, Update, y Delete, pero estos métodos se generaron basados en la consulta principal CategoriesTableAdapter, que no incluye la columna Picture. Por lo tanto, los Insert métodos y Update no incluyen parámetros para especificar los datos binarios para la imagen de la categoría. Al igual que hicimos en el tutorial anterior, es necesario crear un nuevo método TableAdapter para actualizar la Categories tabla al especificar datos binarios.

Abra el Conjunto de datos tipado y, en el Diseñador, haga clic con el botón derecho en el encabezado de CategoriesTableAdapter y elija Agregar consulta en el menú contextual para iniciar el Asistente para configuración de consultas TableAdapter. Este asistente comienza preguntándonos cómo debe acceder la consulta TableAdapter a la base de datos. Elija Usar instrucciones SQL y haga clic en Siguiente. En el paso siguiente se solicita el tipo de consulta que se va a generar. Puesto que estamos creando una consulta para agregar un nuevo registro a la Categories tabla, elija ACTUALIZAR y haga clic en Siguiente.

Seleccione la opción UPDATE.

Figura 1: Seleccionar la opción UPDATE (haga clic para ver la imagen de tamaño completo)

Ahora es necesario especificar la UPDATE instrucción SQL. El asistente sugiere automáticamente una instrucción UPDATE correspondiente a la consulta principal del TableAdapter, que actualiza los valores de CategoryName, Description, y BrochurePath. Cambie la instrucción para que la Picture columna se incluya junto con un @Picture parámetro, de la siguiente manera:

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

La pantalla final del asistente nos pide que asignemos un nombre al nuevo método TableAdapter. Escriba UpdateWithPicture y haga clic en Finalizar.

Asigne un nombre al nuevo método TableAdapter UpdateWithPicture.

Figura 2: Asignar un nombre al nuevo método UpdateWithPicture TableAdapter (haga clic para ver la imagen de tamaño completo)

Paso 2: Agregar los métodos de la capa lógica de negocios

Además de actualizar la DAL, es necesario actualizar el BLL para incluir métodos para actualizar y eliminar una categoría. Estos son los métodos que se invocarán desde la capa de presentación.

Para eliminar una categoría, podemos usar el CategoriesTableAdapter método generado Delete automáticamente. Agregue el siguiente método a la clase 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

En este tutorial, vamos a crear dos métodos para actualizar una categoría: uno que espera los datos de imagen binaria e invoca el método UpdateWithPicture que acabamos de agregar a CategoriesTableAdapter y otro que acepta solo los valores CategoryName, Description, y BrochurePath, y utiliza la instrucción CategoriesTableAdapter generada automáticamente por la clase Update. La lógica detrás del uso de dos métodos es que, en algunas circunstancias, es posible que un usuario quiera actualizar la imagen de la categoría junto con sus otros campos, en cuyo caso el usuario tendrá que cargar la nueva imagen. Los datos binarios de la imagen cargada se pueden usar en la UPDATE instrucción . En otros casos, es posible que el usuario solo esté interesado en actualizar, por ejemplo, el nombre y la descripción. Pero si la UPDATE instrucción espera también los datos binarios de la Picture columna, tendríamos que proporcionar esa información también. Esto requeriría un viaje adicional a la base de datos para devolver los datos de imagen del registro que se está editando. Por lo tanto, queremos dos UPDATE métodos. La capa lógica de negocios determinará cuál se usará en función de si se proporcionan datos de imagen al actualizar la categoría.

Para facilitar esto, agregue dos métodos a la CategoriesBLL clase , ambos denominados UpdateCategory. El primero debe aceptar tres String s, una Byte matriz y un Integer como sus parámetros de entrada; el segundo, solo tres String s y un Integer. Los String parámetros de entrada son para el nombre, la descripción y la ruta del archivo de folleto de la categoría, la Byte matriz es para el contenido binario de la imagen de la categoría e Integer identifica el CategoryID del registro que se va a actualizar. Observe que la primera sobrecarga invoca la segunda si la matriz pasada Byte es 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

Paso 3: Transferir la funcionalidad de inserción y visualización

En el tutorial anterior hemos creado una página denominada UploadInDetailsView.aspx que enumera todas las categorías de una clase GridView y proporcionamos un DetailsView para agregar nuevas categorías al sistema. En este tutorial ampliaremos GridView para incluir soporte para edición y eliminación. En lugar de continuar trabajando desde UploadInDetailsView.aspx, coloquemos en su lugar los cambios de este tutorial en la página de la UpdatingAndDeleting.aspx misma carpeta, ~/BinaryData. Copie y pegue el marcado declarativo y el código de UploadInDetailsView.aspx a UpdatingAndDeleting.aspx.

Para empezar, abra la UploadInDetailsView.aspx página. Copie toda la sintaxis declarativa dentro del elemento <asp:Content>, como se muestra en la figura 3. A continuación, abra UpdatingAndDeleting.aspx y pegue este marcado dentro de su <asp:Content> elemento. Del mismo modo, copie el código de la clase de código subyacente de la UploadInDetailsView.aspx página en UpdatingAndDeleting.aspx.

Copie el marcado declarativo de UploadInDetailsView.aspx

Figura 3: Copiar el marcado declarativo de UploadInDetailsView.aspx (haga clic para ver la imagen de tamaño completo)

Después de copiar el marcado declarativo y el código, diríjase a UpdatingAndDeleting.aspx. Debería ver la misma salida y tener la misma experiencia de usuario que con la UploadInDetailsView.aspx página del tutorial anterior.

Paso 4: Agregar soporte para la eliminación a ObjectDataSource y GridView

Como se ha explicado en el tutorial An Overview of Inserting, Updating, and Deleting Data (Introducción a la inserción, actualización y eliminación de datos ), GridView proporciona funcionalidades de eliminación integradas y estas funcionalidades se pueden habilitar en el tic de una casilla si el origen de datos subyacente de la cuadrícula admite la eliminación. Actualmente, el ObjectDataSource al que está enlazado el GridView (CategoriesDataSource) no admite la eliminación.

Para solucionar este problema, haga clic en la opción Configurar origen de datos de la etiqueta inteligente ObjectDataSource para iniciar el asistente. La primera pantalla muestra que ObjectDataSource está configurado para trabajar con la CategoriesBLL clase . Presione Siguiente. Actualmente, solo se especifican las propiedades ObjectDataSource InsertMethod y SelectMethod . Sin embargo, el asistente actualizó automáticamente las listas desplegables en las pestañas ACTUALIZAR y ELIMINAR con los métodos UpdateCategory y DeleteCategory, respectivamente. Esto se debe a que en la CategoriesBLL clase hemos marcado estos métodos usando DataObjectMethodAttribute como métodos predeterminados para actualizar y eliminar.

Por ahora, establezca la lista desplegable de la pestaña UPDATE en (Ninguno), pero deje establecida la lista desplegable de la pestaña DELETE en DeleteCategory. Volveremos a este asistente en el paso 6 para agregar soporte de actualización.

Configurar ObjectDataSource para usar el método DeleteCategory

Figura 4: Configurar objectDataSource para usar el método (DeleteCategory imagen de tamaño completo)

Nota:

Al completar el asistente, Visual Studio puede preguntar si desea actualizar campos y claves, lo que regenerará los campos de controles web de datos. Elija No, ya que si elige Sí sobrescribirá las personalizaciones de campo que haya realizado.

ObjectDataSource ahora incluirá un valor para su DeleteMethod propiedad, así como un DeleteParameter. Recuerde que cuando se usa el asistente para especificar los métodos, Visual Studio establece la propiedad OldValuesParameterFormatStringObjectDataSource en original_{0}, lo que provoca problemas con las invocaciones de los métodos actualizar y eliminar. Por lo tanto, borre esta propiedad por completo o restablezcala al valor predeterminado, {0}. Si necesita actualizar la memoria en esta propiedad ObjectDataSource, consulte el tutorial Introducción a la inserción, actualización y eliminación de datos .

Después de completar el asistente y corregir el OldValuesParameterFormatString, el marcado declarativo de ObjectDataSource debe tener un aspecto similar al siguiente:

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

Después de configurar el ObjectDataSource, agregue funcionalidades de eliminación al GridView activando la casilla Habilitar eliminación de la etiqueta inteligente de GridView. Esto agregará un CommandField a GridView cuya ShowDeleteButton propiedad está establecida en True.

Habilitar la compatibilidad para eliminar en GridView

Figura 5: Habilitar la compatibilidad con la eliminación en GridView (haga clic para ver la imagen de tamaño completo)

Dedique un momento a probar la funcionalidad de eliminación. Hay una clave externa entre la tabla Products s CategoryID y la tabla Categories s CategoryID; por eso, recibirá una excepción de infracción de restricción de clave externa si intenta eliminar cualquiera de las ocho primeras categorías. Para probar esta funcionalidad, agregue una nueva categoría, proporcionando un folleto y una imagen. Mi categoría de prueba, que se muestra en la figura 6, incluye un archivo de folleto de prueba denominado Test.pdf y una imagen de prueba. En la figura 7 se muestra GridView después de agregar la categoría de prueba.

Agregar una categoría de prueba con un folleto e imagen

Figura 6: Agregar una categoría de prueba con un folleto e imagen (haga clic para ver la imagen de tamaño completo)

Después de insertar la categoría de prueba, se muestra en GridView.

Figura 7: Después de insertar la categoría de prueba, se muestra en gridView (haga clic para ver la imagen de tamaño completo)

En Visual Studio, actualice el Explorador de soluciones. Ahora debería ver un nuevo archivo en la ~/Brochures carpeta Test.pdf (vea la figura 8).

A continuación, haga clic en el vínculo Eliminar de la fila Categoría de prueba, lo que causa que la página se recargue y se active el método de la clase CategoriesBLLDeleteCategory. Esto invocará el método dal Delete , lo que hará que la instrucción adecuada DELETE se envíe a la base de datos. Los datos se vuelven a enlazar a GridView y el marcado se devuelve al cliente con la categoría de prueba que ya no está presente.

Aunque el flujo de trabajo de eliminación quitó correctamente el registro Categoría de prueba de la Categories tabla, no quitó su archivo de folleto del sistema de archivos del servidor web. Actualice el Explorador de soluciones y verá que Test.pdf todavía está sentado en la ~/Brochures carpeta .

El archivo Test.pdf no se eliminó del sistema de archivos del servidor web

Figura 8: El Test.pdf archivo no se eliminó del sistema de archivos del servidor web

Paso 5: Eliminar el archivo de folletos de la categoría eliminada

Una de las desventajas de almacenar datos binarios externos a la base de datos es que se deben realizar pasos adicionales para limpiar estos archivos cuando se elimina el registro de base de datos asociado. GridView y ObjectDataSource proporcionan eventos que se activan antes y después de que se haya realizado el comando delete. En realidad, es necesario crear controladores de eventos para los eventos previos y posteriores a la acción. Antes de eliminar del Categories registro, necesitamos determinar la ruta del archivo PDF, pero no queremos eliminar el PDF antes de que se elimine la categoría en caso de excepción y la categoría no sea eliminada.

El evento RowDeleting de GridView se desencadena antes de que se haya invocado el comando de eliminación de ObjectDataSource, mientras que su evento RowDeleted se desencadena después. Cree controladores de eventos para estos dos eventos mediante el código siguiente:

' 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

En el RowDeleting controlador de eventos, la CategoryID de la fila que se va a eliminar se toma de la colección GridView s DataKeys , a la que se puede tener acceso en este controlador de eventos a través de la e.Keys colección. A continuación, se invoca la CategoriesBLL clase s GetCategoryByCategoryID(categoryID) para devolver información sobre el registro que se va a eliminar. Si el objeto devuelto CategoriesDataRow tiene un NULL``BrochurePath valor distinto de cero, se almacena en la variable de la página deletedCategorysPdfPath para que el archivo se pueda eliminar en el controlador de eventos RowDeleted.

Nota:

En lugar de recuperar los BrochurePath detalles del Categories registro que se va a eliminar en el RowDeleting controlador de eventos, podríamos haber agregado BrochurePath a la propiedad DataKeyNames de GridView y accedido al valor del registro a través de la colección e.Keys. Si lo hace, aumentaría ligeramente el tamaño del estado de vista de GridView, pero reduciría la cantidad de código necesario y guardaría un viaje a la base de datos.

Una vez invocado el comando De eliminación subyacente de ObjectDataSource, se activa el controlador de eventos de RowDeleted GridView. Si no hay excepciones en la eliminación de los datos y hay un valor para deletedCategorysPdfPath, el PDF se elimina del sistema de archivos. Tenga en cuenta que este código adicional no es necesario para limpiar los datos binarios de la categoría asociados a su imagen. Esto se debe a que los datos de imagen se almacenan directamente en la base de datos, por lo que la eliminación de la Categories fila también elimina los datos de imagen de esa categoría.

Después de agregar los dos controladores de eventos, vuelva a ejecutar este caso de prueba. Al eliminar la categoría, también se elimina su PDF asociado.

La actualización de los datos binarios asociados de un registro existente proporciona algunos desafíos interesantes. El resto de este tutorial profundiza en la adición de funcionalidades de actualización al folleto y la imagen. En el paso 6 se exploran las técnicas para actualizar la información del folleto, mientras que el paso 7 examina la actualización de la imagen.

Paso 6: Actualizar un folleto de categoría

Como se describe en el tutorial Introducción a la inserción, actualización y eliminación de datos, GridView ofrece compatibilidad integrada de edición de nivel de fila que se puede implementar mediante la marca de una casilla si su origen de datos subyacente está configurado correctamente. Actualmente, ObjectDataSource CategoriesDataSource aún no está configurado para incluir compatibilidad con la actualización, por lo que vamos a agregarlo.

Haga clic en el vínculo Configurar origen de datos desde el asistente objectDataSource y continúe con el segundo paso. Debido a que DataObjectMethodAttribute se usa en CategoriesBLL, la lista desplegable UPDATE debe rellenarse automáticamente con la sobrecarga de UpdateCategory que admite cuatro parámetros de entrada (para todas las columnas, excepto Picture). Cambie esto para que use la sobrecarga con cinco parámetros.

Configurar ObjectDataSource para usar el método UpdateCategory que incluye un parámetro para la imagen

Figura 9: Configure el ObjectDataSource para usar el método que incluye un parámetro para UpdateCategory (Picture)

ObjectDataSource incluirá ahora un valor para su UpdateMethod propiedad, así como los valores correspondientes UpdateParameter . Como se indica en el paso 4, Visual Studio establece la propiedad OldValuesParameterFormatString del ObjectDataSource en original_{0} al usar el asistente para configurar los orígenes de datos. Esto provocará problemas con las invocaciones del método update y delete. Por lo tanto, borre esta propiedad por completo o restablezcala al valor predeterminado, {0}.

Después de completar el asistente y corregir el OldValuesParameterFormatString, el marcado declarativo de ObjectDataSource debe ser similar al siguiente:

<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 activar las características de edición integradas de GridView, active la opción Habilitar edición desde la etiqueta inteligente GridView s. Esto establecerá la propiedad ShowEditButton CommandField en True, lo que dará lugar a la adición de un botón de edición (y botones de actualización y cancelación para la fila que se está editando).

Configuración de GridView para admitir la edición

Figura 10: Configurar GridView para admitir la edición (haga clic para ver la imagen de tamaño completo)

Visite la página a través de un navegador y haga clic en uno de los botones Editar de la fila. Los campos delimitados CategoryName y Description son representados como cuadros de texto. BrochurePath TemplateField carece de un EditItemTemplate, por lo que continúa mostrando su ItemTemplate vínculo al folleto. Picture ImageField se representa como un TextBox cuya Text propiedad tiene asignado el valor del valor de ImageFieldDataImageUrlField, en este caso CategoryID.

GridView carece de una interfaz de edición para BrochurePath

Figura 11: GridView carece de una interfaz de edición para (BrochurePath ver la imagen de tamaño completo)

Personalización de la interfaz de edición

Es necesario crear una interfaz de edición para TemplateField BrochurePath , una que permita al usuario:

  • Deje el folleto de la categoría as-is.
  • Actualice el folleto de la categoría mediante la carga de un nuevo folleto o
  • Quite el folleto de la categoría por completo (en caso de que la categoría ya no tenga un folleto asociado).

También necesitamos actualizar la Picture interfaz de edición de ImageField, pero llegaremos a esto en el paso 7.

En la etiqueta inteligente GridView, haga clic en el vínculo Editar plantillas y seleccione TemplateField BrochurePath s EditItemTemplate en la lista desplegable. Agregue un control Web RadioButtonList a esta plantilla, estableciendo su ID propiedad en BrochureOptions y su AutoPostBack propiedad en True. En la ventana Propiedades, haga clic en los puntos suspensivos de la Items propiedad, que abrirá el ListItem Editor de recopilación. Agregue las tres opciones siguientes con Value s 1, 2 y 3, respectivamente:

  • Utilice el folleto actual
  • Quitar folleto actual
  • Cargar nuevo folleto

Establezca la primera ListItem propiedad s Selected en True.

Agregar tres elementos de lista a la RadioButtonList

Figura 12: Añadir tres elementos a la RadioButtonList

Debajo de RadioButtonList, agregue un control FileUpload denominado BrochureUpload. Establezca la propiedad Visible en False.

Agregar un control RadioButtonList y FileUpload a EditItemTemplate

Figura 13: Agregar los controles RadioButtonList y FileUpload al EditItemTemplate (Haga clic para ver la imagen de tamaño completo)

RadioButtonList proporciona las tres opciones para el usuario. La idea es que el control FileUpload solo se mostrará si se selecciona la última opción, Cargar nuevo folleto. Para ello, cree un controlador de eventos para el evento RadioButtonList y SelectedIndexChanged agregue el código siguiente:

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

Dado que los controles RadioButtonList y FileUpload están dentro de una plantilla, tenemos que escribir un poco de código para acceder mediante programación a estos controles. Al controlador de eventos SelectedIndexChanged se le pasa una referencia del RadioButtonList en el parámetro de entrada sender. Para obtener el control FileUpload, es necesario obtener el control primario RadioButtonList y usar el FindControl("controlID") método desde allí. Una vez que tengamos una referencia a los controles RadioButtonList y FileUpload, la propiedad del control Visible FileUpload se establece True solo si el RadioButtonList es igual a SelectedValue 3, que es el Value para Cargar nuevo folleto ListItem.

Con este código en vigor, dedique un momento a probar la interfaz de edición. Haga clic en el botón Editar para una fila. Inicialmente, se debe seleccionar la opción Usar folleto actual. Al cambiar el índice seleccionado, se produce un postback. Si se selecciona la tercera opción, se muestra el control FileUpload; de lo contrario, se oculta. En la figura 14 se muestra la interfaz de edición cuando se hace clic por primera vez en el botón Editar; La figura 15 muestra la interfaz después de seleccionar la opción Cargar nuevo folleto.

Inicialmente, la opción Usar folleto actual está seleccionada

Figura 14: Inicialmente, la opción Usar folleto actual está seleccionada (Haga clic para ver la imagen de tamaño completo)

Elegir la opción Cargar nuevo folleto Muestra el control FileUpload

Figura 15: Elegir la opción Cargar nuevo folleto Muestra el control FileUpload (Haga clic para ver la imagen de tamaño completo)

Guardar el archivo de folleto y actualizar laBrochurePathcolumna

Cuando se hace clic en el botón Actualizar del GridView, el evento RowUpdating se desencadena. Se invoca el comando de actualización de ObjectDataSource y, a continuación, se desencadena el evento de GridView RowUpdated. Al igual que con el flujo de trabajo de eliminación, es necesario crear controladores de eventos para ambos eventos. En el RowUpdating controlador de eventos, es necesario determinar qué acción realizar en función de SelectedValue del control BrochureOptions RadioButtonList.

  • Si es SelectedValue 1, queremos seguir usando la misma BrochurePath configuración. Por lo tanto, es necesario establecer el parámetro ObjectDataSource s brochurePath en el valor existente BrochurePath del registro que se está actualizando. El parámetro brochurePath de ObjectDataSource se puede establecer mediante e.NewValues["brochurePath"] = value.
  • Si SelectedValue es 2, queremos establecer el valor BrochurePath del registro en NULL. Esto se puede lograr estableciendo el parámetro brochurePath del ObjectDataSource en Nothing, lo que da como resultado que se use una base de datos NULL en la instrucción UPDATE. Si hay un archivo de folleto existente que se va a quitar, es necesario eliminar el archivo existente. Sin embargo, solo queremos hacerlo si la actualización se completa sin generar una excepción.
  • Si es SelectedValue 3, queremos asegurarnos de que el usuario ha cargado un archivo PDF y, a continuación, guardarlo en el sistema de archivos y actualizar el valor de la columna del BrochurePath registro. Además, si hay un archivo de folleto existente que se va a reemplazar, es necesario eliminar el archivo anterior. Sin embargo, solo queremos hacerlo si la actualización se completa sin generar una excepción.

Los pasos necesarios para completarse cuando RadioButtonList s SelectedValue es 3 son prácticamente idénticos a los usados por el controlador de eventos DetailsView ItemInserting . Este controlador de eventos se ejecuta cuando se agrega un nuevo registro de categoría desde el control DetailsView que hemos agregado en el tutorial anterior. Por lo tanto, nos corresponde refactorizar esta funcionalidad en métodos independientes. En concreto, he movido la funcionalidad común a dos métodos:

  • ProcessBrochureUpload(FileUpload, out bool) acepta como entrada una instancia de control FileUpload y un valor booleano de salida que especifica si la operación de eliminación o edición debe continuar o si se debe cancelar debido a algún error de validación. Este método devuelve la ruta de acceso al archivo guardado o null si no se guardó ningún archivo.
  • DeleteRememberedBrochurePath elimina el archivo especificado por la ruta de acceso en la variable de página deletedCategorysPdfPath si deletedCategorysPdfPath no es null.

A continuación se muestra el código de estos dos métodos. Tenga en cuenta la similitud entre ProcessBrochureUpload y el controlador de eventos DetailsView ItemInserting del tutorial anterior. En este tutorial he actualizado los controladores de eventos detailsView para usar estos nuevos métodos. Descargue el código asociado a este tutorial para ver las modificaciones en los controladores 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

Los controladores de eventos de GridView RowUpdating y RowUpdated usan los métodos ProcessBrochureUpload y DeleteRememberedBrochurePath, como se muestra en el código siguiente.

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 cómo el RowUpdating controlador de eventos usa una serie de instrucciones condicionales para realizar la acción adecuada en función del BrochureOptions valor de la propiedad RadioButtonList SelectedValue .

Con este código en su lugar, puede editar una categoría y hacer que use su folleto actual, no use ningún folleto o cargue uno nuevo. Inténtelo. Establezca puntos de interrupción en los controladores de eventos RowUpdating y RowUpdated para entender mejor el flujo de trabajo.

Paso 7: Cargar una nueva imagen

La Picture interfaz de edición de ImageField se representa como un cuadro de texto rellenado con el valor de su DataImageUrlField propiedad. Durante el flujo de trabajo de edición, GridView pasa un parámetro a ObjectDataSource, donde el nombre del parámetro es el valor de la propiedad DataImageUrlField de ImageField y el valor del parámetro es el valor ingresado en el cuadro de texto de la interfaz de edición. Este comportamiento es adecuado cuando la imagen se guarda como un archivo en el sistema de archivos y DataImageUrlField contiene la dirección URL completa de la imagen. Con tales circunstancias, la interfaz de edición muestra la dirección URL de la imagen en el cuadro de texto, que el usuario puede cambiar y volver a guardar en la base de datos. Es cierto que esta interfaz predeterminada no permite al usuario cargar una nueva imagen, pero sí les da la opción de cambiar el URL de la imagen del valor actual a uno nuevo. En este tutorial, sin embargo, la interfaz de edición predeterminada de ImageField no es suficiente porque los Picture datos binarios se almacenan directamente en la base de datos y la DataImageUrlField propiedad solo contiene .CategoryID

Para comprender mejor lo que sucede en nuestro tutorial cuando un usuario edita una fila con imageField, considere el ejemplo siguiente: un usuario edita una fila con CategoryID 10, lo que hace Picture que ImageField se represente como un cuadro de texto con el valor 10. Imagine que el usuario cambia el valor de este cuadro de texto a 50 y hace clic en el botón Actualizar. Se produce un postback y GridView crea inicialmente un parámetro denominado CategoryID con el valor 50. Sin embargo, antes de que GridView envíe este parámetro (y los CategoryName parámetros y Description ), agrega los valores de la DataKeys colección. Por lo tanto, sobrescribe el valor del parámetro CategoryID con el valor subyacente CategoryID de la fila actual, 10. En resumen, la interfaz de edición de ImageField no tiene efecto sobre el flujo de trabajo de edición en este tutorial porque los nombres de la propiedad de ImageField y el valor de la cuadrícula son lo mismo.

Aunque ImageField facilita la visualización de una imagen basada en datos de base de datos, no queremos proporcionar un cuadro de texto en la interfaz de edición. En su lugar, queremos ofrecer un control FileUpload que el usuario final puede usar para cambiar la imagen de la categoría. A diferencia del BrochurePath valor, para estos tutoriales hemos decidido requerir que cada categoría tenga una imagen. Por lo tanto, no es necesario que el usuario indique que no hay ninguna imagen asociada; el usuario puede cargar una imagen nueva o dejar la imagen actual as-is.

Para personalizar la interfaz de edición de ImageField, es necesario convertirla en templateField. En la etiqueta inteligente GridView, haga clic en el vínculo Editar columnas, seleccione ImageField y haga clic en el vínculo Convertir este campo en un campo TemplateField.

Convertir ImageField en un templateField

Figura 16: Convertir ImageField en un TemplateField

La conversión de ImageField en un TemplateField de esta manera genera un TemplateField con dos plantillas. Como muestra la siguiente sintaxis declarativa, ItemTemplate contiene un control Web de imágenes cuya propiedad ImageUrl se asigna mediante el enlace de datos con base en las propiedades DataImageUrlField y DataImageUrlFormatString del campo de imagen. EditItemTemplate contiene un TextBox cuya Text propiedad está enlazada al valor especificado por la DataImageUrlField propiedad .

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

Es necesario actualizar el EditItemTemplate para que use un control FileUpload. En la etiqueta inteligente GridView, haga clic en el vínculo Editar plantillas y, a continuación, seleccione TemplateField Picture s EditItemTemplate en la lista desplegable. En la plantilla debería ver un cuadro de texto y quitarlo. A continuación, arrastre un control FileUpload desde el Cuadro de herramientas a la plantilla y establezca su propiedad en IDPictureUpload. Agregue también el texto Para cambiar la imagen de la categoría, especifique una imagen nueva. Para mantener la imagen de la categoría igual, deje también el campo vacío en la plantilla.

Agregar un control FileUpload a la EditItemTemplate

Figura 17: Agregar un control FileUpload a EditItemTemplate (Haga clic para ver la imagen de tamaño completo)

Después de personalizar la interfaz de edición, vea el progreso en un explorador. Al ver una fila en modo de solo lectura, la imagen de la categoría se muestra como estaba antes, pero al hacer clic en el botón Editar se representa la columna de imagen como texto con un control FileUpload.

La interfaz de edición incluye un control FileUpload

Figura 18: La interfaz de edición incluye un control FileUpload (haga clic para ver la imagen de tamaño completo)

Recuerde que ObjectDataSource está configurado para llamar al método de la CategoriesBLL clase s UpdateCategory que acepta como entrada los datos binarios de la imagen como una Byte matriz. Sin embargo, si esta matriz es Nothing, se llama a la sobrecarga alternativa UpdateCategory , que emite la UPDATE instrucción SQL que no modifica la Picture columna, dejando intacta la imagen actual de la categoría. Por lo tanto, en el controlador de eventos de RowUpdating GridView, es necesario hacer referencia mediante programación al PictureUpload control FileUpload y determinar si se cargó un archivo. Si no se subió ningún archivo, no queremos asignar un valor al picture parámetro. Por otro lado, si se cargó un archivo en el PictureUpload control FileUpload, queremos asegurarnos de que es un archivo JPG. Si es así, podemos enviar su contenido binario al objectDataSource a través del picture parámetro .

Al igual que con el código usado en el paso 6, gran parte del código necesario aquí ya existe en el controlador de eventos DetailsView.ItemInserting Por lo tanto, he refactorizado la funcionalidad común en un nuevo método, ValidPictureUploady he actualizado el ItemInserting controlador de eventos para usar este método.

Agregue el código siguiente al inicio del controlador de eventos de RowUpdating GridView. Es importante que este código venga antes del código que guarda el archivo de folleto, ya que no queremos guardar el folleto en el sistema de archivos del servidor web si se carga un archivo de imagen no válido.

' 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

El ValidPictureUpload(FileUpload) método toma un control FileUpload como único parámetro de entrada y comprueba la extensión del archivo cargado para asegurarse de que el archivo cargado es un JPG; solo se llama si se carga un archivo de imagen. Si no se carga ningún archivo, no se establece el parámetro picture y, por tanto, usa su valor predeterminado de Nothing. Si se cargó una imagen y ValidPictureUpload devuelve True, al picture parámetro se le asignan los datos binarios de la imagen cargada; si el método devuelve False, se cancela el flujo de trabajo de actualización y se cierra el controlador de eventos.

El ValidPictureUpload(FileUpload) código de método, que se refactorizó desde el controlador de eventos de DetailsView ItemInserting, sigue:

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

Paso 8: Reemplazar las imágenes de categorías originales por JPG

Recuerde que las ocho imágenes originales de categorías son archivos de mapa de bits encapsulados en un encabezado OLE. Ahora que hemos agregado la capacidad de editar la imagen de un registro existente, dedique un momento a cambiar estos mapas de bits por JPG. Si desea seguir usando las imágenes de categoría actuales, puede convertirlos a JPG realizando los pasos siguientes:

  1. Guarde las imágenes de mapa de bits en el disco duro. Visite la página en el UpdatingAndDeleting.aspx explorador y para cada una de las ocho primeras categorías, haga clic con el botón derecho en la imagen y elija guardar la imagen.
  2. Abra la imagen en el editor de imágenes que prefiera. Puede usar Microsoft Paint, por ejemplo.
  3. Guarde el mapa de bits como una imagen JPG.
  4. Actualice la imagen de la categoría a través de la interfaz de edición mediante el archivo JPG.

Después de editar una categoría y cargar la imagen JPG, la imagen no se representará en el explorador porque la DisplayCategoryPicture.aspx página quita los primeros 78 bytes de las imágenes de las ocho primeras categorías. Corrija esto quitando el código que realiza la eliminación del encabezado OLE. Después de hacerlo, el DisplayCategoryPicture.aspx``Page_Load controlador de eventos debe tener solo el código siguiente:

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

Nota:

Las UpdatingAndDeleting.aspx interfaces de inserción y edición de páginas podrían usar un poco más de trabajo. Los BoundFields en CategoryName DetailsView y Description GridView deben convertirse en TemplateFields. Puesto que CategoryName no permite valores NULL, se debe agregar un validador de campo requerido. Y es probable que el Description TextBox deba convertirse en un TextBox de varias líneas. Dejo estos toques finales como ejercicio para ti.

Resumen

En este tutorial se completa nuestro análisis sobre cómo trabajar con datos binarios. En este tutorial y los tres anteriores vimos cómo se pueden almacenar datos binarios en el sistema de archivos o directamente dentro de la base de datos. Un usuario proporciona datos binarios al sistema seleccionando un archivo de su disco duro y cargándolo en el servidor web, donde se puede almacenar en el sistema de archivos o insertarlo en la base de datos. ASP.NET 2.0 incluye un control FileUpload que permite proporcionar una interfaz tan fácil como arrastrar y soltar. Sin embargo, como se indica en el tutorial carga de archivos , el control FileUpload solo es adecuado para cargas de archivos relativamente pequeñas, idealmente no superando un megabyte. También hemos explorado cómo asociar datos cargados con el modelo de datos subyacente, así como cómo editar y eliminar los datos binarios de los registros existentes.

Nuestro siguiente conjunto de tutoriales explora varias técnicas de almacenamiento en caché. El almacenamiento en caché proporciona un medio para mejorar el rendimiento general de una aplicación tomando los resultados de operaciones costosas y almacenarlos en una ubicación a la que se pueda acceder más rápidamente.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.

Agradecimientos especiales a

Esta serie de tutoriales fue revisada por muchos revisores de gran ayuda. El revisor principal de este tutorial fue Teresa Murphy. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.