Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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.
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.
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
.
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.
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 OldValuesParameterFormatString
ObjectDataSource 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
.
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.
Figura 6: Agregar una categoría de prueba con un folleto e imagen (haga clic para ver la imagen de tamaño completo)
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 CategoriesBLL
DeleteCategory
. 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 .
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.
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).
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
.
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
.
Figura 12: Añadir tres elementos a la RadioButtonList
Debajo de RadioButtonList, agregue un control FileUpload denominado BrochureUpload
. Establezca la propiedad Visible
en False
.
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.
Figura 14: Inicialmente, la opción Usar folleto actual está seleccionada (Haga clic para ver la imagen de tamaño completo)
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 laBrochurePath
columna
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 mismaBrochurePath
configuración. Por lo tanto, es necesario establecer el parámetro ObjectDataSource sbrochurePath
en el valor existenteBrochurePath
del registro que se está actualizando. El parámetrobrochurePath
de ObjectDataSource se puede establecer mediantee.NewValues["brochurePath"] = value
. - Si
SelectedValue
es 2, queremos establecer el valorBrochurePath
del registro enNULL
. Esto se puede lograr estableciendo el parámetrobrochurePath
del ObjectDataSource enNothing
, lo que da como resultado que se use una base de datosNULL
en la instrucciónUPDATE
. 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 delBrochurePath
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 onull
si no se guardó ningún archivo. -
DeleteRememberedBrochurePath
elimina el archivo especificado por la ruta de acceso en la variable de páginadeletedCategorysPdfPath
sideletedCategorysPdfPath
no esnull
.
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.
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 ID
PictureUpload
. 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.
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.
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, ValidPictureUpload
y 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:
- 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. - Abra la imagen en el editor de imágenes que prefiera. Puede usar Microsoft Paint, por ejemplo.
- Guarde el mapa de bits como una imagen JPG.
- 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.