Cómo exportar la información de un GridView a Excel

ARTÍCULO DESACTUALIZADO

Este artículo es algo viejo, encuentras una mejor forma de hacer la exportación en este otro artículo.


En algunas ocasiones queremos que toda la información que estamos desplegando con un GridView se exporte automáticamente a un archivo de Excel desde nuestra aplicación ASP.NET 2.

Para hacerlo, basta colocar un GridView vacío en nuestra página (puede ser uno diferente al que utilizas para mostrar la información en HTML).

  1. <asp:GridView ID="GridViewExcel" runat="server">
  2. </asp:GridView>

También necesitamos un botón que al ser presionado haga la exportación:

  1. <asp:LinkButton ID="ExportarAExcel" runat="server">Exportar a Excel</asp:LinkButton>

Por último, aquí está el código que hace la exportación:

  1. Protected Sub ExportarAExcel_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ExportarAExcel.Click
  2.     Dim gv As GridView
  3.     gv = Page.Master.FindControl("form1").FindControl("Cuerpo").FindControl("GridviewExcel")
  4.     'Estoy obteniendo la información de mi Business Logic Layer, pero lo puedes hacer
  5.     'de la manera en la que estés acostumbrado
  6.     Dim Logica As New BLLUsers()
  7.     Dim DTable As DSEmpresa.usersDataTable
  8.     DTable = Logica.GetData()
  9.     gv.DataSource = DTable
  10.     gv.DataBind()
  11.     'Exportar a Excel
  12.     Response.Clear()
  13.     Response.Buffer = True
  14.     Response.ContentType = "application/vnd.ms-excel;"
  15.     Response.ContentEncoding = System.Text.Encoding.UTF7
  16.     Response.Charset = ""
  17.     Me.EnableViewState = False
  18.     Dim oStringWriter As System.IO.StringWriter = New System.IO.StringWriter
  19.     Dim oHtmlTextWriter As System.Web.UI.HtmlTextWriter = New System.Web.UI.HtmlTextWriter(oStringWriter)
  20.     'Me.ClearControls(dg)
  21.     gv.RenderControl(oHtmlTextWriter)
  22.     Response.Write(oStringWriter.ToString())
  23.     Response.End()
  24. End Sub
  25. Public Overrides Sub VerifyRenderingInServerForm(ByVal control As Control)
  26.     'Este override debe estar aqui ya que si no aparecera un error
  27.     'indicando que el Gridview debe estar dentro de un form runat=server
  28. End Sub

Actualización
Si por alguna razón obtienes el error:
RegisterForEventValidation can only be called during Render

intenta colocar EnableEventValidation="false" en la directiva de la página, de tal forma que quede algo así:

  1. <%@ Page Title="" Language="VB" MasterPageFile="~/MasterPages/Principal.master" AutoEventWireup="false" CodeFile="Ejemplo.aspx.vb" Inherits="Ejemplo" EnableEventValidation="false" %>

Cómo pasar el id en un ButtonField de Gridview

Si tienes un GridView y quieres realizar una acción personalizada, basta con agregar una columna ButtonField, y pasar el iddel registro que seleccionaron.

El GridView:

  1. <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1" AllowPaging="True" AllowSorting="True" CellPadding="0" PageSize="30" GridLines="None" Width="100%" DataKeyNames="id">
  2.     <Columns>
  3.         <asp:BoundField DataField="id" HeaderText="ID" InsertVisible="False" SortExpression="id" />
  4.         <asp:BoundField DataField="status" HeaderText="Status" SortExpression="status" />
  5.         <asp:ButtonField CommandName="ConmutarStatus" HeaderText="Conmutar status" Text="Conmutar" ButtonType="Link" />
  6.    </Columns>
  7. </asp:GridView>

Nota la propiedad DataKeyNames. La establecí en id porque ese es el nombre de mi columna llave en la tabla.

Cada vez que se hace clic en la columna ButtonField, se dispara el evento RowCommand y es ahí donde debemos realizar la acción.

  1. Protected Sub GridView1_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView1.RowCommand
  2.         If e.CommandName = "ConmutarStatus" Then
  3.  
  4.             Dim indice As Integer = Convert.ToInt32(e.CommandArgument)
  5.             Dim id As Integer = GridView1.DataKeys(indice).Value
  6.             'Ahora en id tenemos el id de nuestra tabla.
  7.             'Podemos realizar cualquier acción con este valor.
  8.  
  9.  
  10.         End If
  11.     End Sub

Cómo enviar un formulario en un MasterPage oprimiendo la tecla Enter

Estoy trabajando con un MasterPage, el cual tiene un campo de texto y un ImageButton para efectuar una búsqueda en el sitio web.

Tengo una página de Inicio de Sesión, la cual está basada en el MasterPage. Cuando tecleó nombre de usuario y contraseña, y oprimo Enter, en lugar de enviarse el formulario del login, se envía el formulario del MasterPage porque toma precedencia sobre el formulario del login.

Después de investigar en Google, llegué al siguiente código:

El control de Login no tiene nada de especial:

  1. <asp:Content ID="Content1" ContentPlaceHolderID="Cuerpo" Runat="Server">
  2.  
  3.     <asp:Login ID="Login1" runat="server" DisplayRememberMe="False" FailureText="Datos incorrectos, inténtelo nuevamente."
  4.        LoginButtonText="Entrar" PasswordLabelText="Contraseña:" TitleText="Inicio de sesión"
  5.        UserNameLabelText="Nombre de usuario:" LoginButtonImageUrl="~/Images/Botones/b_iniciarSesion.jpg" LoginButtonType="Image">
  6.     </asp:Login>
  7.    
  8. </asp:Content>

En el código de servidor de esa misma página, en el evento Load, coloca lo siguiente:

  1. Login1.Focus()
  2. Dim FormaPpal As HtmlForm = Master.FindControl("form1")
  3. Dim Boton As ImageButton = Login1.FindControl("LoginImageButton")
  4. If Not FormaPpal Is Nothing And Not Boton Is Nothing Then
  5.     FormaPpal.DefaultButton = Boton.UniqueID
  6. End If

Estamos estableciendo la propiedad DefaultButton del control Form en el botón del Login. Por cierto, esta propiedad también existe en el control Panel, podría ser útil en algún otro caso. Esta propiedad está diciendo que cuando se oprima la tecla Enter, se debe realizar la misma acción que cuando se hace clic en el botón indicado.

form1 es el nombre del formulario que aparece en el MasterPage

LoginImageButton no aparece en ninguna parte, pero es el nombre que se le da por default al botón de submit, ya que yo utilicé un botón de imagen.

En caso de que uses un botón regular utiliza Button en la declaración y LoginButton en el FindControl (línea 3).

Ahora bien, todo arreglado con el Login, pero cuando intento hacer una búsqueda tecleando una palabra en el campo de búsqueda (que vive en el MasterPage) y oprimo la tecla Enter, entonces se intenta enviar el formulario de inicio de sesión.

Además, en todas las otras páginas basadas en el MasterPage (y que no contienen el Login) cuando tecleo una palabra de búsqueda y oprimo Enter solamente se hace un postback, pero no se efectúa la búsqueda.

Para solucionar el problema, basta abrir el MasterPage y establecer dicha propiedad:

  1. <form id="form1" runat="server" DefaultButton="BotonIr">

BotonIr es mi botón que al ser oprimido envía a la página que realiza la búsqueda.

¡Listo! 😀

Cómo mostrar un mensaje si un DataList o Repeater está vací­o

El GridView provee una propiedad EmptyDataText en la que nosotros establecemos un texto que se desplegará si no se encuentran registros.

En un DataList no existe esta propiedad. Para mostrar el mensaje nos tenemos que valer de un Label:

  1. <asp:Label ID="Mensaje" runat="server" Text="No se encontraron registros."></asp:Label>

Después tenemos que utilizar el evento DataBinding para mostrar el mensaje y el evento ItemDataBound para ocultarlo.

El evento DataBinding ocurre primero que ItemDataBound, es por eso que al principio lo mostramos, y luego en el siguiente evento probamos si existen registros, y si no existen, lo mostramos.

La lógica queda así:

  1. Protected Sub DataList2_DataBinding(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataList2.DataBinding
  2.  
  3.     Mensaje.Visible = True
  4.  
  5. End Sub
  6.  
  7.  
  8. Protected Sub DataList2_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList2.ItemDataBound
  9.  
  10.     If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
  11.         Mensaje.Visible = False
  12.     End If
  13.    
  14. End Sub

Cómo obtener el tipo de un objeto con TypeOf

Estoy trabajando con un DataList y dentro de su ItemTemplate tengo lo siguiente:

  1. <asp:Image ID="Image1" runat="server" ImageUrl='<%# Funciones.DameURLImagen(Eval("Imagen")) %>' />

La función DameURLImagen debe revisar el parámetro que se le manda. Si es de tipo DBNull quiere decir que ese campo es nulo en la base de datos, así que debe mostrar una imagen genérica. Si es un String, entonces debe verificar si está vacío o no. Si lo está, se debe mostrar nuevamente la imagen genérica. Si no lo está se debe mostrar la imagen extraída de la base de datos.

Este chequeo de tipo se hace mediante TypeOf.

Aquí está nuestra función DameURLImagen:

  1. Shared Function DameURLImagen(ByVal Imagen As Object) As String
  2.     If IsDBNull(Imagen) Then
  3.         DameURLImagen = "~/Images/NoHayImagen.jpg"
  4.     Else
  5.         If TypeOf (Imagen) Is String Then
  6.             If Imagen = "" Then
  7.                 DameURLImagen = "~/Images/NoHayImagen.jpg"
  8.             Else
  9.                 DameURLImagen = "~/Images/" & Imagen
  10.             End If
  11.         Else
  12.             DameURLImagen = "~/Images/NoHayImagen.jpg"
  13.         End If
  14.     End If
  15. End Function