usercontrol


UrlImage Control 

Como proposito, se intenta tener un control que pueda cargar una imagen en un PictureBox pasandole una direccion de internet (url) de donde tomarla.

Elementos en disenho del control de usuario

 urlImage en disenho

El control consta de 3 PictureBoxes. Dos de ellos se van a usar como ayudas para que guarden una imagen cada uno, en pbxLoading vamos a poner una imagen que represente que el control esta trabajando y en pbxNotFound vamos a poner una imagen para representar un error en el cargado de la imagen solicitada.  Estos controles siempre permaneceran ocultos (Visible = false) y tomaremos de ellos las imagenes para ponerlas en el pictureBox1 cuando las requiramos.

Codigo detras del control

urlImage codigo

Aqui en codigo podemos ver que estamos utilizando el evento Resize del UserControl para hacer que pictureBox1 cubra toda la superficie de nuestro control.  Y ademas agregando un metodo publico llamado GetImage(string url) con el cual podremos pedirle al control que intente cargar la imagen de internet que le proporcionemos con el parametro url

En GetImage(), primero pasamos la imagen que esta en pbxLoading al PictureBox principal, y despues intentamos (por medio de un try) cargar la imagen desde internet a nuestro PictureBox.  Esto lo logramos con la ayuda de la clase HttpWebRequest, donde especificamos la direccion de internet, y obtenemos su ResponseStream para pasarselo al PictureBox e intente mostrarlo a pantalla; en caso de algun error | excepcion, lo indicaremos cargando la imagen que se encuentra en pbxNotFound.

durante esta clase se trabajo con la aplicacion de “imageBrowser” (la acabo de bautizar). Y basicamente lo que hicimos fue pasarla de su fase uno a la fase dos

El principal concepto desarrollado durante esta sesion es:

Eventos,    Eventos,         Eventos

  – delegate
Explicacion ejemplo: [situacion] llamas a un amigo por telefono a su casa pero no esta, dejas tu telefono para que cuando llegue te marque… mientras tu puedes seguir con tus actividades y no quedarte en la linea esperando (tal vez horas) hasta que llegue.  [analogia .net] le haces saber a un metodo (telefono de tu amigo) que vas a requerir que se te notifique (dejas tu telefono) cuando ocurra este cierto metodo (evento), mientras tu aplicacion puede seguir con sus operaciones…  

La situacion explicada puede concretarse debido a los delegates | delegados que podemos crear en punto NET.   Un delegado | delegate tiene las siguientes caracteristicas:   

  • verifican los tipos de parametros que se les pasan; o bien, verifican que el numero de telefono que dejas para que tu amigo llame este en el formato correcto.
  • se aseguran que siempre se apunte a un metodo existente; o bien, se asegura que el numero de telefono exista.
  • soportan “multicasting”; o bien, puedes dejar un listado de telefonos para que tu amigo haga notificaciones de que ya llego a todos ellos.

 Ahora bien, pasando esto a nuestra aplicacion, enfocandonos a nuestros usercontrols DriveList y FolderList, queremos que cuando el usuario seleccione una nueva unidad de disco, el listado de FolderList se actualice para mostrar las carpetas correctas.

Para esto necesitamos poner en nuestro DriveList, un evento para que otros objetos puedan “suscribirse” y ser notificados cuando el evento ocurra.

create event/deleg

en este codigo, podemos ver que se declara un delegado para validar metodos que no regresan resultado (void), y aceptan un parametro tipo string, este delegado se llamara SelectionChangedHandler.  En la siguiente linea se declara un evento llamado SelectionChanged y acepta subscripciones de metodos validados por el delegado SelectionChangedHandler.  Aparte se esta agregando la linea de codigo que “dispara” el evento desde nuestro control hacia los objetos interesados en reaccionar a dicho evento.  Esta linea esta dentro del metodo comboBox1_SelectedIndexChanged, el cual a su vez se corre cada vez que se selecciona un elemento diferente de nuestro ComboBox.

Cuando se corre SelectionChanged(comboBox1.Text), nuestro evento se esta disparando y se lleva con el el texto desplegado en el ComboBox para proporcionarselo a quien se haya suscrito al evento, y le interese ser notificado cada vez que se cambia de unidad de disco. 

 Vamos haciendo unas observaciones de lo que esta ocurriendo… busquemos en el codigo del control la llamada al metodo ComponentInitialize(), seleccionalo y dale click-derecho y “Goto Definition”; ahora busca por alli y veras la linea

this.comboBox1.SelectedIndexChanged +=
   
new System.EventHandler(this.comboBox1_SelectedIndexChanged);

esto suscribe el metodo comboBox1_SelectedIndexChanged al evento SelectedIndexChanged del objeto comboBox1, y aparte se esta validando que el metodo cumpla con las especificaciones del delegado EventHandler.  Ahora selecciona EventHandler y dale click-derecho y “Goto Definition”, veras la declaracion:

public delegate void EventHandler(object sender, EventArgs e);

Esto nos muestra que las especificaciones del delegado validan metodos que no regresan resultado (void) y requieren dos parametros, uno object, y otro EventArgs.  …volviendo a nuestra declaracion de delegado, nosotros validaremos que el metodo que se suscribe no regrese valor y acepte una cadena.

Nuestro control ya cuenta con el evento SelectionChanged.

Suscribiendome al evento del DriveList ;            Subscripcion

El siguiente paso sera suscribir un metodo al evento de tal manera que nuestro FolderList se actualice con la ruta correspondiente cada vez que se cambie de unidad de disco en DriveList

Necesitamos un metodo que cumpla con los requisitos de nuestro delegado SelectionChangedHandler: no regresa resultado, y acepte una cadena… Lo llamare CambioDrive. [, se validan estos requisitos ya que este tipo de delegado fue el que escogi para declarar el evento SelectionChanged]  En este metodo esta la logica para desplegar en FolderList la ruta que se selecciono en DriveList; recordemos que el evento trae consigo lo que obtuvo de comboBox1.Text al momento de ser disparado, y nos lo va a proporcionar con la variable string newDrive que tiene el metodo CambioDrive.

implement event

…ya que tenemos nuestro metodo que se apega a los requisitos de nuestro delegado, vamos suscribiendonos al evento.  Esto lo estoy poniendo en el Form1_Load:

link method to event

..de esta manera cada vez que el usuario cambia de unidad de disco, se dispara el evento SelectionChanged de driveList1, con esto, se notifica a los suscritos (CambioDrive) y por lo tanto, folderList1.DisplayFolders() con la ruta de driveList1. 😀

En esta sesion, continuamos con la elaboracion de nuestros propios controles (UserControls).  La sesion pasada fue crear un control para validar correos electronicos (EmailInput).

Esta vez empezamos por definir algunos requerimientos para una nueva aplicacion.  Esta aplicacion nos permitira visualizar imagenes que podamos accesar desde nuestro sistema de archivos (lease unidades de disco tales como: discos duros, lectores cd o dvd, floppies, unidades de red mapeadas), basicamente como las que encontramos en windows explorer.  Hasta cierto punto vamos a “copiar” la funcionalidad del windows explorer en cuanto a que podremos seleccionar una unidad de disco, navegar por las carpetas/folders disponibles, y ver los archivos que estan dentro de las carpetas.  Finalmente, para seleccionar un archivo con extension de imagen y desplegarlo en nuestra forma…

Para esto vamos a empezar por desarrollar varios UserControls, especificamente:
DriveList, – FolderList, – FileList.

La funcionalidad principal para DriveList seria poder seleccionar una unidad de disco de la computadora; la funcionalidad principal para FolderList seria desplegar y navegar por los folders presentes; y la funcionalidad de FileList seria desplegar una lista de archivos para determinada carpeta.

DriveList UserControl

Este control sera un ComboBox para poder seleccionar alguna de las unidades de disco existentes en la computadora. En su evento Load, vamos a utilizar el metodo estatico GetDrives() de la clase DriveInfo del namespace System.IO; este metodo nos regresa un arreglo DriveInfo[] con cada uno de sus elementos representando una unidad de disco existente.

//(agregar la namespace System.IO al principio de nuestro codigo)

DriveInfo[] unidades = System.IO.DriveInfo.GetDrives();

..ya con esto, vamos a iterar el arreglo con un for, y agregar el nombre (letra asignada) de la unidad a nuestra lista ComboBox.  Aparte vamos a exponer la unidad seleccionada a manera de propiedad, esta propiedad la estoy llamando SelectedDrive:

drivelist01

por lo pronto, ya tenemos un UserControl para desplegar las unidades de disco existentes en la computadora !!

DriveList in action

FolderList UserControl

Ahora empecemos con nuestro segundo UserControl para navegar carpetas.  Para este, vamos a implementar un metodo que nos pida como parametro una ruta, entonces el metodo enlistara las subcarpetas de esta ruta en un ListBox.  Aparte vamos a exponer la ruta actual de nuestro control (como propiedad).

Vamos a ayudarnos del metodo estatico GetDirectories() de la clase Directory, iteramos el resultado y vamos agregando a nuestro ListBox.  En este punto, vamos a agregar una carpeta especial, para facilidad la vamos a nombrar “..” para simular un cambio de directorio hacia “afuera/arriba” en nuestro arbol de directorios/carpetas.

FolderList01

Ya tenemos nuestro metodo para captar una ruta, y desplegar las sub-carpetas de esa ruta, ahora vamos a extender un poco mas la funcionalidad de nuestro UserControl poniendo logica en el DobleClick / DoubleClick del listado de carpetas.  Esto para que podamos “entrar” a la carpeta que se da doble click, y ver las carpetas que estan dentro…

FolderList02

..con esto ya tenemos un “explorador” de carpetas; con la ayuda de un Label, y asignandole nuestra propiedad Path
label1.Text = folderList1.Path;
podemos desplegar la ruta actual de las carpetas que estan desplegadas…

FolderList in action
[tuve que recurrir a un Timer para sincronizar el label con el folderList, despues vamos a implementar eventos para que otros controles/objetos puedan ser notificados de acciones en nuestros controles…]

Hasta aqui llegamos durante la sesion… el martes despues del pequenho y facil examen le seguimos…

Descarga del proyecto fase uno (incompleto): Proyecto fase uno
nota obligatoria, renombrar el archivo a .zip

En esta clase vimos introduccion a controles “customizados” (custom controls), y nos enfocamos a la tecnica de extender (agregar funcionalidad a) controles ya existentes…  El ejemplo desarrollado en clase fue extender un TextBox de tal manera que nos validara si el texto contenido en el es un correo electronico (email) valido. 

Determinamos algunas reglas que una cadena debe cumplir para considerarla un email valido, siendo estas:

i. que solo contenga una arroba (@).
ii. que exista un punto (.) despues de la posicion de la arroba.
iii. que la cadena tuviera al menos una letra despues del ultimo punto; o bien, que la cadena no termine en punto.

Vimos como podemos notificarle al usuario en caso de que el “email” proporcionado no cumpliera con todas las reglas que determinamos.  Podemos implementar notificaciones intrusivas y no-intrusivas.

Las intrusivas de las que platicamos fueron: – desplegar un MessageBox, – atrapar el foco de la aplicacion en nuestro control hasta que se proporcione un correo valido.

Y la no-intrusiva (y por la que optamos implementar): – cambiar la visualizacion del control.

Cuando se proporcione una cadena que no cumple con las reglas determinadas, nuestro control cambiara a fondo rojo, y letras amarillas, cuando la entrada sea valida, cambiara a fondo blanco con letras negras.

Desarrollo en Visual Studio

Empezamos por crear un proyecto de aplicacion para windows, seguido por agregar otro proyecto a la solucion de biblioteca de controles.  Por defecto, la aplicacion de windows cuenta con el archivo Form1, y la biblioteca de controles con el archivo UserControl1.  Renombramos el UserControl1 a EmailInput.

a continuacion el codigo para EmailInput: (agregar un TextBox en la posicion 0,0)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace MisControles
{
  public partial class EmailInput : UserControl
  {
  private bool _isValid = false;
  public bool IsValid {
  get { return _isValid; }
  //set { _isValid = value; }
  }
public EmailInput()
  {
  InitializeComponent();
  }
private void UserControl1_Load(object sender, EventArgs e)
  {
  //se dispara cuando se agrega este control
  // a una forma
  UserControl1_Resize(this, new EventArgs());
  }
private void UserControl1_Resize(object sender, EventArgs e)
  {
  textBox1.Width = this.Width;
  this.Height = textBox1.Height;
  }
private void textBox1_Leave(object sender, EventArgs e)
  {
  //comprobar que existe arroba
  if (textBox1.Text.Contains("@") == false) {
  //el texto no contiene arroba
  textBox1.BackColor = Color.FromArgb(255,0,0); //Color.Red
  textBox1.ForeColor = Color.Yellow;
  _isValid = false;
  return;
  }
  //comprobar que solo existe una arroba
  string[] pedazos = textBox1.Text.Split('@');
  if (pedazos.Length != 2) {
  textBox1.BackColor = Color.FromArgb(255, 0, 0); //Color.Red
  textBox1.ForeColor = Color.Yellow;
  _isValid = false;
  return;
  }
  //comprobar que existe un punto despues de la arroba
  int pos_arroba = textBox1.Text.IndexOf("@");
  int pos_ultimo_punto = textBox1.Text.LastIndexOf(".");
  if (pos_ultimo_punto < pos_arroba) {
  textBox1.BackColor = Color.FromArgb(255, 0, 0); //Color.Red
  textBox1.ForeColor = Color.Yellow;
  _isValid = false;
  return;
  }
  //comprobar que existen al menos 1 letra despues del ultimo punto
  if(textBox1.Text.EndsWith(".")){
  textBox1.BackColor = Color.FromArgb(255, 0, 0); //Color.Red
  textBox1.ForeColor = Color.Yellow;
  _isValid = false;
  return;
  }
  if (textBox1.Text.Contains(" ")) {
  textBox1.BackColor = Color.FromArgb(255, 0, 0); //Color.Red
  textBox1.ForeColor = Color.Yellow;
  _isValid = false;
  return;
  }
  textBox1.BackColor = Color.FromArgb(255, 255, 255); //Color.White
  textBox1.ForeColor = Color.Black;
  _isValid = true;
  }
  }
}
La logica de las reglas establecidas se corre cada vez que el usuario sale de nuestro control, con el metodo textBox1_Leave que esta asociado al evento Leave del TextBox.  Otra opcion pudo ser que la logica se corriera con cada tecla que el usuario presione, para esto el metodo se puede asociar al evento KeyPress del Textbox: textBox1.KeyPress += new EventHandler(textBox1_Leave).

La logica de las reglas incluye el manejo de una variable privada _isValid, donde guardamos si el control contiene un email valido o no.  Esta propiedad privada la exponemos como propiedad publica pero ReadOnly (solo lectura) para que el programador que utilice nuestro validador de correos pueda determinar si contiene uno valido o no.

     if ( emailInput1.IsValid ) // mandar el correo !!

descarga del proyecto completo: Proyecto Completo
nota obligatoria, renombrar el archivo a .rar

Otros conceptos vistos la clase:
String.Contains, String.StartsWith, String.EndsWith, Color.Black, Color.Red, Color.FromArgb, String.IndexOf, String.LastIndexOf, TextBox.ForeColor, TextBox.BackColor, UserControl.Width, UserControl.Height, ..

Screen..

Les presento a Form1 corriendo, contiene dos EmailInput‘s (control desarrollado en clase) y un Button donde podemos preguntarle a los emailInputs si contienen emails validos…

emailInputs in action