sábado, 19 de octubre de 2013

43 Estructuras dinámicas: Listas tipo Pila - Problema de aplicación


Hasta ahora hemos visto como desarrollar los algoritmos para administrar una lista tipo Pila, hemos visto que hay bastante complejidad en el manejo de punteros pero todo esto acarrea ventajas en la solución de problemas que requieren una estructura de tipo Pila.


Planteo del problema:


Este práctico tiene por objetivo mostrar la importancia de las pilas en las Ciencias de la Computación y más precisamente en la programación de software de bajo nivel.

Todo compilador o intérprete de un lenguaje tiene un módulo dedicado a analizar si una expresión está correctamente codificada, es decir que los paréntesis estén abiertos y cerrados en un orden lógico y bien balanceados.

Se debe desarrollar una clase que tenga las siguientes responsabilidades (clase Formula):


- Ingresar una fórmula que contenga paréntesis, corchetes y llaves.
- Validar que los ( ) [] y {} estén correctamente balanceados. 

Para la solución de este problema la clase formula tendrá un atributo de la clase Pila.

Veamos como nos puede ayudar el empleo de una pila para solucionar este problema.

Primero cargaremos la fórmula en un TextBox.

Ejemplo de fórmula: (2+[3-12]*{8/3})

El algoritmo de validación es el siguiente:

Analizamos caracter a caracter la presencia de los paréntesis, corchetes y llaves.

Si vienen símbolos de apertura los almacenamos en la pila.

Si vienen símbolos de cerrado extraemos de la pila y verificamos si está el mismo símbolo pero de apertura:

en caso negativo podemos inferir que la fórmula no está correctamente balanceada.

Si al finalizar el análisis del último caracter de la fórmula la pila está vacía podemos concluir que está correctamente balanceada.

Ejemplos de fórmulas no balanceadas:

}(2+[3-12]*{8/3})
Incorrecta: llega una } de cerrado y la pila está vacía.
{[2+4}]
Incorrecta: llega una llave } y en el tope de la pila hay un corchete [.
{[2+4]
Incorrecta: al finalizar el análisis del último caracter en la pila queda pendiente una llave {.

Pila


Programa:

archivo: Pila.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Formula
{
    class Pila
    {
        class Nodo
        {
            public char simbolo;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(char x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.simbolo = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public char Extraer()
        {
            if (raiz != null)
            {
                char informacion = raiz.simbolo;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return char.MaxValue;
            }
        }

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}


archivo: Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            Pila pila1;
            pila1 = new Pila();
            string cadena = textBox1.Text;
            for (int f = 0; f < cadena.Length; f++)
            {
                if (cadena.ElementAt(f) == '(' || cadena.ElementAt(f) == '[' || cadena.ElementAt(f) == '{')
                {
                    pila1.Insertar(cadena.ElementAt(f));
                }
                else
                {
                    if (cadena.ElementAt(f) == ')')
                    {
                        if (pila1.Extraer() != '(')
                        {
                            Text = "Incorrecta";
                            return;
                        }
                    }
                    else
                    {
                        if (cadena.ElementAt(f) == ']')
                        {
                            if (pila1.Extraer() != '[')
                            {
                                Text = "Incorrecta";
                                return;
                            }
                        }
                        else
                        {
                            if (cadena.ElementAt(f) == '}')
                            {
                                if (pila1.Extraer() != '{')
                                {
                                    Text = "Incorrecta";
                                    return;
                                }
                            }
                        }
                    }
                }
            }
            if (pila1.Vacia())
            {
                Text = "Correcta";
            }
            else
            {
                Text = "Incorrecta";
            }
        }
    }
}

Primero declaramos y definimos la clase Pila. Almacenamos en cada nodo un caracter y llamamos al campo de información símbolo.

No es necesario implementar los métodos Imprimir, Cantidad, etc. Porque no se requieren para este problema.

La clase Formula tiene como atributos: un TextBox y un Button

En el método Click verifica si la fórmula están correctos los parentesis, corchetes y llaves.

En este analizamos la fórmula para verificar si está correctamente balanceada.

En este método es donde está gran parte del algoritmo de este problema. Mostramos en el titulo del Form si la formula esta correctamente balanceada.


Definimos una pila y extraemos el contenido del TextBox:

            Pila pila1;
            pila1 = new Pila();
            string cadena = textBox1.Text;

El for se repite tantas veces como caracteres tenga el TextBox.

Se deben procesar sólo los símbolos ( [ { y ) ] }.

Si el símbolo es un ( [ { de apertura procedemos a cargarlo en la pila:

                if (cadena.ElementAt(f) == '(' || cadena.ElementAt(f) == '[' || cadena.ElementAt(f) == '{')
                {
                    pila1.Insertar(cadena.ElementAt(f));
                }

En caso de ser un ) cerrado debemos extraer un carácter de la pila y verificar si no coincide con el paréntesis de apertura ( la fórmula está incorrecta:

                    if (cadena.ElementAt(f) == ')')
                    {
                        if (pila1.Extraer() != '(')
                        {
                            Text = "Incorrecta";
                            return;
                        }
                    }

El mismo proceso es para  los símbolos ]  }.

Al finalizar el análisis de toda la cadena si la pila está vacía podemos afirmar que la fórmula está correctamente balanceada, en caso contrario quiere decir que faltan símbolos de cerrado y es incorrecta:

            if (pila1.Vacia())
            {
                Text = "Correcta";
            }
            else
            {
                Text = "Incorrecta";
            }


Es importante entender que la clase Formula utiliza un objeto de la clase Pila para resolver el algoritmo de verificar el balanceo de la fórmula, pero no accede directamente a los nodos de la lista.


42 Estructuras dinámicas: Listas tipo Pila

Una lista se comporta como una pila si las inserciones y extracciones las hacemos por un mismo lado de la lista. También se las llama listas LIFO (Last In First Out - último en entrar primero en salir)

Importante: Una pila al ser una lista puede almacenar en el campo de información cualquier tipo de valor (int, char, float, vector de caracteres, un objeto, etc)

Para estudiar el mecanismo de utilización de una pila supondremos que en el campo de información almacena un entero (para una fácil interpretación y codificación)

Inicialmente la PILA está vacía y decimos que el puntero raiz apunta a null (Si apunta a null decimos que no tiene una dirección de memoria):

pila vacía


Insertamos un valor entero en la pila: insertar(10)

pila


Luego de realizar la inserción la lista tipo pila queda de esta manera: un nodo con el valor 10 y raiz apunta a dicho nodo. El puntero del nodo apunta a null ya que no hay otro nodo después de este.

Insertamos luego el valor 4: insertar(4)

pila


Ahora el primer nodo de la pila es el que almacena el valor cuatro. raiz apunta a dicho nodo. Recordemos que raiz es el puntero externo a la lista que almacena la dirección del primer nodo. El nodo que acabamos de insertar en el campo puntero guarda la dirección del nodo que almacena el valor 10.

Ahora qué sucede si extraemos un nodo de la pila. ¿Cuál se extrae? Como sabemos en una pila se extrae el último en entrar.

Al extraer de la pila tenemos: extraer()

pila


La pila ha quedado con un nodo.

Hay que tener cuidado que si se extrae un nuevo nodo la pila quedará vacía y no se podrá extraer otros valores (avisar que la pila está vacía)

Problema 1:

Confeccionar una clase que administre una lista tipo pila (se debe poder insertar, extraer e imprimir los datos de la pila)

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListasTipoPila1
{
    class Pila
    {
        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("Extraemos de la pila:"+pila1.Extraer());
            pila1.Imprimir();
            Console.ReadKey();
        }
    }
}

Analicemos las distintas partes de este programa:

        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

Para declarar un nodo debemos utilizar una clase. En este caso la 
información del nodo (info) es un entero y siempre el nodo tendrá una 
referencia de tipo Nodo, que le llamamos sig.

El puntero sig apunta al siguiente nodo o a null en caso que no exista otro nodo. Este puntero es interno a la lista. Para poder acceder a los atributos los definimos de tipo public.

También definimos un puntero de tipo Nodo llamado raiz. Este puntero tiene la dirección del primer nodo de la lista. En caso de estar vacía la lista, raiz apunta a null (es decir no tiene dirección)

El puntero raiz es fundamental porque al tener la dirección del primer nodo de la lista nos permite acceder a los demás nodos.

        public Pila()
        {
            raiz = null;
        }

En el constructor de la clase hacemos que raiz guarde el valor null. Tengamos en cuenta que si raiz tiene almacenado null la lista está vacía, en caso contrario tiene la dirección del primer nodo de la lista.

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

Uno de los métodos más importantes que debemos entender en una pila es el de Insertar un elemento en la pila.

Al método llega la información a insertar, en este caso en particular es un valor entero.
La creación de un nodo requiere dos pasos:

- Definición de un puntero o referencia a un tipo de dato Nodo:

            Nodo nuevo;


- Creación del nodo (creación de un objeto):


            nuevo = new Nodo();

Cuando se ejecuta el operador new se reserva espacio para el nodo. Realmente se crea el nodo cuando se ejecuta el new.

pila


Paso seguido debemos guardar la información del nodo:

            nuevo.info = x;

En el campo info almacenamos lo que llega en el parámetro x. Por ejemplo si llega un 5 el nodo queda:

pila


Por último queda enlazar el nodo que acabamos de crear al principio de la lista.

Si la lista está vacía debemos guardar en el campo sig del nodo el valor null para indicar que no hay otro nodo después de este, y hacer que raiz apunte al nodo creado (sabemos si una lista esta vacía si raiz almacena un null)

            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }

pila


Gráficamente podemos observar que cuando indicamos raiz=nuevo, el puntero raiz guarda la dirección del nodo apuntado por nuevo.

Tener en cuenta que cuando finaliza la ejecución del método el puntero nuevo desaparece, pero no el nodo creado con el operador new.

En caso que la lista no esté vacía, el puntero sig del nodo que acabamos de crear debe apuntar al que es hasta este momento el primer nodo, es decir al nodo que apunta raiz actualmente.

            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }

Como primera actividad cargamos en el puntero sig del nodo apuntado por nuevo la dirección de raiz, y posteriormente raiz apunta al nodo que acabamos de crear, que será ahora el primero de la lista.

Antes de los enlaces tenemos:

pila


Luego de ejecutar la línea:

                nuevo.sig = raiz;

Ahora tenemos:

pila


Por último asignamos a raiz la dirección que almacena el puntero nuevo.

                raiz = nuevo;

La lista queda:

pila


El método Extraer:

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

El objetivo del método extraer  es retornar la información del primer nodo y además borrarlo de la lista.

Si la lista no está vacía guardamos en una variable local la información del primer nodo:

                int informacion = raiz.info;

Avanzamos raiz al segundo nodo de la lista, ya que borraremos el primero:

                raiz = raiz.sig;

el nodo que previamente estaba apuntado por raiz es eliminado automáticamente, al no tener ninguna referencia.

Retornamos la información:

                return informacion;

En caso de estar  vacía la pila retornamos el número entero máximo y 
lo tomamos como código de error (es decir nunca debemos guardar el 
entero mayor en la pila)

                return int.MaxValue;

Es muy importante entender gráficamente el manejo de las listas. La 
interpretación gráfica nos permitirá plantear inicialmente las 
soluciones para el manejo de listas.

pila


Por último expliquemos el método para recorrer una lista en forma completa e imprimir la información de cada nodo:

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

Definimos un puntero auxiliar reco y hacemos que apunte al primer nodo de la lista:

            Nodo reco=raiz;

Disponemos una estructura repetitiva que se repetirá mientras reco 
sea distinto a null. Dentro de la estructura repetitiva hacemos que reco
 avance al siguiente nodo:

            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }

Es muy importante entender la línea:

                reco=reco.sig;

Estamos diciendo que reco almacena la dirección que tiene el puntero sig del nodo apuntado actualmente por reco.

Gráficamente:

pila


Al analizarse la condición:

            while (reco!=null) 

se valúa en verdadero ya que reco apunta a un nodo y se vuelve a ejecutar la línea:

                reco=reco.sig;

Ahora reco apunta al siguiente nodo:

pila


La condición del while nuevamente se valúa en verdadera y avanza el puntero reco al siguiente nodo:
                reco=reco.sig;

pila


Ahora sí reco apunta a null y ha llegado el final de la lista (Recordar que el último nodo de la lista tiene almacenado en el puntero sig el valor null, con el objetivo de saber que es el último nodo)

Para poder probar esta clase recordemos que debemos definir un objeto de la misma y llamar a sus métodos:

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("Extraemos de la pila:"+pila1.Extraer());
            pila1.Imprimir();
            Console.ReadKey();
        }

Insertamos 3 enteros, luego imprimimos la pila, extraemos uno de la pila y finalmente imprimimos nuevamente la pila.

Problema 2:

Agregar a la clase Pila un método que retorne la cantidad de nodos y otro que indique si esta vacía.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListasTipoPila1
{
    class Pila
    {
        class Nodo
        {
            public int info;
            public Nodo sig;
        }

        private Nodo raiz;

        public Pila()
        {
            raiz = null;
        }

        public void Insertar(int x)
        {
            Nodo nuevo;
            nuevo = new Nodo();
            nuevo.info = x;
            if (raiz == null)
            {
                nuevo.sig = null;
                raiz = nuevo;
            }
            else
            {
                nuevo.sig = raiz;
                raiz = nuevo;
            }
        }

        public int Extraer()
        {
            if (raiz != null)
            {
                int informacion = raiz.info;
                raiz = raiz.sig;
                return informacion;
            }
            else
            {
                return int.MaxValue;
            }
        }

        public void Imprimir() 
        {
            Nodo reco=raiz;
            Console.WriteLine("Listado de todos los elementos de la pila.");
            while (reco!=null) 
            {
                Console.Write(reco.info+"-");
                reco=reco.sig;
            }
            Console.WriteLine();
        }

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public int Cantidad()
        {
            int cant = 0;
            Nodo reco = raiz;
            while (reco != null)
            {
                cant++;
                reco = reco.sig;
            }
            return cant;
        }

        static void Main(string[] args)
        {
            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);
            pila1.Imprimir();
            Console.WriteLine("La cantidad de nodos de la lista es:"+pila1.Cantidad());
            while (pila1.Vacia()==false) 
            {
                Console.WriteLine(pila1.Extraer());
            }
            Console.ReadKey();
        }
    }
}

Para verificar si la pila esta vacía verificamos el contenido de la variable raiz, si tiene null luego la lista esta vacía y por lo tanto retornamos un true:

        public bool Vacia()
        {
            if (raiz == null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

El algoritmo para saber la cantidad de nodos es similar al imprimir, 
pero en lugar de mostrar la información del nodo procedemos a 
incrementar un contador:

        public int Cantidad()
        {
            int cant = 0;
            Nodo reco = raiz;
            while (reco != null)
            {
                cant++;
                reco = reco.sig;
            }
            return cant;
        }

Para probar esta clase en la main creamos un objeto de la clase Pila insertamos tres enteros:

            Pila pila1=new Pila();
            pila1.Insertar(10);
            pila1.Insertar(40);
            pila1.Insertar(3);

Imprimimos la pila (nos muestra los tres datos):

            pila1.Imprimir();

Llamamos al método Cantidad (nos retorna un 3):

Console.WriteLine("La cantidad de nodos de la lista es:"+pila1.Cantidad());

Luego mientras el método Vacia nos retorne un false (lista no vacía) procedemos a llamar al método extraer:


            while (pila1.Vacia()==false) 
            {
                Console.WriteLine(pila1.Extraer());
            }



    Problemas propuestos

    1. Agregar un método a la clase Pila que retorne la información del primer nodo de la Pila sin borrarlo.

41 Estructuras dinámicas: Listas


Una lista es un conjunto de nodos, cada uno de los cuales tiene dos campos: uno de información y un apuntador al siguiente nodo de la lista. Además un apuntador externo señala el primer nodo de la lista.
Representación gráfica de un nodo:

Nodo

La información puede ser cualquier tipo de dato simple, estructura de datos o inclusive uno o más objetos.
La dirección al siguiente nodo es un puntero.

Representación gráfica de una lista:

lista

Como decíamos, una lista es una secuencia de nodos (en este caso cuatro nodos). La información de los nodos en este caso es un entero y siempre contiene un puntero que guarda la dirección del siguiente nodo.
raiz es otro puntero externo a la lista que contiene la dirección del primer nodo.

El estado de una lista varía durante la ejecución del programa:

lista vacia


De esta forma representamos gráficamente una lista vacía.

Si insertamos un nodo en la lista quedaría luego:
lista con un nodo


Si insertamos otro nodo al principio con el valor 9 tenemos:
lista con dos nodos


Lo mismo podemos borrar nodos de cualquier parte de la lista.

Esto nos trae a la mente el primer problema planteado: el desarrollo del procesador de texto. Podríamos utilizar una lista que inicialmente estuviera vacía e introdujéramos un nuevo nodo con cada línea que tipea el operador. Con esta estructura haremos un uso muy eficiente de la memoria.

Tipos de listas.

Según el mecanismo de inserción y extracción de nodos en la lista tenemos los siguientes tipos:
  • Listas tipo pila.
  • Listas tipo cola.
  • Listas genéricas.
Una lista se comporta como una pila si las inserciones y extracciones las hacemos por un mismo lado de la lista. También se las llama listas LIFO (Last In First Out - último en entrar primero en salir)

Una lista se comporta como una cola si las inserciones las hacemos al final y las extracciones las hacemos por el frente de la lista. También se las llama listas FIFO (First In First Out - primero en entrar primero en salir)


Una lista se comporta como genérica cuando las inserciones y extracciones se realizan en cualquier parte de la lista.

Podemos en algún momento insertar un nodo en medio de la lista, en otro momento al final, borrar uno del frente, borrar uno del fondo o uno interior, etc.

40 Estructuras dinámicas

Conocemos algunas estructuras de datos como son los vectores y matrices. No son las únicas. Hay muchas situaciones donde utilizar alguna de estas estructuras nos proporcionará una solución muy ineficiente (cantidad de espacio que ocupa en memoria, velocidad de acceso a la información, etc.)

Ejemplo 1. Imaginemos que debemos realizar un procesador de texto, debemos elegir la estructura de datos para almacenar en memoria las distintas líneas que el operador irá tipeando. Una solución factible es utilizar una matriz de caracteres. Pero como sabemos debemos especificar la cantidad de filas y columnas que ocupará de antemano. Podría ser por ejemplo 2000 filas y 200 columnas. Con esta definición estamos reservando de antemano 800000 bytes de la memoria, no importa si el operador después carga una línea con 20 caracteres, igualmente ya se ha reservado una cantidad de espacio que permanecerá ociosa.

Tiene que existir alguna estructura de datos que pueda hacer más eficiente la solución del problema anterior.
Ejemplo 2. ¿Cómo estarán codificadas las planillas de cálculo? ¿Reservarán espacio para cada casilla de la planilla al principio? Si no la lleno, ¿lo mismo se habrá reservado espacio?


Utilizar una matriz para almacenar todas las casillas de una planilla de cálculo seguro será ineficiente.



Bien, todos estos problemas y muchos más podrán ser resueltos en forma eficiente cuando conozcamos estas nuevas estructuras de datos (Listas, árboles)

sábado, 12 de octubre de 2013

39 Controles comunes - ComboBox

El control ComboBox permite seleccionar un string de una lista.

Para inicializar los string que contendrá el ComboBox debemos acceder a la propiedad Items
Un evento muy útil con este control es cuando el operador selecciona un Item de la lista. Para capturar la selección de un item debemos codificar el evento SelectedIndexChanged.

Problema 1:

Cargar en un ComboBox los nombres de varios colores. Al seleccionar alguno mostrar en la barra de título del Form el string seleccionado.


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            Text = comboBox1.Text;
        }
    }
}

Cuando se selecciona un string de la lista se dispara el evento SelectedIndexChanged y procedemos a extraer el texto seleccionado del ComboBox y lo mostramos en el título del Form:

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            Text = comboBox1.Text;
        }


Problema 2:

Disponer tres controles de tipo ComboBox con valores entre 0 y 255 (cada uno representa la cantidad de rojo, verde y azul). Luego al presionar un botón pintar el fondo del Form con el color que se genera combinando los valores de los ComboBox.

ComboBox

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void Form1_Load(object sender, EventArgs e)
        {
            for (int f = 0; f <= 255; f++)
            {
                comboBox1.Items.Add(f.ToString());
                comboBox2.Items.Add(f.ToString());
                comboBox3.Items.Add(f.ToString());
            }
            comboBox1.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int rojo = int.Parse(comboBox1.Text);
            int verde = int.Parse(comboBox2.Text);
            int azul = int.Parse(comboBox3.Text);
            BackColor = Color.FromArgb(rojo, verde, azul);
        }
    }
}

La carga manual de cada ComboBox nos haría perder mucho tiempo en tiempo de diseño por lo que lo hacemos mediante un algoritmo. Cuando se carga el Form se ejecuta el evento Load donde mediante un for procedemos a añadir los 256 valores:

        private void Form1_Load(object sender, EventArgs e)
        {
            for (int f = 0; f <= 255; f++)
            {
                comboBox1.Items.Add(f.ToString());
                comboBox2.Items.Add(f.ToString());
                comboBox3.Items.Add(f.ToString());
            }

La propiedad Items del ComboBox tiene un método llamado Add que añade
 un elemento a la lista (como debemos pasar un string como parámetro 
convertimos a la variable entera f a string)

Luego para dejar seleccionado por defecto el primer item añadido inicializamos la propiedad SelectedIndex:

            comboBox1.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;

En el evento Click del botón procedemos a extraer el valor seleccionado de cada ComboBox y lo convertimos a entero:

            int rojo = int.Parse(comboBox1.Text);
            int verde = int.Parse(comboBox2.Text);
            int azul = int.Parse(comboBox3.Text);

Para cambiar el color de fondo del Form actualizamos la propiedad BackColor. El color lo generamos llamando al método estático FromArgb de la clase Color:

            BackColor = Color.FromArgb(rojo, verde, azul);


Problemas propuestos


  1. Solicitar el ingreso del nombre de una persona y seleccionar de un control ComboBox un país. Al presionar un botón mostrar en la barra del título del Form el nombre ingresado y el país seleccionado.

37 Controles comunes - CheckBox

El control CheckBox permite implementar un cuadro de selección (básicamente un botón de dos estados: seleccionado o no seleccionado)


Problema 1:

Confeccionar un programa que muestre 3 objetos de la clase CheckBox con etiquetas de tres idiomas.

Cuando se presiona un botón mostrar en la barra de títulos del Form todos los CheckBox seleccionados hasta el momento.

CheckBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            Text = "";
            if (checkBox1.Checked == true)
            {
                Text = Text + "(Inglés)";
            }
            if (checkBox2.Checked == true)
            {
                Text = Text + "(Francés)";
            }
            if (checkBox3.Checked == true)
            {
                Text = Text + "(Alemán)";
            }
        }
    }
}

La clase CheckBox tiene una propiedad llamada Checked (si tiene el valor true significa que el CheckBox esta seleccionado, en caso contrario no esta seleccionado.

En el evento Click del botón primero borramos el contenido del título del Form:

            Text = "";

Y seguidamente mediante estructuras if verificamos el estado de cada CheckBox, en caso de estar seleccionado concatenamos al título del Form el valor que representa ese CheckBox:

            if (checkBox1.Checked == true)
            {
                Text = Text + "(Inglés)";
            }


Problema 2:

Disponer un control Label que muestre el siguiente mensaje: "Esta de acuerdo con las normas del servicio?", luego un CheckBox y finalmente un objeto de tipo Button desactivo (propiedad Enabled con false). Cuando se tilde el CheckBox debemos activar el botón (para esto debemos responder al evento)

CheckBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                button1.Enabled = true;
            }
            else
            {
                button1.Enabled = false;
            }
        }
    }
}

Debemos implementar el evento CheckedChange del objeto checkBox1 (preguntamos si el CheckBox se encuentra seleccionado o no, en caso de estar seleccionado activamos el botón asignando a la propiedad Enabled el valor true):

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                button1.Enabled = true;
            }
            else
            {
                button1.Enabled = false;
            }
        }


Problema propuesto


  1. Disponer tres objetos de la clase CheckBox con nombres de navegadores web. Cuando se presione un botón mostrar en el título del Form los programas seleccionados.

36 Controles comunes - TextBox

El control más común para la entrada de datos por teclado es el TextBox.


Problema 1:

Confeccionar un programa que permita ingresar dos valores enteros por teclado y al presionar un botón mostrar en una Label la suma de dichos valores.

TextBox

Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            int valor1 = int.Parse(textBox1.Text);
            int valor2 = int.Parse(textBox2.Text);
            int suma = valor1 + valor2;
            label4.Text = suma.ToString();
        }
    }
}

Para saber el valor almacenado en un TextBox disponemos de la 
propiedad Text. Como la propiedad Text es de tipo string debemos 
convertir dicho valor a tipo entero mediante el método estático Parse de la clase int.

Luego para recuperar como enteros los dos valores almacenados en los TextBox:

            int valor1 = int.Parse(textBox1.Text);
            int valor2 = int.Parse(textBox2.Text);

Sumamos los dos enteros:

            int suma = valor1 + valor2;

Y finalmente cargamos en un objeto de la clase Label el resultado de 
la suma. Como la variable suma es un entero debemos llamar al método 
ToString() para retornar dicho valor como string:

            label4.Text = suma.ToString();


Problema 2:

Solicitar que se ingrese una clave. Si se ingresa la cadena "abc123" mostrar un mensaje de clave correcta en caso contrario mostrar clave incorrecta.

Utilizar un control de tipo TextBox para el ingreso de la clave y una Label para mostrar el resultado al presionar un botón.

Inicializar la propiedad UseSystemPasswordChar con el valor true (esto hace que cuando el operador tipee caracteres dentro del TextBox se visualicen como asteriscos)

TextBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text == "abc123")
            {
                label2.Text = "Clave correcta";
            }
            else
            {
                label2.Text = "Clave incorrecta";
            }
        }
    }
}

Para verificar si la clave es correcta comparamos la cadena cargada en el textBox1 con la cadena "abc123".
Hay otra propiedad en la clase TextBox llamada PasswordChar, si la propiedad UseSystemPasswordChar esta configurada con false podemos inicializar la propiedad PasswordChar con el caracter que queremos que se muestre al ingresar datos en el TextBox. Probar de inicializarlo con el caracter '+' y veremos que en vez de aparecer asteriscos aparecen caracteres '+'

Problema 3:

Disponer un control de tipo TextBox e inicializar la propiedad Multiline con el valor true (esto permite ingresar múltiples líneas dentro de un TextBox.

TextBox


Programa:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(textBox2.Text);
        }
    }
}

Cuando se presiona un botón se muestra en cuadro de mensajes (MessageBox) el texto ingresado en el textBox2:

            MessageBox.Show(textBox2.Text);

Problema propuesto


  1. Solicitar el ingreso de una clave de hasta 10 caracteres en un control de tipo TextBox (inicializar la propiedad MaxLength con el valor 10)
    Mostrar en un cuadro de mensajes la clave ingresada al presionar un botón.