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. 

viernes, 4 de octubre de 2013

35 Controles comunes - Button

Un control común a disponer dentro de un Form son los botones, esto se hace disponiendo objetos de la clase Button.

Problema 1:

Confeccionar un formulario que muestre tres objetos de la clase Button, disponer como en cada botón 1,2 y 3. Cuando se presiona el botón mostrar en el título del formulario el valor de la etiqueta del botón presionado.

Button

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 WindowsFormsApplicationButton1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Text = button1.Text;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Text = button2.Text;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Text = button3.Text;
        }
    }
}

Para el evento click de cada botón inicializamos la propiedad Text del formulario con la propiedad Text del botón presionado (como la clase Form1 hereda de la clase Form luego accedemos a la propiedad Text sin anteceder nombre alguno: Text = button1.Text; ):

        private void button1_Click(object sender, EventArgs e)
        {
            Text = button1.Text;
        }

Problema 2:

el problema anterior para que se acumulen en el título del formulario los valores de los botones presionados.

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 WindowsFormsApplicationButton2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Text = Text + button1.Text;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Text = Text + button2.Text;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Text = Text + button3.Text;
        }
    }
}

Concatenamos el actual de la propiedad Text del formulario con el valor de la propiedad Text del botón respectivo:

        private void button1_Click(object sender, EventArgs e)
        {
            Text = Text + button1.Text;
        }

Problema 3:

Similar al problema anterior solo permitir mostrar hasta 10 caracteres en el título del formulario.

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 WindowsFormsApplicationButton3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button1.Text;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button2.Text;
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button3.Text;
            }
        }
    }
}

Como la propiedad Text es de tipo string luego podemos acceder a la propiedad Length para conocer la cantidad de caracteres almacenados:

        private void button1_Click(object sender, EventArgs e)
        {
            if (Text.Length < 10)
            {
                Text = Text + button1.Text;
            }
        }

Problema propuesto


  1. Elaborar una interfaz que muestre una calculadora (utilizar objetos de la clase Button y un objeto de la clase Label donde se muestra el valor ingresado), tener en que solo se debe implementar la interfaz y la carga de un valor de hasta 12 dígitos.

34 Controles comunes - Label

En el "cuadro de herramientas" podemos ver las componentes visuales agrupadas. En la pestaña de comunes podemos acceder a los controles visuales que normalmente toda aplicación requiere.
El primer control que vamos a analizar es la clase Label. Un control de tipo Label nos mostrar básicamente un texto inicializando la propiedad Text.

Clase Label - Windows Forms


Las propiedades más comunes de este control son:
  • Text: Es el string que muestra el control.
  • BackColor: Define el color de fondo del control.
  • ForeColor: Define el color del Texto .
  • Font: Define la fuente del texto.
  • BorderStyle: Define si la label tiene un borde visible.
  • AutoSize: Permite o no el label en Tamaño.
  • Cursor: Definimos el ícono del cursor a mostrar cuando disponemos el mouse del control.
  • Visible: Determina si el control está visible u oculto cuando ejecutamos el programa.

Problema propuesto


  1. Crear una aplicación que muestre en 6 objetos de la clase Label con algunos nombres de controles visuales contenidos en la pestaña de "controles comunes" del cuadro de herramientas

33 Ventana de eventos (Windows Forms)

La ventana de eventos coincide con la ventana de propiedades. Para activar la disponibles para un objeto debemos presionar:

Ventana de eventos Windows Forms


Podemos observar la lista de que puede reaccionar el objeto seleccionado en ese momento. Por ejemplo si tenemos seleccionado un objeto de la clase Button el evento más común que deberemos implementar es el Click (este evento se dispara cuando en tiempo de ejecución del programa se presiona el botón)
Para disponer el código para dicho evento debemos hacer doble clic sobre dicho evento (esto hace que se active la ventana del editor y genere automáticamente el método asociado a dicho evento):

Ventana de eventos Windows Forms

Problema:

Confeccionar un programa que al presionar un botón se muestre en un objeto de la clase Label el string "Hola Mundo".

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 WindowsFormsApplication5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = "Hola Mundo";
        }
    }
}
tener en cuenta que la clase anterior es parcial (el archivo Form1.Designer.cs contiene la definición de los dos objetos y la inicialización de sus propiedades y evento):
namespace WindowsFormsApplication5
{
    partial class Form1
    {
        /// 
        /// Variable del diseñador requerida.
        /// 
        private System.ComponentModel.IContainer components = null;

        /// 
        /// Limpiar los recursos que se estén utilizando.
        /// 
        /// true si los recursos administrados se deben eliminar;
        /// false en caso contrario, false.
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Código generado por el Diseñador de 

        /// 
        /// Método necesario para admitir el Diseñador. No se puede modificar
        /// el contenido del método con el .
        /// 
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(48, 102);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(35, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "label1";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(51, 148);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "Presionar";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button button1;
    }
}

Al ejecutar el programa si presionamos el botón vemos como cambia el contenido de la Label (esto debido a que en el evento click del botón cambiamos el valor de la propiedad Text del objeto de la clase Label):

Ventana de eventos Windows Forms

Problema propuesto

  1. Disponer 7 objetos de la clase Button con los días de la semana. Fijar en los atributos Text de cada botón los días de la semana. Al presionar un botón mostrar en un objeto de la clase Label el día seleccionado. 

32 Ventana de propiedades (Windows Forms)

La " de propiedades" nos permite inicializar de las propiedades del objeto que se encuentra seleccionado en el formulario (Button, MonthCalendar, TextBox etc.)

Por ejemplo si disponemos dos objetos de la clase Button y seleccionamos uno de ellos podremos editar las propiedades del mismo en la "ventana de propiedades":

Ventana de propiedades Windows Forms


A medida que seleccionamos un objeto en la ventana de "Diseño" podemos ver como se actualiza la "ventana de propiedades", por ejemplo la propiedad Text de la clase Button fijar la que muestra el botón.

El formulario también es un objeto, esto quiere decir que se lo seleccionamos luego la "ventana de propiedades" nos muestra las propiedades de la clase Form:

Ventana de propiedades Windows Forms

Problema propuesto

  1. Elaborar una interfaz que muestre una calculadora (utilizar objetos de la clase Button y un objeto de la clase donde se mostrarían los resultados y se cargarían los datos).

31 Cuadro de herramientas (Windows Forms)

El cuadro de herramientas contiene componentes visuales que nos permiten elaborar nuestro formulario.
Podemos ver controles visuales en :

Cuadro de herramientas Windows Forms


O agrupados por su uso(Menús, datos etc.):

Cuadro de herramientas Windows Forms

Problema 1:

: Desarrollar un programa que muestre un objeto de cada una de las siguientes clases: MonthCalendar, y Button

La interfaz visual debe ser parecida a esta:

Cuadro de herramientas Windows Forms


Hasta ahora solo hemos creado una interfaz visual, como podemos ver algunas componentes en de ejecución tienen funcionalidad (el objeto de la clase MonthCalendar si ejecutamos el programa nos seleccionar una fecha, cambiar de mes etc., el control de la clase TextBox nos permite ingresar una cadena de caracteres, pero el objeto de la clase Button cuando se presiona podemos ver que se visualiza que es hundido con el mouse pero no hace nada):

Cuadro de herramientas Windows Forms

30 Interfaces visuales (Windows Forms)

Hasta ahora hemos resuelto todos los algoritmos haciendo las salidas a través de una consola en modo texto. La realidad que es muy común la necesidad de hacer la entrada y salida de datos mediante una interfaz más amigable con el usuario.

En C# existen varias librerías de clase para implementar interfaces visuales. Utilizaremos las ventanas.
Para crear una aplicación que utilice esta librería debemos crear un proyecto. Los pasos son los siguientes:

  1. Desde el menú de opciones del .Net seleccionamos la opción: Archivo -> Nuevo proyecto...
  2. Seleccionamos la plantilla "Aplicación de Windows Forms".Proyecto Windows Forms
  3. Ahora ya tenemos un esqueleto para desarrollar nuestra aplicación. Si vemos la ventana del "Explorador de soluciones tenemos tres archivos generados en : Program.cs, Form1.cs y Form1.Designer.cs:
  4. Proyecto Windows Forms
  5. En la tenemos el Form listo para disponer controles con el mouse.
  6. Ahora podemos seleccionar un control visual de la ventana "Cuadro de herramientas" (seleccionemos el control Button) y seguidamente presionemos el botón izquierdo del mouse dentro del formulario que se encuentra en la parte central del Visual Studio .net:
  7. Proyecto Windows Forms
  8. Ahora podemos analizar la ventana "Propiedades" que nos muestra las propiedades del objeto seleccionado del formulario. Podemos por ejemplo si tenemos seleccionado el botón cambiar la propiedad text (la misma cambia la etiqueta que muestra el botón):
    Proyecto Windows Forms
  9. Cuando ejecutamos la aplicación el resultado podemos ver que es muy distinto a la interfaz en modo texto vista hasta el momento:
    Windows Forms
  10. Por último vamos a ver los contenidos de los archivos generados automáticamente por el Visual Studio .Net.

    Program.ch

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        static 
        {
            /// 
            /// Punto de entrada principal para la aplicación.
            /// 
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        }
    }
    

    Form1.ch

    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 WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
        }
    }
    

    Form1.Designer.ch

    namespace WindowsFormsApplication1
    {
        partial class Form1
        {
            /// 
            /// Variable del diseñador requerida.
            /// 
            private System.ComponentModel.IContainer components = null;
    
            /// 
            /// Limpiar los recursos que se estén utilizando.
            /// 
            /// true si los recursos administrados se deben eliminar; false en caso contrario, false.
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Código generado por el Diseñador de Windows Forms
    
            /// 
            /// Método necesario para admitir el Diseñador. No se puede modificar
            /// el contenido del método con el .
            /// 
            private void InitializeComponent()
            {
                this.button1 = new System.Windows.Forms.Button();
                this.SuspendLayout();
                // 
                // button1
                // 
                this.button1.Location = new System.Drawing.Point(190, 223);
                this.button1.Name = "button1";
                this.button1.Size = new System.Drawing.Size(75, 23);
                this.button1.TabIndex = 0;
                this.button1.Text = "Hola Mundo";
                this.button1.UseVisualStyleBackColor = true;
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(292, 273);
                this.Controls.Add(this.button1);
                this.Name = "Form1";
                this.Text = "Form1";
                this.ResumeLayout(false);
    
            }
    
            #endregion
    
            private System.Windows.Forms.Button button1;
        }
    }