jueves, 23 de junio de 2011

[GridView] Eventos de controles contenidos en el GridView (1/2)

 

Introducción

En ciertas ocasiones puede ser necesario trabajar directamente con los eventos de los controles contenidos en el propio GridView, pero el problema planteado es como trabajar con el control que ejecuta la acción y al mismo tiempo poder acceder a la información de la fila del gridview que contiene a dicho control.

El articulo demuestra como conseguirlo de dos formas distintas:

- una implicara extender el control que se quiera utilizar

- la otra simplemente accederá al objeto que contiene al control que lanza el evento

En los ejemplos se hará uso de un control radiobutton como parte de gridview y será este quien ejecute el evento con el cual se necesitara trabajar.

 

Extendiendo el control

Para poder implementar este camino se necesitara definir una propiedad que será útil cuando se trabaje con el evento del control.

Esta propiedad no se dispone de forma estándar en el control, sino que deberá crearse un control que extienda del original.

public class CustomRadioButtonList : RadioButtonList 
    {
        //
        // La creacion del custom control es justamente para agregar esta propiedad 
        // que por defecto no posee el RadioButtonList
        //
        [DefaultValue("")]
        public string CommandArgument
        {
            get
            {
                string s = ViewState["CommandArgument"] as string;
                return s == null ? String.Empty : s;
            }
            set
            {
                ViewState["CommandArgument"] = value;
            }
        }

    }

Es importante mencionar que el uso de este nuevo control requiere de su declaración en la pagina

<%@ Register Assembly="GridViewEventosControles" Namespace="GridViewEventosControles"
    TagPrefix="cc1" %>

El siguiente paso sea definir del control que se ha creado dentro de la pagina.

<asp:TemplateField HeaderText="Respuesta">

    <ItemTemplate>
        <cc1:CustomRadioButtonList ID="rblRespuesta" runat="server" 
            CommandArgument='<%#Container.DataItemIndex %>' 
            onselectedindexchanged="rblRespuesta_SelectedIndexChanged" AutoPostBack="True">
        </cc1:CustomRadioButtonList>
    </ItemTemplate>

</asp:TemplateField>

La línea donde se usa el Container.DataItemIndex permite asigna el índice de cada row a la propiedad, para que pueda ser tomada desde el código al ejecutarse el evento.

protected void rblRespuesta_SelectedIndexChanged(object sender, EventArgs e)
{
    CustomRadioButtonList radiolist = sender as CustomRadioButtonList;

    if (radiolist == null)
        return;

    //
    // Se toma el index de la row del gridview, el cual fue asociado en el CommandArgument
    //
    int rowindex = Convert.ToInt32(radiolist.CommandArgument);
    
    //
    // Sabiendo el index de la row con que se debe trabajar se pued recuperat el id
    //
    int idpregunta = Convert.ToInt32(GridView1.DataKeys[rowindex].Value);

    bool seleccion = Convert.ToBoolean(Convert.ToInt32(radiolist.SelectedValue));

    PreguntasManager.Update(idpregunta, seleccion);

}

[C#]
[C# Skydrive]

 

Accediendo al objeto contenedor

Para esta implementación no se requiere el uso de ningún control adiciona, simplemente se hará uso de una propiedad que permite recuperar el contenedor del control.

Específicamente se hace referencia a NamingContainer

La definición del control en el grid será de forma normal

<asp:TemplateField HeaderText="Respuesta">

    <ItemTemplate>
        <asp:RadioButtonList ID="rblRespuesta" runat="server" 
            onselectedindexchanged="rblRespuesta_SelectedIndexChanged" AutoPostBack="True">
        </asp:RadioButtonList>
    </ItemTemplate>

</asp:TemplateField>

Pero el acceso al row que contiene el control es donde esta el truco.

protected void rblRespuesta_SelectedIndexChanged(object sender, EventArgs e)
{
    RadioButtonList radiolist = sender as RadioButtonList;

    if (radiolist == null)
        return;

    //
    // Se recupera la row del GridView que contien el control
    //
    GridViewRow row = radiolist.NamingContainer as GridViewRow;
    
    //
    // Sabiendo el index de la row con que se debe trabajar se puede recuperar el id
    //
    int idpregunta = Convert.ToInt32(GridView1.DataKeys[row.RowIndex].Value);

    bool seleccion = Convert.ToBoolean(Convert.ToInt32(radiolist.SelectedValue));

    PreguntasManager.Update(idpregunta, seleccion);

}

Es allí donde se usa la propiedad para obtener el row donde esta contenido el control que lanzo el evento, y el el index será utilizado para obtener el identificador de la entidad, en este caso de la pregunta a la cual se da la respuesta.

 

[C#]
[C# SkyDrive]

9 comentarios:

  1. Buenas Leandro.
    He estado revisando tu ejemplo y me han quedado una duda :
    ¿Por qué en rblRespuesta_SelectedIndexChanged
    cuando recuperas el RadioButtonList personalizado si lo conviertes en CustomRadioButtonList , pero cuando lo recuperas en GridView1_RowDataBound no??
    Me podrias explicar a que se debe ese detalle??

    Gracias de antemano :D

    ResponderEliminar
  2. hola Antonio

    Recuerda que el control que estamos extendiendo hereda de RadioButtonList, por lo tanto puedes castear a la base si es que no necesitas la funcionalidad de la especializacion

    En el SelectedIndexChanged se necesitaba de CommandArgument que solo lo tiene la version custom
    Mientras que en el RowDataBound no se requiere por eso se usa el control base


    saludos

    ResponderEliminar
  3. OK entiendo, solo una consulta mas , me podrías indicar un caso en el me que podría necesitar trabajar directamente con los eventos de los controles contenidos en el GridView, solo para tenerlo presente cuando me tope con esos escenarios :$

    Gracias nuevamente

    ResponderEliminar
  4. hola

    bueno un caso concreto podrias ser si necesitas anidar el comportamiento de controles en la row del grid

    o sea, quizas teniendo dos combos y al seleccionar un item en uno cargue el segundo, por supuesto ambos combos en la misma row

    saludos

    ResponderEliminar
  5. Estoy tratando de realizar un ejemplo de dropdownlist anidados dentro de un grid en asp.net, he revisado su ejemplo, tengo dos controles uno de provincias y el otro de Cantones y al cambiar el valor de Provincias se deberia actualizar los cantones pero no sucede Ayuda
    Mi codigo es el siguiente
    DropDownList ddlProvincia = sender as DropDownList;
    GridView row = ddlProvincia.NamingContainer as GridView;
    string codProv = ddlProvincia.SelectedValue;
    if (codProv != null)
    {
    DropDownList ddlCanton = sender as DropDownList;
    GridView row1 = ddlCanton.NamingContainer as GridView;
    GenCantonDAL genCantonDAL = new GenCantonDAL();
    DataTable data = new DataTable();
    data = genCantonDAL.Canton(codProv);
    ddlCanton.DataSource = data;
    ddlCanton.DataTextField = "descripc";
    ddlCanton.DataValueField = "codicant";
    ddlCanton.DataBind();
    }

    ResponderEliminar
  6. hola Elizabeth

    asignaste la propiedad AutopostBack = true en el dropdownlist

    porque si esa propiedad esta en false los eventos del combo no se generan

    saludos

    ResponderEliminar
  7. Gracias Leandro,
    me sirvió muchisimo!!!

    ResponderEliminar
  8. Leandro como siempre muy agradecido con tu aporte, me ha servido de maravilla.

    ResponderEliminar
  9. Hola Leandro. Yo quiero anidar dos DropDownList en distintas filas del gridview, o sea que si cambio el valor de alguno los otros DropDownList se igualen. Mi código es el siguiente:


    Límites y Opciones


    Seleccione...
    10% VC/Tope UF40
    10% VC/Tope UF50
    10% VC/Tope UF80




    Y mi método para el evento es el siguiente:

    protected void ddlLimitesOpciones_SelectedIndexChanged(object sender, EventArgs e)
    {
    DropDownList ddl = sender as DropDownList;
    string valor = ddl.SelectedItem.Text;
    foreach (GridViewRow row in gv_CobHorizontal.Rows)
    {
    if (((DropDownList)row.Cells[4].FindControl("ddlLimitesOpciones")).Visible == true)
    {
    //((DropDownList)row.Cells[4].FindControl("ddlLimitesOpciones")).SelectedValue = valor;
    foreach (ListItem item in ((DropDownList)row.Cells[4].FindControl("ddlLimitesOpciones")).Items)
    {
    if (item.Text == valor)
    {
    item.Selected = true;
    }
    }
    }
    }
    }



    Pero los valores no me cambian y no sé por qué, no veo el error.

    ResponderEliminar