jueves, 29 de abril de 2010

[jQuery] DropDownList anidados (nested DropDownList)

 

Introducción


La finalidad del articulo es mostrar como utilizando puramente jquery se puede vincular dos dropdownlist de forma tal que al cambiar la selección de uno de ellos se recargue el contenido del siguiente.

En el articulo se hará uso de una invocación a un Page Method, para exponer la información y poder recuperar de forma dinámica.

 

Inicialización de las listas


En el evento Page_Load se proceder a la carga inicial de los combos, esto será útil para que el usuario visualice una selección por defecto

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        ddlPaises.DataSource = GetPaises();
        ddlPaises.DataTextField = "Pais";
        ddlPaises.DataValueField = "Cod";
        ddlPaises.DataBind();

        ddlCiudades.DataSource = GetCiudadesByPais(Convert.ToInt32(ddlPaises.SelectedValue));
        ddlCiudades.DataTextField = "descripcion";
        ddlCiudades.DataValueField = "cod";
        ddlCiudades.DataBind();
    }

    
}


[WebMethod]
public static List<CiudadEntity> GetCiudadesByPais(int pais)
{
    var query = from item in GetCiudades().AsEnumerable()
                where Convert.ToInt32(item["CodPais"]) == pais
                select new CiudadEntity
                { 
                  cod = Convert.ToInt32(item["Cod"]),
                  descripcion = Convert.ToString(item["Ciudad"])
                };

    return query.ToList<CiudadEntity>();

}

Es importante remarcar como uno de los métodos tiene definido el atributo [WebMethod] y además se ha definido como static, este método será clave en la invocación desde el cliente con jQuery.

 

Cambio de selección desde el cliente, con jQuery


La definición del html es muy simple, solo son 2 DropDownList.

<form id="form1" runat="server">
<table>
    <tr>
        <td>Paises:</td>
        <td>
            <asp:DropDownList ID="ddlPaises" runat="server">
            </asp:DropDownList>
        </td>
    </tr>
     <tr><td colspan="2"></td></tr>
    <tr>
        <td>Ciudades:</td>
        <td>
            <asp:DropDownList ID="ddlCiudades" runat="server">
            </asp:DropDownList>
        </td>
    </tr>
</table>

</form>

La definición de script seria la siguiente:

<script language="javascript" type="text/javascript">

    $().ready(function() {

        $("#<%=ddlPaises.ClientID%>").change(function() {

            // armo el objeto que servira de parametro, para ello utilizo una libreria de JSON
            //este parametro mapeara con el definido en el web service
            var params = new Object();
            params.pais = $("#<%=ddlPaises.ClientID%>").val();
            params = JSON.stringify(params);

            $.ajax({
                type: "POST",
                url: "Default.aspx/GetCiudadesByPais",
                data: params,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: false,
                success: LoadCiudades,
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(textStatus + ": " + XMLHttpRequest.responseText);
                }
            });

        });

        $("#<%=ddlCiudades.ClientID%>").change(function() {
            alert("Ha seleccionado: " + $("#<%=ddlCiudades.ClientID%> :selected").text());
        });

    });

    function LoadCiudades(result) {
        //quito los options que pudiera tener previamente el combo
        $("#<%=ddlCiudades.ClientID%>").html("");

        //recorro cada item que devuelve el servicio web y lo añado como un opcion
        $.each(result.d, function() {
            $("#<%=ddlCiudades.ClientID%>").append($("<option></option>").attr("value", this.cod).text(this.descripcion))
        });
    }
</script>

En el $().ready() se asocian a los combos dos eventos para poder atrapar un cambio de selección, es por eso que se usa el change().

Ante el cambio de selección de un país se procede en primer instancia al armado de parámetro que será enviado como filtro el Web Method, el cual se ha visto en el código anterior.

Para esta operación se hará uso de una librería de nombre json2.js, esta permitirá armar con objetos de javascript la información de una forma simple, y mediante el método JSON.stringify() convertir esta en json para ser enviada como parámetro.

En la operación también se toma el valor seleccionado en el combo usando:

$("#<%=ddlPaises.ClientID%>").val()

también se podría haber usado:

$(this).val()

ya que quien lanza el evento es el propio combo de países.

Es importante remarcar en este punto como se seleccionan los controles de asp.net usando en el selector de jquery, como asp.net renombran el atributo id de los controles es necesario usar el ClientID para tener el nombre correcto.

El siguiente paso es la invocación al método del servicio, para lo cual se utiliza el $.ajax(), hay algunas propiedades importantes que deben tenerse en cuenta, una de ellas es la definición de la url a invoca:

url: "Default.aspx/GetCiudadesByPais"

como se vera define directamente la propia pagina web donde se encuentra y luego el nombre del Web Method, pero podría ponerse los método del servicio en otro pagina si se requiere.

En el parámetro

data: params,

se define la variable del paso previo, en esta estará contenida el json que será enviado el web method.

También es importante definir si la invocación será asíncrona o no

async: true

en este caso no se espera a que el servicio web responda para continuar. Aquí no afecta si es es asíncrona o no la invocación ya que no hay código que continúe en la función javascript, pero puede que sea necesario a veces esperar una repuesta para poder tomar una decisión.

También se definen eventos en caso de una ejecución satisfactoria o un error

success: LoadCiudades,

hay varias formas de hacer esto, aquí se decidió usar una función nueva para procesar el contenido resultante de la invocación. Este método recibirá por parámetro de nombre “result” en donde se alojara el json de resultado.

 

Carga del combo de Ciudades


Es interesante analizar la función responsable de la carga de las ciudades proveniente de la invocación

function LoadCiudades(result) {
    //quito los options que pudiera tener previamente el combo
    $("#<%=ddlCiudades.ClientID%>").html("");

    //recorro cada item que devuelve el servicio web y lo añado como un opcion
    $.each(result.d, function() {
        $("#<%=ddlCiudades.ClientID%>").append($("<option></option>").attr("value", this.cod).text(this.descripcion))
    });
}

Esta recibe el resultado, y la primer operación será limpiar el combo, para ello es que lo selecciona y quita el html dentro de este, o sea quita los tag <option> que posea.

Luego toma cada valor devuelto en el json y lo recorre:

$.each(result.d, function() {..}

algo que seguro notaran extraño es el uso de result.d, este “d” es una propiedad del objeto result y allí contiene la información devuelta en la invocación, en este caso la lista de ciudades.

Se se pone un breakpoint en el código javascript, se podrá analizar claramente el valor devuelto en el json

Por supuesto el this dentro de la función representa cada ítem en el loop, y por tratarse de json este esta tipado con los mismas propiedades definida en la clase CiudadEntity devuelta por el web method.

 

 

[C#]
[VB.NET]

jueves, 22 de abril de 2010

C# - [DataGridView] – Pasar Registros entre Grillas

 

Introducción


El articulo explica de que forma se puede seleccionar registros en el control DataGridView y pasarlos a otro, representando este la selección de las filas por el usuario.

En uno de los DataGridView se listaran productos, y por medio de un checkbox en cada fila, se podrá seleccionar que ítems se desean seleccionar.

Botones que indican la dirección serán los encargados de ejecutar la opción de pasaje.

El diseño de la interfaz puede apreciarse en esta imagen:

form1

 

Carga de la lista de productos


Para esta operación se requerirá la ayuda del DataSet tipado el cual define la estructura de la entidad, pero será responsabilidad de la clase con sufijo DAL, la que cargara la información, proveniente de la base de datos.

private void frmPedidos_Load(object sender, EventArgs e)
{
    //
    // Se recupera los datos de los productos desde la tabla
    //
    dtoProductos datos = ProductoDAL.ProductosGetAll();

    //
    // Se bindean los datos a la grilla
    //
    dgvProductos.AutoGenerateColumns = false;
    dgvProductos.DataSource = datos;
    dgvProductos.DataMember = "Productos";

}

Selección de un ítem en la grilla de origen


Hay que destacar que la operación sobre un DataGridView difiere si esta se encuentra previamente enlazada a datos, o no.

En esta primera operación implica la sección de un producto que pasara a una lista no enlazada a datos, por lo tanto se podrá hacer uso de método Add() de la colección de Rows de la grilla.

private void btnSeleccionar_Click(object sender, EventArgs e)
{
    //
    // Se define una lista temporal de registro seleccionados
    //
    List<DataGridViewRow> rowSelected = new List<DataGridViewRow>();

    //
    // Se recorre ca registro de la grilla de origen
    //
    foreach (DataGridViewRow row in dgvProductos.Rows)
    {
        //
        // Se recupera el campo que representa el checkbox, y se valida la seleccion
        // agregandola a la lista temporal
        //
        DataGridViewCheckBoxCell cellSelecion = row.Cells["Seleccion"] as DataGridViewCheckBoxCell;

        if (Convert.ToBoolean(cellSelecion.Value))
        {
            rowSelected.Add(row);
        }
    }

    //
    // Se agrega el item seleccionado a la grilla de destino
    // eliminando la fila de la grilla original
    //
    foreach (DataGridViewRow row in rowSelected)
    {
        dgvSeleccion.Rows.Add(new object[] {false, 
                                            row.Cells["Descripcion"].Value,
                                            row.Cells["PrecioUnitario"].Value,
                                            row.Cells["UnidadMedida"].Value});

        dgvProductos.Rows.Remove(row);
    }

}

Hay que remarcar también que el uso de la lista temporal no es un capricho, sino que es necesaria ya que mientras se recorre una lista por medio de la instrucción “for each”, no pueden removerse elementos. Por esta razón es que en una operación siguiente se recorre la selección y allí si se procede a incluye los ítems en al grilla de destino y quitarla de la original.

Este punto lo he explicado con mas detalle siguiente articulo:

Eliminar ítems en una lista

 

Selección de un ítem en la grilla de destino


Ahora le toca el turno a la operación contraria, y aquí hay un punto que ya se menciono en el paso previo, la lista de origen esta vinculada a datos de productos con lo cual usar el Rows.Add() no es posible.

Para esta operación se agregaran los datos directo en el origen, en este caso el datatable, que se ha creado dentro del DataSet tipado.

private void btnQuitar_Click(object sender, EventArgs e)
{
    //
    // Se define una lista temporal de registro seleccionados
    //
    List<DataGridViewRow> rowSelected = new List<DataGridViewRow>();

    //
    // Se recorre cada fila de la grilla de seleccion y se determian que registros estan checkeados
    //
    foreach (DataGridViewRow row in dgvSeleccion.Rows)
    {
        DataGridViewCheckBoxCell cellSelecion = row.Cells["SeleccionSel"] as DataGridViewCheckBoxCell;

        if (Convert.ToBoolean(cellSelecion.Value))
        {
            rowSelected.Add(row);
        }
    }

    //
    // Se valida si hay algun registro por eliminar
    //
    if (rowSelected.Count > 0)
    {
        //
        // Se recupera el origen de datos que tiene asignada la grilla de productos
        //
        dtoProductos datos = dgvProductos.DataSource as dtoProductos;

        //
        // Se recorre cada item seleccionado y se arma programaticamente la fila del DataTable
        // se elimina el registro de la grilla de selecciones
        //
        foreach (DataGridViewRow row in rowSelected)
        {

            dtoProductos.ProductosRow productoRow = datos.Productos.NewProductosRow();

            productoRow.Descripcion = Convert.ToString(row.Cells["DescripcionSel"].Value);
            productoRow.PrecioUnitario = Convert.ToDecimal(row.Cells["PrecioUnitarioSel"].Value);
            productoRow.UnidadMedida = Convert.ToString(row.Cells["UnidadMedidaSel"].Value);
            datos.Productos.Rows.Add(productoRow);

            dgvSeleccion.Rows.Remove(row);
        }

        //
        // Se binden los datos nuevamente, pero ahora con los nuevos registros
        // agregados del paso anterior
        //
        dgvProductos.AutoGenerateColumns = false;
        dgvProductos.DataSource = datos;
        dgvProductos.DataMember = "Productos";
    }
}

Es necesario analizar como toma los datos originales convirtiendo el contenido de DataSource de la grilla, en este caso a dtoProductos, ya que este es el tipo de datos utilizado en al carga original.

Se toman los datos originales, se procede a la creación de cada row de forma programática, asignando el valor a cada campo, por ultimo se vuelve a bindear la grilla.

 

[C#]
[VB.NET]

domingo, 18 de abril de 2010

C# – Descripción del Mes

 

Introducción

En esta ocasión se describirá las distintas técnicas que pueden aplicarse para obtener la descripción del mes.

Muchas veces sucede que dado una fecha, es necesario mostrar al usuario que mes se ha esta seleccionado, pero no alcanza con mostrar el numero que lo representa, sino que es necesario visualizar la descripción del mismo, y en el idioma del usuario.

Existen varias técnicas y formas de lograrlo, en este articulo se intentara abordad las alternativas que utilizo.

Obtener mes en una cultura especifica, usando método de extensión

El ejemplo de este punto se podrá visualizar en el código del Form1.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
    {
        textBox1.Text = dateTimePicker1.Value.Month.MonthName().CapitalizeFirstLetter();
    }


}

public static class DateTimeHelper
{

    public static string MonthName(this int month)
    {
        DateTimeFormatInfo dtinfo = new CultureInfo("es-ES", false).DateTimeFormat;
        return dtinfo.GetMonthName(month);
    }

    public static string CapitalizeFirstLetter(this string value)
    {
        return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value);
    }
}

En el código se puede observar la declaración de una clase estática, la cual contiene dos métodos:

- MonthName(), define un parámetro del tipo entero el cual representa el numero del mes, del cual se quiere obtener la descripción.

- CapitalizeFirstLetter(), en realidad este es solo un agregado, el cual hará que la descripción del mes tenga la primer letra en mayúsculas. Si la cultura usada es Ingles, esta de por si ya trae la primer letra en mayúscula, pero para idiomas Español, no es así, por eso hay que agrégasela.

En el evento ValueChange del DateTimePicker, se toma la fecha seleccionada y se recupera solo el mes en formato numéricos pero al definir un método de extensión para los enteros al inspeccionar los métodos habilitados estará el que hemos creado, lo interesante por remarcar es que los métodos de extensión permiten invocarlos directamente sin tener que pasar un valor por parámetro.

El único inconveniente de esta implementación es que la cultura, (o se el idioma) en el cual se recupera la descripción del mes, esta fija en el método.

Obtener mes en una cultura especifica, usando método simple

En el Form2 se podrá ver la misma implementación del punto anterior, pero en esta oportunidad se hace uso de método simples.

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
    {
        textBox1.Text = CapitalizeFirstLetter(MonthName(dateTimePicker1.Value.Month));
    }


    public string MonthName(int month)
    {
        DateTimeFormatInfo dtinfo = new CultureInfo("es-ES", false).DateTimeFormat;
        return dtinfo.GetMonthName(month);
    }

    public string CapitalizeFirstLetter(string value)
    {
        return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value);
    }

}

En este caso cada método recibe por parámetro el valor que necesita para poder trabajar.

Obtener mes seleccionando la cultura y usando método de extensión

En esta otra implementación iremos un poco mas allá, al permitir que el idioma del mes se obtenga en base a la cultura seleccionada, o la que este definida en la aplicación.

El ejemplo se dividió en dos formulario: Form3 y Form4, ya que era preciso que en la inicialización se pudiera cambiar el Thread.CurrentThread.CurrentCulture, lo cual no se podía lograr si estamos situados en el mismo form.

public partial class Form4 : Form
{
    public Form4()
    {
        InitializeComponent();
    }

    public Form4(CultureInfo culture) : this()
    {
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }

    private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
    {
        textBox1.Text = MonthNameCurrentCulture(dateTimePicker1.Value.Month);
        
    }


    public string MonthNameCurrentCulture(int month)
    {
        DateTimeFormatInfo dtinfo = Thread.CurrentThread.CurrentCulture.DateTimeFormat;
        return dtinfo.GetMonthName(month);
    }
}

En el Form4 se recibe la cultura por parámetro, ya que es preciso que sea en la inicialización del form donde se especifique, es por ello que se ha creado un constructor nuevo en el Form4.

Lo destacable es que ahora el método MonthNameCurrentCulture() usa la cultura de la aplicación para obtener la descripción del mes, ya no esta fija como en los ejemplos anteriores.

Obtener mes mediante el formato de la fecha

Una otra alternativa valida, se podría usar el formato que se puede aplicar a un tipo DateTime, cuando se lo convierte a string.

En el Form6 justamente se hace uso de esta técnica, aplicando el formato MMMM, el cual devuelve la descripción del mes, para la fecha seleccionada.

public partial class Form5 : Form
{
    public Form5()
    {
        InitializeComponent();
    }

    private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
    {
        if(rbespanol.Checked)
            textBox1.Text = dateTimePicker1.Value.ToString("MMMM", CultureInfo.CreateSpecificCulture("es-ES")); 
        else if(rbingles.Checked)
            textBox1.Text = dateTimePicker1.Value.ToString("MMMM", CultureInfo.CreateSpecificCulture("en-US")); 
    }

}

Aclaraciones generales

Hay que recordar el agregado del using de System.Globalization, para que reconozca los métodos.

 

[C#]
[VB.NET]

jueves, 15 de abril de 2010

C# - [DataGridView] – Uso del CheckBox - DataGridViewCheckBoxColumn

 

Introducción

El articulo intentara mostrar las algunas forma en que se puede utilizar el checkbox en un control DataGridView.

Detectar el cambio en la selección

Para realizar la tarea se hará uso de dos eventos que pueden ser igual de útiles, el CellValueChange y el CellContentClick

De forma estándar el CellValueChange, se deparará a seleccionar el check de la celda y quitar el foco de la misma, saliendo del modo de edición.

Pero este no ejecuta la acción en el mismo instante en que se origina, sino que hay que salir de la edición de la celda para que el evento ocurra.

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
	//
	// Solo se trabaja ante los cambios en la columan de los checkbox 
	//
	if (dataGridView1.Columns[e.ColumnIndex].Name == "Seleccion") {
		//
		// Se toma la fila seleccionada
		//
		DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

		//
		// Se selecciona la celda del checkbox
		//
		DataGridViewCheckBoxCell cellSelecion = row.Cells["Seleccion"] as DataGridViewCheckBoxCell;
              
		//
		// Se valida si esta checkeada
		//
		if (Convert.ToBoolean(cellSelecion.Value)) {

                  string mensaje = string.Format("Evento CellValueChanged.\n\nSe ha seccionado, \nDescripcion: '{0}', \nPrecio Unitario: '{1}', \nMedida: '{2}'", 
                                                      row.Cells["Descripcion"].Value, 
                                                      row.Cells["PrecioUnitario"].Value, 
                                                      row.Cells["UnidadMedida"].Value);

			MessageBox.Show(mensaje, "", MessageBoxButtons.OK, MessageBoxIcon.Information);

		}

	}

}

Con respecto al CellContentClick, que si ejecuta la acción en el mismo momento en que el usuario marca, o desmarca, el checkbox, pero me encontré que solo devolvía null en la propiedad Value de la celda.

private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    //
    // Detecta si se ha seleccionado el header de la grilla
    //
    if (e.RowIndex == -1)
        return;

    if (dataGridView1.Columns[e.ColumnIndex].Name == "Seleccion")
    {

        //
        // Se toma la fila seleccionada
        //
        DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

        //
        // Se selecciona la celda del checkbox
        //
        DataGridViewCheckBoxCell cellSelecion = row.Cells["Seleccion"] as DataGridViewCheckBoxCell;


        if (Convert.ToBoolean(cellSelecion.Value))
        {

            string mensaje = string.Format("Evento CellContentClick.\n\nSe ha seccionado, \nDescripcion: '{0}', \nPrecio Unitario: '{1}', \nMedida: '{2}'",
                                                row.Cells["Descripcion"].Value,
                                                row.Cells["PrecioUnitario"].Value,
                                                row.Cells["UnidadMedida"].Value);

            MessageBox.Show(mensaje, "", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }
        else
        {
            string mensaje = string.Format("Evento CellContentClick.\n\nSe ha quitado la seleccion, \nDescripcion: '{0}', \nPrecio Unitario: '{1}', \nMedida: '{2}'",
                                                row.Cells["Descripcion"].Value,
                                                row.Cells["PrecioUnitario"].Value,
                                                row.Cells["UnidadMedida"].Value);

            MessageBox.Show(mensaje, "", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }
    }
}

También se ha intentado hacer uso del evento EditingControlShowing, el cual permitiría detectar la selección en el mismo momento que el usuario realiza la acción, pero esto no fue posible ya que este tipo de columna no provoca el evento necesario para tomar el control CheckBox y asignar el evento SelectedIndexChanged.

Ante este problema con los eventos, y notar que ninguno responde como debería, se encontró un método que resolvió todos los problemas de un solo golpe, se trata del CommitEdit

private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dataGridView1.IsCurrentCellDirty)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}

En el evento CurrentCellDirtyStateChanged, se detecta si la grilla esta con algún cambio pendiente, y en caso de estarlo se hace un Commit del mismo para reflejar el valor en los eventos que lo usaran. Esto arregla los dos problemas detectados anteriormente:

- se lanza el evento CellValueChanged, sin tener que quitar el foco de la celda

- ya no se recibe el null en el Value de la celda, en el evento CellContentClick

[C#]
[VB.NET]

Aplicar formato a la selección

Teniendo en cuenta lo contado en la sección anterior, aplicar un formato a la fila seleccionada no debería ser un misterio.

private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    //
    // Detecta si se ha seleccionado el header de la grilla
    //
    if (e.RowIndex == -1)
        return;

    if (dataGridView1.Columns[e.ColumnIndex].Name == "Seleccion")
    {

        //
        // Se toma la fila seleccionada
        //
        DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

        //
        // Se selecciona la celda del checkbox
        //
        DataGridViewCheckBoxCell cellSelecion = row.Cells["Seleccion"] as DataGridViewCheckBoxCell;

        if (Convert.ToBoolean(cellSelecion.Value))
            row.DefaultCellStyle.BackColor = Color.Green;    
        else
            row.DefaultCellStyle.BackColor = Color.White; 

    }
}

private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dataGridView1.IsCurrentCellDirty)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}

Simplemente ante la detección de la selección del usuario se cambia el color de la fila usando el DefaultCellStyle.BackColor.

[C#]
[VB.NET]

lunes, 12 de abril de 2010

C# - [ASP.NET] - JavaScript alert() y confirm() - Como usar los mensajes

 

Introducción

En este articulo analizaremos algunas alternativas utilizadas para trabajar con alertas o pedir confirmación, mediante cuadros de mensaje en un proyecto web.

El uso de javascript en un desarrollo web con asp.net puede no ser tan simple como uno imagina, pues este código es ejecutado en el cliente, mientras que código .net se encuentra en el servidor.

Esta distinción de ambientes donde es ejecutado cada lenguaje puede traer problemas al momento de integrarlo, mas que al hacer uso de controles de brindados en asp.net que son claramente diseñador para trabajar por medio de requerimiento al servidor.

Uso del alert()

El uso del alert() de javascript es muy útil para informar al usuario de un suceso en la aplicación, pero este puede ser usado antes o después del postback o acción del usuario, lo que requiere distintas técnicas en cada caso.

Para demostrar estos casos en el formulario de nombre WebForm1.aspx trataremos el uso de las alertas.

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
    <script type="text/javascript">
    
        function alerta(numero) {
            alert('Se ha presionado el boton: ' + numero);
        }
    
    </script>
    
</head>
<body>

    <form id="form1" runat="server">
        <table>
            <tr>
                <td>
                    <input id="btnMensaje1" type="button" value="Mensaje 1" onclick="alert('Se ha presionado el boton: 1');" />
                </td>
                <td>
                    <asp:Button ID="btnMensaje2" runat="server" Text="Mensaje 2" OnClientClick="alert('Se ha presionado el boton: 2');"  />
                </td>
                <td>
                    <asp:Button ID="btnMensaje3" runat="server" Text="Mensaje 3" OnClientClick="alert('Se ha presionado el boton: 3'); return false;"  />
                </td>
                <td>
                    <asp:Button ID="btnMensaje4" runat="server" Text="Mensaje 4" />
                </td>
             </tr>
             <tr>
                <td>
                    <asp:Button ID="btnMensaje5" runat="server" Text="Mensaje 5" 
                        onclick="btnMensaje5_Click" />
                </td>
                <td>
                    <asp:Button ID="btnMensaje6" runat="server" Text="Mensaje 6" 
                        onclick="btnMensaje6_Click" />
                </td>
                <td>
                    <asp:Button ID="btnMensaje7" runat="server" Text="Mensaje 7" 
                        onclick="btnMensaje7_Click" />
                </td>
                <td>
                    <asp:Button ID="btnMensaje8" runat="server" Text="Mensaje 8" 
                        onclick="btnMensaje8_Click"  />
                </td>
            </tr>
        </table>  
       
    </form>
</body>
</html>

public partial class WebForm1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        btnMensaje4.Attributes["onclick"] = "alert('Se ha presionado el boton: 4'); return false;";  
    }

    protected void btnMensaje5_Click(object sender, EventArgs e)
    {
        string script = @"<script type='text/javascript'>
                            alerta(5);
                        </script>";

        if(!Page.IsClientScriptBlockRegistered("alerta"))
            Page.RegisterStartupScript("alerta", script);   
    }

    protected void btnMensaje6_Click(object sender, EventArgs e)
    {
        string script = @"<script type='text/javascript'>
                            alerta(6);
                        </script>";

        ScriptManager.RegisterStartupScript(this, typeof(Page), "alerta", script, false) ;
    }

    protected void btnMensaje7_Click(object sender, EventArgs e)
    {
        string script = @"<script type='text/javascript'>
                            alert('Se ha presionado el boton: 7');
                        </script>";

        ScriptManager.RegisterStartupScript(this, typeof(Page), "alerta", script, false);

    }

    protected void btnMensaje8_Click(object sender, EventArgs e)
    {
        try
        {
            throw new Exception("Se ha producido un error no controlado."); 
        }
        catch (Exception ex)
        {
            string script = @"<script type='text/javascript'>
                            alert('{0}');
                        </script>";

            script = string.Format(script, ex.Message);

            ScriptManager.RegisterStartupScript(this, typeof(Page), "alerta", script, false);

        }
    }
}

El código refleja distintas alternativas para trabajar con alertas, desde algunas muy simple al otras mas complejas.

btnMensaje1: Este simplemente es un botón simple de html, se hace uso del evento onclick para desplegar el alert

btnMensaje2: Este es el primer ejemplo con un botón de asp.net, vemos como el evento onclick aquí no esta disponible, se ha reemplazado por su equivalente OnClientClick. Un detalle de este método es que luego de despegar el mensaje el botón realiza el postaback de la pagina invocando los eventos del mismo.

btnMensaje3: A diferencia del anterior este introduce un pequeño truco, el agregado del “return false;”, ante un evento javascript que devuelva false, el postback del control al servidor es impedido

btnMensaje4: Este no define el evento en el html del control, sino que es en el evento Form_Load que se asigna haciendo uso de la colección Attributes. en realidad es otra forma valida de lograr lo mismo.

Suele suceder que a veces se necesita crear script con cierto dinamismo que solo se logra con código .net, de esta forma ese código javascript creado puede asignarse a un evento en un control para ser ejecutado.

btnMensaje5: En este caso se cambia un poco el concepto del ejemplo ya que aquí se muestra le mensaje a la vuelva del postback del evento del botón. Además se esta invocando a un método predefinido en el tag <script ..> del html

Esta técnica es muy útil cuando se quiere desplegar un mensaje luego de una operación realizada en el servidor.

Este hace uso de los métodos Page.IsClientScriptBlockRegistered() y Page.RegisterStartupScript(), paro debe aclararse que estos en la documentación del MSDN esta marcados como obsoletos, ya que hay nueva funcionalidad que los reemplazan.

btnMensaje6: Justamente en la implementación de este botón es que se hace uso de la forma actualizada en que se inyecta código javascript en una pagina.

Este hace uso del método ScriptManager.RegisterStartupScript y es este el que reemplaza a los método del ejemplo del btnMensaje5

btnMensaje7: Este demuestra que no siempre es necesario invocar a una funciona javascript en la pagina para mostrar una alerta, se puede generar código directo que muestre el mensaje.

btnMensaje8: Releja una alternativa muy interesante para visualizar errores que se producen en el servidor durante el procesamiento del postback del evento del botón.

En este caso se lanza un error a propósito que será capturado por el bloque try..catch, dentro de este se aplica las misma técnica descripta para en evento anteriores, solo que el mensaje a mostrar el usuario será el error que se atrapo en el servidor.

 

Uso del confirm()

Este aplica muchas de las técnicas ya reflejadas en el uso del alert(), pero hay ciertos detalles que es bueno remarcar.

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>

    <script type="text/javascript">

        function Confirmacion() {

            var seleccion = confirm("acepta el mensaje ?");

            if (seleccion)
                alert("se acepto el mensaje");
            else
                alert("NO se acepto el mensaje");

            //usado para que no haga postback el boton de asp.net cuando 
            //no se acepte el confirm
            return seleccion;
        
        }
    
    </script>

</head>
<body>
    <form id="form1" runat="server">

        <table>
            <tr>
                <td>
                    <asp:Button ID="btnConfirmacion1" runat="server" Text="Confirmacion 1" OnClientClick="return Confirmacion();"/>
                </td>
                <td>
                    <asp:Button ID="btnConfirmacion2" runat="server" Text="Confirmacion 2"  />
                </td>
                <td>
                    <asp:Button ID="btnConfirmacion3" runat="server" Text="Confirmacion 3" 
                            OnClientClick="return confirm('hacer el postback del control ?');"   />
                </td>
            </tr>
        </table>

    </form>
</body>
</html>

 

public partial class WebForm2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        btnConfirmacion2.Attributes["onclick"] = "return Confirmacion();"; 
    }
}

btnConfirmacion1: Hace uso de la propiedad OnClientClick para definir el método javascript definido en la pagina.

Un punto importante es el uso de return como antecesor el nombre de la función

OnClientClick="return Confirmacion();"

El objetivo de este es evitar el postback del botón cuando no se acepte el mensaje de confirmación. Si este return es quitado se realiza la invocación al servidor se acepte o no el mensaje de confirmación.

btnConfirmacion2: Aplica la misma técnica del ejemplo anterior, solo que la asignación del evento es realizada desde el servidor.

btnConfirmacion3: Hace uso de código javascript en línea directo en el evento, sin utilizar una función definida para tratar la confirmación.

Es importante notar el uso del return delante el mensaje de confirm(), este tomara el respuesta y asignara true o false, permitiendo o no hace el postback del botón.

 

[C#]
[VB.NET]

domingo, 4 de abril de 2010

C# - [Winforms] Como usar el SelectedValue

 

Introducción

En varias oportunidades he notado que trae problemas el incorrecto uso de ciertas propiedades en controles como ser el ComboBox, ListBox, CheckBoxList especialmente cuando estos están bindeados a datos que usan datatables.

Es por eso que aquí se trataran ejemplo de distintas formas de trabajar estos controles unidos a datos, y que propiedades usar cuando se quiere trabajar con ellos.

Uno de los principales problemas es el uso de la propiedad SelectedValue, esta permite tomar el valor del ítem seleccionado por el usuario, pero como manipular esta propiedad depende de la forma en como se haya vinculado el control al origen de datos.

Las distintas formas de trabajar la propiedad estará dada por la asignación o no de un valor en la propiedad ValueMember

Sin asignación del ValueMember

En el Form1 se hace uso de un DataSet Tipado de nombre “ProductosDataSet”, el cual será responsable por medio del TableAdapter (creado por el asistente), de cargar los datos proveniente de la db.

Solo se ha especificado la propiedad DisplayMember, al momento de bindear el DataTable de producto, devuelto por el método creado para tal fin.

[C#]

private void Form1_Load(object sender, EventArgs e)
{
    CargarComboBox();

    CargarListBox();

    CargarCheckBoxList();
}

private void CargarComboBox()
{
    cbProductos.DataSource = ObtenerProductos();
    cbProductos.DisplayMember = "Descripcion";
}

private void CargarListBox()
{
    lbProductos.DataSource = ObtenerProductos();
    lbProductos.DisplayMember = "Descripcion";
}

private void CargarCheckBoxList()
{
    ckbProductos.DataSource = ObtenerProductos();
    ckbProductos.DisplayMember = "Descripcion";
}

[VB.NET]

Private Sub Form1_Load(sender As Object, e As EventArgs)
	CargarComboBox()

	CargarListBox()

	CargarCheckBoxList()
End Sub

   Private Sub CargarComboBox()

       cbProductos.DataSource = ObtenerProductos()
       cbProductos.DisplayMember = "Descripcion"

   End Sub

   Private Sub CargarListBox()

       lbProductos.DataSource = ObtenerProductos()
       lbProductos.DisplayMember = "Descripcion"

   End Sub

   Private Sub CargarCheckBoxList()

       ckbProductos.DataSource = ObtenerProductos()
       ckbProductos.DisplayMember = "Descripcion"

   End Sub

En este caso al inspeccionar la propiedad SelectedValue, todos los controles devolverá un objeto del tipo DataRowView.

[C#]

private void cbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    DataRowView rowView = cbProductos.SelectedValue as DataRowView;
    
   
    lblSeleccionComboBox.Text = string.Format("Id: {0} \nDescripcion: {1} \nPrecio: {2}", 
                                        rowView["IdProducto"], 
                                        rowView["Descripcion"], 
                                        rowView["PrecioUnitario"]);
}

private void lbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    DataRowView rowView = lbProductos.SelectedValue as DataRowView;

    lblSeleccionListBox.Text = string.Format("Id: {0} \nDescripcion: {1} \nPrecio: {2}", 
                                        rowView["IdProducto"], 
                                        rowView["Descripcion"], 
                                        rowView["PrecioUnitario"]);

}

private void ckbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    DataRowView rowView = ckbProductos.SelectedValue as DataRowView;

    txtSeleccionCheckBoxList.Text = string.Format("Id: {0} \r\nDescripcion: {1} \r\nPrecio: {2}", 
                                        rowView["IdProducto"], 
                                        rowView["Descripcion"], 
                                        rowView["PrecioUnitario"]);
}

private void btnCheckedItems_Click(object sender, EventArgs e)
{
    txtSeleccionCheckBoxList.Text = "";

    foreach (DataRowView rowView in ckbProductos.CheckedItems)
    {
        txtSeleccionCheckBoxList.Text += string.Format("Id: {0} \r\nDescripcion: {1} \r\nPrecio: {2}\r\n\r\n", 
                                                            rowView["IdProducto"], 
                                                            rowView["Descripcion"], 
                                                            rowView["PrecioUnitario"]);
    }

}

[VB.NET]

Private Sub cbProductos_SelectedIndexChanged(sender As Object, e As EventArgs)
	Dim rowView As DataRowView = TryCast(cbProductos.SelectedValue, DataRowView)


       lblSeleccionComboBox.Text = String.Format("Id: {1} {0}Descripcion: {2} {0}Precio: {3}", _
                                                 Environment.NewLine, _
                                                 rowView("IdProducto"), _
                                                 rowView("Descripcion"), _
                                                 rowView("PrecioUnitario"))
End Sub

   Private Sub lbProductos_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

       Dim rowView As DataRowView = TryCast(lbProductos.SelectedValue, DataRowView)

       lblSeleccionListBox.Text = String.Format("Id: {1} {0}Descripcion: {2} {0}Precio: {3}", _
                                                 Environment.NewLine, _
                                                 rowView("IdProducto"), _
                                                 rowView("Descripcion"), _
                                                 rowView("PrecioUnitario"))
   End Sub

   Private Sub ckbProductos_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

       Dim rowView As DataRowView = TryCast(ckbProductos.SelectedValue, DataRowView)

       txtSeleccionCheckBoxList.Text = String.Format("Id: {1} {0}Descripcion: {2} {0}Precio: {3}", _
                                         Environment.NewLine, _
                                         rowView("IdProducto"), _
                                         rowView("Descripcion"), _
                                         rowView("PrecioUnitario"))
   End Sub

   Private Sub btnCheckedItems_Click(ByVal sender As Object, ByVal e As EventArgs)

       txtSeleccionCheckBoxList.Text = ""

       For Each rowView As DataRowView In ckbProductos.CheckedItems

           txtSeleccionCheckBoxList.Text += String.Format("Id: {1} {0}Descripcion: {2} {0}Precio: {3}{0}{0}", _
                                             Environment.NewLine, _
                                             rowView("IdProducto"), _
                                             rowView("Descripcion"), _
                                             rowView("PrecioUnitario"))
       Next

   End Sub

Seguramente uno se preguntara porque este dato en concreto, bien resulta que este representa una vista de la fila del DataTable, y como solo estamos seleccionado un ítem individual en el control, este devolverá solo una fila (row) del DataTable usado para vincular el dato de ese ítem en particular.

 

Asignando un valor al ValueMember

En el Form2, se define el valor de uno de los campos del origen de datos en la propiedad ValueMember, esto hace que la aplicación trabaje de una forma distinta.

[C#]

private void Form2_Load(object sender, EventArgs e)
{
    CargarComboBox();

    CargarListBox();

    CargarCheckBoxList();
}

private void CargarComboBox()
{
    cbProductos.DisplayMember = "Descripcion";
    cbProductos.ValueMember = "IdProducto";
    cbProductos.DataSource = ObtenerProductos();

}

private void CargarListBox()
{
    lbProductos.DisplayMember = "Descripcion";
    lbProductos.ValueMember = "IdProducto";
    lbProductos.DataSource = ObtenerProductos();

}

private void CargarCheckBoxList()
{
    ckbProductos.ValueMember = "IdProducto";
    ckbProductos.DataSource = ObtenerProductos();
    ckbProductos.DisplayMember = "Descripcion";
}

[VB.NET]

Private Sub Form2_Load(ByVal sender As Object, ByVal e As EventArgs)
    CargarComboBox()

    CargarListBox()

    CargarCheckBoxList()
End Sub

Private Sub CargarComboBox()

    cbProductos.DisplayMember = "Descripcion"
    cbProductos.ValueMember = "IdProducto"
    cbProductos.DataSource = ObtenerProductos()

End Sub

Private Sub CargarListBox()

    lbProductos.DisplayMember = "Descripcion"
    lbProductos.ValueMember = "IdProducto"
    lbProductos.DataSource = ObtenerProductos()

End Sub

Private Sub CargarCheckBoxList()

    ckbProductos.ValueMember = "IdProducto"
    ckbProductos.DataSource = ObtenerProductos()
    ckbProductos.DisplayMember = "Descripcion"

End Sub

Es importante destacar en este punto que la asignación de las propiedades debe realizarse antes de bindear la información, ya que si la propiedad ValueMember es asignada después esta no tendrá efecto.

[C#]

private void cbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    int IdProducto = Convert.ToInt32(cbProductos.SelectedValue);
    string Descripcion = Convert.ToString(cbProductos.Text);
    
   
    lblSeleccionComboBox.Text = string.Format("Id: {0} \nDescripcion: {1}",
                                        IdProducto, 
                                        Descripcion);
}

private void lbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    int IdProducto = Convert.ToInt32(lbProductos.SelectedValue);
    string Descripcion = Convert.ToString(lbProductos.Text);


    lblSeleccionListBox.Text = string.Format("Id: {0} \nDescripcion: {1}",
                                        IdProducto,
                                        Descripcion);

}

private void ckbProductos_SelectedIndexChanged(object sender, EventArgs e)
{
    int IdProducto = Convert.ToInt32(ckbProductos.SelectedValue);
    string Descripcion = Convert.ToString(ckbProductos.Text);


    txtSeleccionCheckBoxList.Text = string.Format("Id: {0} \r\nDescripcion: {1}",
                                        IdProducto,
                                        Descripcion);
}

private void btnCheckedItems_Click(object sender, EventArgs e)
{
    txtSeleccionCheckBoxList.Text = "";

    foreach (DataRowView rowView in ckbProductos.CheckedItems)
    {
        txtSeleccionCheckBoxList.Text += string.Format("Id: {0} \r\nDescripcion: {1} \r\nPrecio: {2}\r\n\r\n", 
                                                            rowView["IdProducto"], 
                                                            rowView["Descripcion"], 
                                                            rowView["PrecioUnitario"]);
    }

}

 

[VB.NET]

Private Sub cbProductos_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

    Dim IdProducto As Integer = Convert.ToInt32(cbProductos.SelectedValue)
    Dim Descripcion As String = Convert.ToString(cbProductos.Text)


    lblSeleccionComboBox.Text = String.Format("Id: {1} {0}Descripcion: {2}", _
                                              Environment.NewLine, _
                                              IdProducto, _
                                              Descripcion)
End Sub

Private Sub lbProductos_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

    Dim IdProducto As Integer = Convert.ToInt32(lbProductos.SelectedValue)
    Dim Descripcion As String = Convert.ToString(lbProductos.Text)


    lblSeleccionListBox.Text = String.Format("Id: {1} {0}Descripcion: {2}", _
                                              Environment.NewLine, _
                                              IdProducto, _
                                              Descripcion)
End Sub

Private Sub ckbProductos_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)

    Dim IdProducto As Integer = Convert.ToInt32(ckbProductos.SelectedValue)
    Dim Descripcion As String = Convert.ToString(ckbProductos.Text)


    txtSeleccionCheckBoxList.Text = String.Format("Id: {1} {0}Descripcion: {2}", _
                                      Environment.NewLine, _
                                      IdProducto, _
                                      Descripcion)
End Sub

Private Sub btnCheckedItems_Click(ByVal sender As Object, ByVal e As EventArgs)

    txtSeleccionCheckBoxList.Text = ""

    For Each rowView As DataRowView In ckbProductos.CheckedItems

        txtSeleccionCheckBoxList.Text += String.Format("Id: {1} {0}Descripcion: {2} {0}Precio: {3}{0}{0}", _
                                          Environment.NewLine, _
                                          rowView("IdProducto"), _
                                          rowView("Descripcion"), _
                                          rowView("PrecioUnitario"))
    Next

End Sub

Ahora si, al momento de recuperar el valor este retornara en la propiedad SelectedValue un dato simple, en este caso un entero por tratarse del Id del Producto.

Aquí ya no podrá recuperarse el precio en la misma acción, o al menos no con la propiedad SelectedValue, para poder tomar el DataRowView, se deberá recurrir al SelectedItem

 

Aclaración

Para cambiar la ejecución de un Formulario a otro, deberás modificar el archivo Program.cs o Program.vb según el lenguaje usado

cambiando la línea: Application.Run(new Form1());

por el nombre de la clase del formulario que quiere ejecutarse

 

[C#]
[VB.NET]

viernes, 2 de abril de 2010

C# – Eliminar ítems en una lista

 

Introducción

Existen muchas formas de trabajar con una lista, y realizar operaciones de búsqueda y eliminación de aquellos elementos que debe ser filtrados, pero hay que tener cuidado ya que no todas las operaciones son validas

En este articulo se analizaría las formas de buscar un elemento y removerlo de la colección, analizando además métodos no recomendados

El problema

Cuando se tiene que realizar una operación sobre una lista, filtrando sus datos, lo primero que viene a la mente es recorrerla y allí mismo eliminar los ítems que no coinciden con la búsqueda.

O sea implementar algo similar a esto:

private void btnEliminarConProblema_Click(object sender, EventArgs e)
{
    List<Producto> list = ObtenerLista();

    foreach (Producto item in list)
    {
        if (!item.Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower()))
            list.Remove(item);
    }

    LoadListView(list);
}

Pero esta primera aproximación presenta un defecto técnico, resulta que las listas necesita mantener el índice intacto para poder mantener el ciclo, el cual estamos alterando si removemos un ítem.

En este caso veremos un error con el mensaje:

Collection was modified; enumeration operation may not execute.

Soluciones

Existe una buen variedad de soluciones a este problema, en donde la implementación de cada una dependerá del objetivo que se quiera alcanzar

Una de ellas podría ser implementando el ciclo por medio de un simple for

[Usando el For]

private void btnEliminarConFor_Click(object sender, EventArgs e)
{
    List<Producto> list = ObtenerLista();

    for (int i = 0; i < list.Count; i++)
    {
        if (!list[i].Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower()))
        {
            list.RemoveAt(i);
            i--;
        }
    }

    LoadListView(list);
}

Como se observa este requiere trabar con los índices de la colección, pero para poder mantenerlo actualizado es preciso restar uno cuando un ítem es quitado.

Otra alternativa podrías ser usar una lista auxiliar que acumule los ítems que no coinciden con la búsqueda, para ser eliminado en un segundo ciclo de la lista

[Usando una lista auxiliar]

private void btnEliminarConColeccionAuxiliar_Click(object sender, EventArgs e)
{
    List<Producto> listAux = new List<Producto>();
    List<Producto> list = ObtenerLista();

    foreach (Producto item in list)
    {
        if (!item.Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower()))
        {
            listAux.Add(item);
            continue;
        }

    }

    foreach (Producto item in listAux)
    {
        list.Remove(item);
    }

    LoadListView(list);

}

El inconveniente con esta aproximación es que se necesitan dos ciclos para terminar la tarea.

También existen alternativas algo mas performantes, como ser el uso de delegados, expresiones Lambda, o Linq

[Usando Delegados]

private void btnEliminarConDelegate_Click(object sender, EventArgs e)
{
    List<Producto> list = ObtenerLista();

    //
    // quitara aquellos productos en donde la funcion
    // definida en el delegado retorne un true
    //
    list.RemoveAll(delegate(Producto prod)
    {
        return !prod.Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower());
    });


    LoadListView(list);

}

[Usando Expresiones Lambda]

private void btnEliminarConLambda_Click(object sender, EventArgs e)
{
    List<Producto> list = ObtenerLista();

    list.RemoveAll(x => !x.Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower()));

    LoadListView(list);
}

[Usando Linq]

private void btnEliminarConLinq_Click(object sender, EventArgs e)
{
    List<Producto> list = ObtenerLista();

    list = (from item in list
            where item.Descripcion.ToLower().Contains(txtBusqueda.Text.ToLower())
            select item).ToList<Producto>();

    LoadListView(list);
}

 

[C#]
[VB.NET]

jueves, 1 de abril de 2010

C# - [Winforms] Seleccionar Fila con ENTER – DataGridView y ListView

 

Introducción

Este articulo es mostrara una alternativa de como se puede detectar la presión de una tecla (en este caso el ENTER) en un control determinado, pudiendo así trabajar con la selección.

En el ejemplo se trabajar con dos controles, el DataGridView y un ListView.

Básicamente la técnica para ambos ejemplos será similar, todo el trabajo se realizaría en el método ProcessCmdKey del formulario, (el cual es una sobrecarga), permitiendo atrapar la teclas pulsadas en el formulario, en este además se podrías validar que control esta activo en ese momento y en caso de ser el que resulta útil, procede a trabajar con la selección.

Para ambos ejemplo se permitirá optar (mediante radiobuttons) si la selección del ítem en la lista se mostrar en un mensaje, o será desplegado en otra ventana (formulario).

 

DataGridView - Selección de la fila

Como había comentado todo el trabajo esta en la función:

[C#]

protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
{
    //
    // Si el control DataGridView no tiene el foco, 
    // se abandonamos el procedimiento, llamando al metodo base
    //
    if ((!dataGridView1.Focused)) 
        return base.ProcessCmdKey(ref msg, keyData);

    //
    // Si la tecla presionada es distinta al ENTER, 
    // se abandonamos el procedimiento, llamando al metodo base
    //
    if (keyData != Keys.Enter) 
        return base.ProcessCmdKey(ref msg, keyData);
    //
    // Obtenemos la fila actual 
    //
    DataGridViewRow row = dataGridView1.CurrentRow;

    if (rbMostrarMensaje.Checked)
    {
        MessageBox.Show(string.Format("Se ha seleccionado, Cuenta:'{0}' Descripcion:'{1}'", 
                                            row.Cells["cuenta"].Value, 
                                            row.Cells["descripcion"].Value));
    }
    else if (rbMostrarForm.Checked)
    {
        int cuenta = Convert.ToInt32(row.Cells["cuenta"].Value);
        string desc = Convert.ToString(row.Cells["descripcion"].Value);

        frmSeleccion frm = new frmSeleccion(cuenta, desc);
        frm.ShowDialog();
    }

    return true;
} 

[VB.NET]

Protected Overloads Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
	'
	' Si el control DataGridView no tiene el foco, 
	' se abandonamos el procedimiento, llamando al metodo base
	'
	If (Not dataGridView1.Focused) Then
		Return MyBase.ProcessCmdKey(msg, keyData)
	End If

	'
	' Si la tecla presionada es distinta al ENTER, 
	' se abandonamos el procedimiento, llamando al metodo base
	'
	If keyData <> Keys.Enter Then
		Return MyBase.ProcessCmdKey(msg, keyData)
	End If
	'
	' Obtenemos la fila actual 
	'
	Dim row As DataGridViewRow = dataGridView1.CurrentRow

       If rbMostrarMensaje.Checked Then

           MessageBox.Show(String.Format("Se ha seleccionado, Cuenta:'{0}' Descripcion:'{1}'", row.Cells("cuenta").Value, row.Cells("descripcion").Value))

       ElseIf rbMostrarForm.Checked Then

           Dim cuenta As Integer = CInt(row.Cells("cuenta").Value)
           Dim desc As String = CStr(row.Cells("descripcion").Value)

           Dim frm As New frmSeleccion(cuenta, desc)
           frm.ShowDialog()

       End If

	Return True
End Function

- El primer punto es detectar si al presionar la tecla el foco lo tiene el control que se quiere trabajar, en este caso el DataGridView

- El segundo paso es determinar si la tecla presionada por el usuario es la que se desea controlar, en este caso será el ENTER

Si las dos validaciones anteriores pasaron, se puede recuperar la fila seleccionada en la grilla, y trabajar con la información que esta proporcione.

 

[C#]
[VB.NET]

 

ListView – Selección de la fila

La única diferencia entre este código y el usado en el DataGridview, será la forma en que se obtiene el ítem seleccionado.

Las validaciones en cuanto a la tecla presionada, y el foco en el control activo son idénticas para ambos casos.

[C#]

protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
{
    //
    // Si el control DataGridView no tiene el foco, 
    // se abandonamos el procedimiento, llamando al metodo base
    //
    if ((!listView1.Focused)) 
        return base.ProcessCmdKey(ref msg, keyData);

    //
    // Si la tecla presionada es distinta al ENTER, 
    // se abandonamos el procedimiento, llamando al metodo base
    //
    if (keyData != Keys.Enter) 
        return base.ProcessCmdKey(ref msg, keyData);

    //
    // Sino hay item seleccinado en la lista
    // se abandonamos el procedimiento, llamando al metodo base
    //
    if(listView1.SelectedItems.Count == 0)
        return base.ProcessCmdKey(ref msg, keyData);

    //
    // Obtenemos la fila actual 
    //
    ListViewItem item = listView1.SelectedItems[0]; 

    if (rbMostrarMensaje.Checked)
    {
        MessageBox.Show(string.Format("Se ha seleccionado, Cuenta:'{0}' Descripcion:'{1}'",
                                            item.Text,
                                            item.SubItems[1].Text));
    }
    else if (rbMostrarForm.Checked)
    {
        int cuenta = Convert.ToInt32(item.Text);
        string desc = Convert.ToString(item.SubItems[1].Text);

        frmSeleccion frm = new frmSeleccion(cuenta, desc);
        frm.ShowDialog();
    }

    return true;
} 

[VB.NET]

Protected Overloads Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
	'
	' Si el control DataGridView no tiene el foco, 
	' se abandonamos el procedimiento, llamando al metodo base
	'
	If (Not listView1.Focused) Then
		Return MyBase.ProcessCmdKey(msg, keyData)
	End If

	'
	' Si la tecla presionada es distinta al ENTER, 
	' se abandonamos el procedimiento, llamando al metodo base
	'
	If keyData <> Keys.Enter Then
		Return MyBase.ProcessCmdKey(msg, keyData)
	End If

	'
	' Sino hay item seleccinado en la lista
	' se abandonamos el procedimiento, llamando al metodo base
	'
	If listView1.SelectedItems.Count = 0 Then
		Return MyBase.ProcessCmdKey(msg, keyData)
	End If

	'
	' Obtenemos la fila actual 
	'
	Dim item As ListViewItem = listView1.SelectedItems(0)

       If rbMostrarMensaje.Checked Then

           MessageBox.Show(String.Format("Se ha seleccionado, Cuenta:'{0}' Descripcion:'{1}'", item.Text, item.SubItems(1).Text))

       ElseIf rbMostrarForm.Checked Then

           Dim cuenta As Integer = CInt(item.Text)
           Dim desc As String = CStr(item.SubItems(1).Text)

           Dim frm As New frmSeleccion(cuenta, desc)
           frm.ShowDialog()

       End If

       Return True

End Function

 

[C#]
[VB.NET]