Para los que nos dedicamos al mundo del desarrollo con WebForms en ASP.NET con la librería Telerik muchas veces queremos personalizar los radgrid para hacerlos multiselección con checkboxs, personalmente no encontré ejemplos muy claros y algunos que fallaban con el uso de Ajax, para tal dejaré un ejemplo usando el lenguaje Vb.Net.
1. Configuraremos el proyecto: Agregando la librería Telerik, en mi caso también agregue la EnterpriseLibrary para mi conexión a la base de datos.
2. Configuramos la conexión a la BD, para el ejemplo utilizaré la BD NORTWND en SQL Server 2012.
3. Para el ejemplo cree el procedimiento almacenado “EmployeeSalesByCountry” para que nos trajera información de ventas por usuario.
CREATE procedure [dbo].[EmployeeSalesByCountry] @Beginning_Date DateTime, @Ending_Date DateTime AS SELECT Employees.Country , Employees.LastName , Employees.FirstName , Orders.ShippedDate , Orders.OrderID , "Order Subtotals".Subtotal AS SaleAmount , CAST(0 as bit) seleccionado FROM Employees INNER JOIN (Orders INNER JOIN "Order Subtotals" ON Orders.OrderID = "Order Subtotals".OrderID) ON Employees.EmployeeID = Orders.EmployeeID WHERE Orders.ShippedDate Between @Beginning_Date And @Ending_Date
Fíjense que agregamos un campo BIT que representará el estado del checkbox de selección.
4. Agregamos la página que nos servirá para el ejemplo y creamos el formulario (pueden hacerlo desde la pestaña de Diseño, pero yo recomiendo que sea desde la pestaña de Código, pues es más rápido y limpio).
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="RadGridExample.aspx.vb" Inherits="RadGridExample.RadGridExample" %> <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="Sm1" runat="server" /> <div> <table cellpadding="5" cellspacing="0"> <tr> <td colspan="5"> <h1> RadGrid Multiselect con checkboxs</h1> </td> </tr> <tr> <td> Desde: </td> <td> <telerik:RadDatePicker ID="DpDesde" runat="server" /> </td> <td> Hasta: </td> <td> <telerik:RadDatePicker ID="DpHasta" runat="server" /> </td> <td> <telerik:RadButton ID="BtnBuscar" runat="server" Text="Buscar" /> </td> </tr> <tr> <td colspan="5"> <telerik:RadGrid ID="GvVentas" runat="server" AllowPaging="True" Width="100%" AutoGenerateColumns="False" AllowMultiRowSelection="True" EnableEmbeddedSkins="True" PageSize="10"> <PagerStyle AlwaysVisible="true" /> <MasterTableView CellSpacing="0" DataKeyNames="Country, LastName, FirstName, ShippedDate, OrderID, SaleAmount" > <Columns> <telerik:GridCheckBoxColumn DataField="seleccionado" Visible="false" /> <telerik:GridTemplateColumn> <HeaderTemplate> <asp:CheckBox ID="chbAll" OnCheckedChanged="ToggleSelectedStateDisponibles" AutoPostBack="True" runat="server" /> </HeaderTemplate> <ItemTemplate> <asp:CheckBox ID='chbSeleccionado' OnCheckedChanged="ToggleRowSelection" AutoPostBack="True" runat="server" /> </ItemTemplate> </telerik:GridTemplateColumn> <telerik:GridBoundColumn DataField="Country" HeaderText="Pais" HeaderStyle-HorizontalAlign="Center" /> <telerik:GridBoundColumn DataField="LastName" HeaderText="Apellido" HeaderStyle-HorizontalAlign="Center" /> <telerik:GridBoundColumn DataField="FirstName" HeaderText="Nombre" HeaderStyle-HorizontalAlign="Center" /> <telerik:GridBoundColumn DataField="ShippedDate" HeaderText="Fecha" DataFormatString="{0:dd/MM/yyyy}" HeaderStyle-HorizontalAlign="Center" /> <telerik:GridBoundColumn DataField="OrderID" HeaderText="Venta" HeaderStyle-HorizontalAlign="Center" /> <telerik:GridBoundColumn DataField="SaleAmount" HeaderText="Monto" DataFormatString="{0:$###,###.00}" ItemStyle-HorizontalAlign="Right" HeaderStyle-HorizontalAlign="Center" /> </Columns> <NoRecordsTemplate> <div> No hay resultados</div> </NoRecordsTemplate> <PagerStyle AlwaysVisible="True" /> </MasterTableView> </telerik:RadGrid> </td> </tr> <tr> <td colspan="5" align="center" valign="middle"> <telerik:RadButton ID="BtnGuardar" runat="server" Text="Guardar" /> </td> </tr> <tr> <td colspan="5"> <telerik:RadGrid ID="GvSeleccionados" runat="server" Width="100%" AutoGenerateColumns="False" EnableEmbeddedSkins="True"> <PagerStyle AlwaysVisible="true" /> <MasterTableView CellSpacing="0"> <Columns> <telerik:GridBoundColumn DataField="Country" HeaderText="Pais" /> <telerik:GridBoundColumn DataField="LastName" HeaderText="Apellido" /> <telerik:GridBoundColumn DataField="FirstName" HeaderText="Nombre" /> <telerik:GridBoundColumn DataField="ShippedDate" HeaderText="Fecha" DataFormatString="{0:dd/MM/yyyy}" /> <telerik:GridBoundColumn DataField="OrderID" HeaderText="Venta" /> <telerik:GridBoundColumn DataField="SaleAmount" HeaderText="Monto" DataFormatString="{0:$###,###.00}" /> </Columns> <NoRecordsTemplate> <div>No hay resultados</div> </NoRecordsTemplate> <PagerStyle AlwaysVisible="True" /> </MasterTableView> <ClientSettings EnableRowHoverStyle="true"> <Selecting AllowRowSelect="true" EnableDragToSelectRows="true" /> </ClientSettings> </telerik:RadGrid> </td> </tr> </table> <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server" DefaultLoadingPanelID="RadAjaxLoadingPanel1"> <AjaxSettings> <telerik:AjaxSetting AjaxControlID="BtnBuscar"> <UpdatedControls> <telerik:AjaxUpdatedControl ControlID="GvVentas" /> </UpdatedControls> </telerik:AjaxSetting> <telerik:AjaxSetting AjaxControlID="GvVentas"> <UpdatedControls> <telerik:AjaxUpdatedControl ControlID="GvVentas" /> </UpdatedControls> </telerik:AjaxSetting> <telerik:AjaxSetting AjaxControlID="BtnGuardar"> <UpdatedControls> <telerik:AjaxUpdatedControl ControlID="GvSeleccionados" /> </UpdatedControls> </telerik:AjaxSetting> </AjaxSettings> </telerik:RadAjaxManager> </div> </form> </body> </html>
Lo que nos dejará un formulario como:
Fíjese que hemos agregado un ScriptManager y un RadAjaxManager, para el manejo de Ajax durante la página.
El funcionamiento será el siguiente: Se seleccionará las fechas que necesitamos como parámetros para el filtro de nuestro Procedimiento de SQL y luego lo ejecutamos en el botón Buscar, el cual llenará la grilla y esta tendrá las opciones de selección mediante CheckBox, vendrá paginada y podrá hacer una selección y deselección total con un checkbox en la cabecera de la misma.
Así mismo recuperaremos los items seleccionados mediante el botón Guardar y los agregaremos a una grilla de resultados en la parte inferior.
6. Ahora veremos como es el funcionamiento desde el lado del servidor (VB.NET):
6.1 La conexión a la BD
Private db As Database = DatabaseFactory.CreateDatabase("NORTHWND")
6.2 El DataSource del RadGrid multiselect debe registrarse se la siguiente manera:
Public Property DtVentas() As DataTable Get Return Session("DtVentas") End Get Set(ByVal value As DataTable) Session("DtVentas") = value End Set End Property
Fíjese que mantenemos la información en sesión para evitar consultar la BD, para que no se pierda al hacer un PostBack.
6.3 La función de búsqueda
Public Function ObtenerVentas(ByVal finicio As Date, ByVal ffinal As Date) As DataTable Dim cmd As New SqlCommand("EmployeeSalesByCountry") cmd.CommandType = CommandType.StoredProcedure db.AddInParameter(cmd, "Beginning_Date", DbType.DateTime, finicio) db.AddInParameter(cmd, "Ending_Date", DbType.DateTime, ffinal) Return db.ExecuteDataSet(cmd).Tables(0) End Function
6.4 El Botón Buscar: Ejecutará la búsqueda y registrará el resultado en la propiedad DtVentas
Private Sub BtnBuscar_Click(sender As Object, e As System.EventArgs) Handles BtnBuscar.Click DtVentas = ObtenerVentas(DpDesde.SelectedDate, DpHasta.SelectedDate) GvVentas.DataSource = DtVentas GvVentas.DataBind() End Sub
6.5 Para el CheckBox de la cabecera también es necesario crearle una propiedad.
Public Property CheckBoxAll() As Boolean Get Return Session("chkAll") End Get Set(ByVal value As Boolean) Session("chkAll") = value End Set End Property
De igual manera lo mantenemos en sesión para que no se pierda al refrescar la página.
6.6 En los checkboxs de cabecera y los checkboxs de selección, en el XHTML se indica que ejecutarán unas funciones las cuales serán:
Protected Sub ToggleRowSelection(ByVal sender As Object, ByVal e As EventArgs) Dim chbSeleccionado As CheckBox = CType(sender, CheckBox) Dim orden As String = chbSeleccionado.ToolTip CType(chbSeleccionado.NamingContainer, GridItem).Selected = chbSeleccionado.Checked For index = 0 To DtVentas.Rows.Count - 1 If DtVentas.Rows(index)("OrderID") = orden Then DtVentas.Rows(index)("seleccionado") = chbSeleccionado.Checked Exit For End If Next End Sub Protected Sub ToggleSelectedStateDisponibles(ByVal sender As Object, ByVal e As EventArgs) Dim chbAll As CheckBox = CType(sender, CheckBox) CheckBoxAll = chbAll.Checked For index = 0 To DtVentas.Rows.Count - 1 DtVentas.Rows(index)("seleccionado") = CheckBoxAll Next For Each dataItem As GridDataItem In GvVentas.MasterTableView.Items CType(dataItem.FindControl("chbSeleccionado"), CheckBox).Checked = CheckBoxAll dataItem.Selected = CheckBoxAll Next End Sub
Cambiamos el valor no sólo en los checkboxs sino también en el DataSource, dado que los valores no están amarrados.
6.7 La Grilla:
Private Sub GvVentas_ItemDataBound(sender As Object, e As Telerik.Web.UI.GridItemEventArgs) Handles GvVentas.ItemDataBound If TypeOf e.Item Is GridEditableItem Then Dim item As GridItem = e.Item Try Dim seleccionado As Boolean = Convert.ToBoolean(DirectCast(item.DataItem, DataRowView).Item("seleccionado")) Dim codigo As String = Convert.ToString(DirectCast(item.DataItem, DataRowView).Item("OrderID")) Dim chkSeleccionado As CheckBox = DirectCast(item.FindControl("chbSeleccionado"), CheckBox) chkSeleccionado.Checked = seleccionado chkSeleccionado.ToolTip = codigo Catch ex As Exception End Try ElseIf TypeOf e.Item Is GridHeaderItem Then Dim item As GridHeaderItem = e.Item Dim chkAll As CheckBox = DirectCast(item.FindControl("chbAll"), CheckBox) chkAll.Checked = CheckBoxAll End If End Sub Private Sub GvVentas_PageIndexChanged(sender As Object, e As Telerik.Web.UI.GridPageChangedEventArgs) Handles GvVentas.PageIndexChanged GvVentas.DataSource = DtVentas GvVentas.DataBind() End Sub
Para los que desconocen los eventos, son
ItemDataBound: Se ejecuta cada vez que se carga la grilla y recorre todas las filas (incluyendo la cabecera y el footer). Nos sirve para asignar el valor del RadioButton oculto (con el valor seleccionado) al CheckBox que está en un ItemTemplate.
PageIndexChanged: Se ejecuta cuando cambias de página dentro de la grilla, esto ejecuta un render y por el Ajax se pierde el DataSource, por eso se debe guardar el valor obtenido en memoria.
6.8 El Botón Guardar:
Private Sub BtnGuardar_Click(sender As Object, e As System.EventArgs) Handles BtnGuardar.Click Dim dt As New DataTable dt.Columns.Add("Country") dt.Columns.Add("LastName") dt.Columns.Add("FirstName") dt.Columns.Add("ShippedDate") dt.Columns.Add("OrderID") dt.Columns.Add("SaleAmount") For Each i As GridDataItem In GvVentas.SelectedItems dt.Rows.Add() Dim index = dt.Rows.Count - 1 dt.Rows(index)("Country") = i.GetDataKeyValue("Country").ToString() dt.Rows(index)("LastName") = i.GetDataKeyValue("LastName").ToString() dt.Rows(index)("FirstName") = i.GetDataKeyValue("FirstName").ToString() dt.Rows(index)("ShippedDate") = Date.Parse(i.GetDataKeyValue("ShippedDate")) dt.Rows(index)("OrderID") = i.GetDataKeyValue("OrderID").ToString() dt.Rows(index)("SaleAmount") = Decimal.Parse(i.GetDataKeyValue("SaleAmount")) Next GvSeleccionados.DataSource = dt GvSeleccionados.DataBind() End Sub
Nos permitirá pasar de los seleccionados sus valores a la grilla de resultados.
La manera más fácil de obtener valores de respuesta es declararlos en el MasterTableView como DataKeyNames (Ejem: i.GetDataKeyValue(«LastName»)).
El código completo sería:
Imports Microsoft.Practices.EnterpriseLibrary.Data Imports System.Data.SqlClient Imports Telerik.Web.UI Public Class RadGridExample Inherits System.Web.UI.Page Private db As Database = DatabaseFactory.CreateDatabase("NORTHWND") #Region "Propiedades" Public Property DtVentas() As DataTable Get Return Session("DtVentas") End Get Set(ByVal value As DataTable) Session("DtVentas") = value End Set End Property Public Property CheckBoxAll() As Boolean Get Return Session("chkAll") End Get Set(ByVal value As Boolean) Session("chkAll") = value End Set End Property #End Region #Region "Eventos Página" Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not Page.IsPostBack Then GvVentas.DataBind() GvSeleccionados.DataBind() End If End Sub #End Region #Region "Eventos Botón" Private Sub BtnBuscar_Click(sender As Object, e As System.EventArgs) Handles BtnBuscar.Click DtVentas = ObtenerVentas(DpDesde.SelectedDate, DpHasta.SelectedDate) GvVentas.DataSource = DtVentas GvVentas.DataBind() End Sub Private Sub BtnGuardar_Click(sender As Object, e As System.EventArgs) Handles BtnGuardar.Click Dim dt As New DataTable dt.Columns.Add("Country") dt.Columns.Add("LastName") dt.Columns.Add("FirstName") dt.Columns.Add("ShippedDate") dt.Columns.Add("OrderID") dt.Columns.Add("SaleAmount") For Each i As GridDataItem In GvVentas.SelectedItems dt.Rows.Add() Dim index = dt.Rows.Count - 1 dt.Rows(index)("Country") = i.GetDataKeyValue("Country").ToString() dt.Rows(index)("LastName") = i.GetDataKeyValue("LastName").ToString() dt.Rows(index)("FirstName") = i.GetDataKeyValue("FirstName").ToString() dt.Rows(index)("ShippedDate") = Date.Parse(i.GetDataKeyValue("ShippedDate")) dt.Rows(index)("OrderID") = i.GetDataKeyValue("OrderID").ToString() dt.Rows(index)("SaleAmount") = Decimal.Parse(i.GetDataKeyValue("SaleAmount")) Next GvSeleccionados.DataSource = dt GvSeleccionados.DataBind() End Sub #End Region #Region "Eventos GridView" Private Sub GvVentas_ItemDataBound(sender As Object, e As Telerik.Web.UI.GridItemEventArgs) Handles GvVentas.ItemDataBound If TypeOf e.Item Is GridEditableItem Then Dim item As GridItem = e.Item Try Dim seleccionado As Boolean = Convert.ToBoolean(DirectCast(item.DataItem, DataRowView).Item("seleccionado")) Dim codigo As String = Convert.ToString(DirectCast(item.DataItem, DataRowView).Item("OrderID")) Dim chkSeleccionado As CheckBox = DirectCast(item.FindControl("chbSeleccionado"), CheckBox) chkSeleccionado.Checked = seleccionado chkSeleccionado.ToolTip = codigo Catch ex As Exception 'rbtPredeterminado.Checked = False End Try ElseIf TypeOf e.Item Is GridHeaderItem Then Dim item As GridHeaderItem = e.Item Dim chkAll As CheckBox = DirectCast(item.FindControl("chbAll"), CheckBox) chkAll.Checked = CheckBoxAll End If End Sub Private Sub GvVentas_PageIndexChanged(sender As Object, e As Telerik.Web.UI.GridPageChangedEventArgs) Handles GvVentas.PageIndexChanged GvVentas.DataSource = DtVentas GvVentas.DataBind() End Sub #End Region #Region "Funciones" Public Function ObtenerVentas(ByVal finicio As Date, ByVal ffinal As Date) As DataTable Dim cmd As New SqlCommand("EmployeeSalesByCountry") cmd.CommandType = CommandType.StoredProcedure db.AddInParameter(cmd, "Beginning_Date", DbType.DateTime, finicio) db.AddInParameter(cmd, "Ending_Date", DbType.DateTime, ffinal) Return db.ExecuteDataSet(cmd).Tables(0) End Function Protected Sub ToggleRowSelection(ByVal sender As Object, ByVal e As EventArgs) Dim chbSeleccionado As CheckBox = CType(sender, CheckBox) Dim orden As String = chbSeleccionado.ToolTip CType(chbSeleccionado.NamingContainer, GridItem).Selected = chbSeleccionado.Checked For index = 0 To DtVentas.Rows.Count - 1 If DtVentas.Rows(index)("OrderID") = orden Then DtVentas.Rows(index)("seleccionado") = chbSeleccionado.Checked Exit For End If Next End Sub Protected Sub ToggleSelectedStateDisponibles(ByVal sender As Object, ByVal e As EventArgs) Dim chbAll As CheckBox = CType(sender, CheckBox) CheckBoxAll = chbAll.Checked For index = 0 To DtVentas.Rows.Count - 1 DtVentas.Rows(index)("seleccionado") = CheckBoxAll Next For Each dataItem As GridDataItem In GvVentas.MasterTableView.Items CType(dataItem.FindControl("chbSeleccionado"), CheckBox).Checked = CheckBoxAll dataItem.Selected = CheckBoxAll Next End Sub #End Region End Class
Al final tendremos una secuencia como lo siguiente:
Se agradece cualquier mejora del código, espero les sea de utilidad.