100% encontró este documento útil (1 voto)
160 vistas

Guia C# 01

El documento describe cómo crear formularios MDI (Multiple Document Interface) con C#. Explica cómo configurar un formulario como contenedor MDI, agregar menús y botones de acción, y crear e interactuar con formularios secundarios ("hijos") desde el formulario principal. Además, muestra cómo usar ciclos como while para generar listas dinámicas y calcular factoriales de números introducidos por el usuario.
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (1 voto)
160 vistas

Guia C# 01

El documento describe cómo crear formularios MDI (Multiple Document Interface) con C#. Explica cómo configurar un formulario como contenedor MDI, agregar menús y botones de acción, y crear e interactuar con formularios secundarios ("hijos") desde el formulario principal. Además, muestra cómo usar ciclos como while para generar listas dinámicas y calcular factoriales de números introducidos por el usuario.
Derechos de autor
© © All Rights Reserved
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 79

Formularios MDI con C# (C Sharp)

EJERCICIO 01
Un formulario MDI es un formulario (llamado "Padre" o "Parent") que puede contener otros
formularios (llamados "Hijos" o "Child"). Para trabajar con este tipo de formularios se
ejecuta el Visual C# y se crea un nuevo proyecto del tipo "Windows Application". De forma
automtica se crea un formulario con nombre "Form1". En la ventana Propiedades, se busca
la propiedad "IsMDIContainer" y se cambia a "True". Luego en el men se elige Proyectos ->
Agregar Windows Form. Aparecer la ventana de "Agregar Nuevo Elemento". Se elige
"Windows Form".
El nuevo formulario se crea con el nombre Form2. A este control le agregamos 3 controles
tipo textBox que se dejarn con sus nombres por defecto: textBox1, textBox2 y textBox3.
En la ventana "Explorador de Soluciones", se le hace clic derecho al icono de Form1 y
clicamos en "Ver Cdigo". El cdigo a aadir ser:

Lo que se est haciendo es crear un objeto de la clase "Form2" (C# es orientado a objetos,
y todo est hecho en base a clases y namespaces). El objeto se llamar "Frm2". En las tres

lneas dentro de Form1_Load, le decimos a Form2 (a travs de Frm2) que est contenido
dentro de Form1.
"Frm2" nos permite tener control sobre Form2. Mas existe la condicin de que todo lo que
queramos hacerle a Form2 est dentro de mtodos pblicos. Form1 y Form2 son dos clases
separadas (ambas estn dentro del namespace "Semana07"), por lo que slo podemos
acceder a Form2 desde Form1 a travs de sus mtodos pblicos.
En el cdigo de Form2 aadimos:

Luego a Form1 le aadimos un control MenuStrip, jalndolo desde la caja de herramientas:

Luego se le hace clic a la etiqueta "limpiar" y se aade el cdigo:

Esto permite borrar las cajas de texto en Form2 haciendo click a la opcin "limpiar" en el
men de Form1.
Se pueden hacer ms cosas, por ejemplo, aad esto al menuStrip:

Junto con este cdigo:

Esto permite minimizar y maximizar Form2 clickeando la opcin correspondiente en el


men.
Por ltimo, siempre es bueno revisar que en el cdigo del archivo Program.cs, dentro del
mtodo Main, sea a Form1 al que se llame en la lnea de Application.Run (los programas en
C# siempre empiezan en el mtodo Main).

EJERCICIO 02
En el ejemplo vamos a crear una aplicacin MDI en la que tendremos un formulario principal
en el que aadiremos un men con opciones para:
Crear nuevos documentos, abrir un fichero de texto para mostrar en el documento activo,
una opcin para guardar el fichero del documento activo, otra para cerrar el documento
activo y una lista con los documentos que estn abiertos.
En el formulario que usaremos para cada nuevo documento simplemente tendremos una caja
de textos multilnea en la que mostraremos el fichero abierto.
Las opciones del formulario principal se basar en el documento activo, es decir que se
trata del documento activo es para que quede claro que cualquier accin que hagamos con
las opciones de los mens se basarn en el documento o formulario secundario que tenga el
foco, es decir, el activo.
Creamos un nuevo proyecto, se aade un formulario llamado Form3, le dejamos ese nombre.
El form se muestra por defecto, por tanto vamos a la ventana de propiedades (si no est
activa, puedes pulsar F4), buscamos la propiedad IsMdiContainer y le asignamos el valor
True (por defecto tiene el valor False).
A partir de ahora ya tenemos un formulario MDI principal, y notaremos que es as porque el
fondo del formulario cambia de color usando el que tengamos predeterminado en el
sistema, normalmente es un fondo gris oscuro.
Para aadir el formulario hijo, usaremos el Form2 que creamos en el ejercicio anterior.
Asignamos la propiedad MultiLine a True en el ltimo TextBox creado, el tipo de letras lo
puedes poner como Courier New, tambin debes asignar a la propiedad Dock el valor
Bottom (el botn que est en la parte inferior de las opciones que te muestra al seleccionar
esa propiedad desde la ventana de propiedades).

Aadir los mens al formulario MDI principal


Ahora aadimos un men principal, del Cuadro de herramientas seleccionamos MenuStrip, y
agregamos los siguientes mens: (Entre parntesis est el nombre del men)

&Ficheros (mnuFic)
o &Nuevo (mnuNuevo)
o o &Abrir... (mnuAbrir)
o &Guardar como... (mnuGuardarComo)
o o &Cerrar ventana (mnuCerrar)
o o &Salir (mnuSalir)
&Ventanas (mnuVentanas)
o &Minimizar todas (mnuMinimizar)

En los separadores no he puesto el nombre porque no los usamos en el cdigo, pero yo suelo
nombrarlos al estilo de mnuFicSepN donde N es un nmero que voy incrementando.
El siguiente es el cdigo del proyecto:
using
using
using
using
using
using
using
using
using

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

namespace Semana07
{
public partial class Form3 : Form
{
Form2 frm2;
public Form3()
{
InitializeComponent();
}
private void mnuNuevo_Click(object sender, EventArgs e)
{
// Crear una nueva ventana hija
frm2 = new Form2();
frm2.MdiParent = this;
// Para mostrarlo maximizado:
frm2.WindowState = FormWindowState.Maximized;
frm2.Show();
}
private void mnuSalir_Click(object sender, EventArgs e)
{
// Cerrando la ventana principal se cierran todas
this.Close();
//Application.Exit()
}
private void mnuCerrar_Click(object sender, EventArgs e)
{
// Cerrar la ventana hija que tiene el foco

// Asignar la ventana que tiene el foco


Form2 frm2 = (Form2)this.ActiveMdiChild;
if (frm2 != null)
{
frm2.Close();
}
}
private void mnuAbrir_Click(object sender, EventArgs e)
{
Form2 frm2 = ((Form2)this.ActiveMdiChild);
if (frm2 != null)
{
// Seleccionar el fichero que queremos abrir
OpenFileDialog ofD = new OpenFileDialog();
if (ofD.ShowDialog() == DialogResult.OK)
{
// Leemos el contenido de ese fichero
System.IO.StreamReader sr =
new System.IO.StreamReader(ofD.FileName, System.Text.Encoding.Default,
true);
// Lo asignamos al TextBox1 del Form2:
frm2.textBox3.Text = sr.ReadToEnd();
sr.Close();
}
}
}
private void mnuGuardarComo_Click(object sender, EventArgs e)
{
Form2 frm2 = ((Form2)this.ActiveMdiChild);
if (frm2 != null)
{
SaveFileDialog sfD = new SaveFileDialog();
if (sfD.ShowDialog() == DialogResult.OK)
{
System.IO.StreamWriter sw =
new System.IO.StreamWriter(sfD.FileName, false,
System.Text.Encoding.Default);
sw.WriteLine(frm2.textBox3.Text);
sw.Close();
}
}
}
private void mnuMinimizarTodas_Click(object sender, EventArgs e)
{
for (int i = 0; i < this.MdiChildren.Length; i++)
{
this.MdiChildren[i].WindowState = FormWindowState.Minimized;
}
}
}
}

Por ltimo, siempre es bueno revisar que en el cdigo del archivo Program.cs, dentro del
mtodo Main, sea a Form3 al que se llame en la lnea de Application.Run (los programas en
C# siempre empiezan en el mtodo Main).

CICLOS
EJERCICIO 03
Replique el siguiente ejercicio.

Cdigo
using
using
using
using
using
using
using
using
using

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

namespace Semana07
{
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
private void btnDia_Click(object sender, EventArgs e)
{
String numero;
switch (int.Parse(txtDia.Text))
{
case 1:

numero
break;
case 2:
numero
break;
case 3:
numero
break;
case 4:
numero
break;
case 5:
numero
break;
case 6:
numero
break;
case 7:
numero
break;
default:
numero
break;

= "Domingo";
= "Lunes";

= "Martes";

= "Miercoles";

= "Jueves";
= "Viernes";

= "Sabado";

= "No existe ese Dia";

}
lblDia.Text = "Da: " + numero;
}
private void btnFactorial_Click(object sender, EventArgs e)
{
int valor = 0;
valor = int.Parse(txtFactorial.Text);
lblFactorial.Text = "El factorial es: " + Factorial(valor);
}
public static long Factorial(int n)
{
if (n < 0) { return -1; } //para evitar errores asignamos cualquier numero.
if (n > 256) { return -2; }
if (n == 0) { return 1; }
// Calcula el factorial.
long tempResult = 1;
for (int i = 1; i <= n; i++)
{
tempResult *= i;
}
return tempResult;
}
private void btnMultiplicar_Click(object sender, EventArgs e)
{
int reng = 0;
int val = int.Parse(txtMultiplicar.Text);
lbxMultiplicar.Items.Clear();
while (reng <= 10)
{
int cal = 0;
cal = val * reng;
lbxMultiplicar.Items.Add(reng.ToString() + " x " + val + " = " + cal);
reng++;
};
}
}
}

01-Nombre de Aplicacin: Manejo del Control Listbox


Foto:

Descripcin: Pequea aplicacin que nos enseara como utilizar el Control Listbox,
agregaremos items provenientes de un Textbox y podremos eliminar un tem posicionando
nos en l y dando clic derecho.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio01 : Form
{
public Ejercicio01()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Add(textBox1.Text);
textBox1.Text = "";
textBox1.Focus();
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void eliminarSeleccionadoToolStripMenuItem_Click(object sender, EventArgs e)
{
listBox1.Items.Remove(listBox1.SelectedItem);
}

private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)


{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

02-Nombre de Aplicacin: Cargar Imagen en PictureBox


Foto:

Descripcin: Pequea aplicacin para cargar una Imagen en un Picturebox utilizando el


OpenFileDialog.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio02 : Form
{
public Ejercicio02()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog BuscarImagen = new OpenFileDialog();
BuscarImagen.Filter = "Archivos de Imagen|*.jpg";
BuscarImagen.FileName = "";
BuscarImagen.Title = "Programa de Contabilidad Casero";
BuscarImagen.InitialDirectory = "C:\\";
BuscarImagen.FileName = this.textBox1.Text;
if (BuscarImagen.ShowDialog() == DialogResult.OK)
{
/// Si esto se cumple, capturamos la propiedad File Name y la guardamos en el control
this.textBox1.Text = BuscarImagen.FileName;
//Pueden usar tambien esta forma para cargar la Imagen solo activenla y comenten la
linea donde se cargaba anteriormente
String Direccion = BuscarImagen.FileName;
this.pictureBox1.ImageLocation = Direccion;
//this.pictureBox1.ImageLocation = textBox1.Text;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;

}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

03-Nombre de Aplicacin: Convertidor de Grados Centigrados en Farenheit


Foto:

Descripcin: Aplicacin que nos sirve para hacer una conversin de Grados C a F o
Viceversa, el botn reconocer donde se han ingresado datos.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio03 : Form
{
private TextBox objTextBox = null;
public Ejercicio03()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
double grados;
// Estp valida en que caja de texto se escribio para mandar el resultado a la otra
if (objTextBox == textBox2)
{
grados = Convert.ToDouble(textBox2.Text) * 9.0 / 5.0 + 32.0;
// Como la variable es de tipo double puede arrojar decimales le decimos aqui
// que redondee el resultado a unicamente 2 decimales.
textBox1.Text = string.Format("{0:F2}", grados);
}
if (objTextBox == textBox1)
{
grados = (Convert.ToDouble(textBox1.Text) - 32.0) * 5.0 / 9.0;
// Mostrar el resultado redondeado a dos decimales
textBox2.Text = string.Format("{0:F2}", grados);

}
}
catch (FormatException)
{
textBox2.Text = "0,00";
textBox1.Text = "32,00";
}
}
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
objTextBox = (TextBox)sender;
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
objTextBox = (TextBox)sender;
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Focus();
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void textBox2_MouseClick(object sender, MouseEventArgs e)
{
TextBox ObjTextBox = (TextBox)sender;
ObjTextBox.SelectAll();
}
private void textBox1_MouseClick(object sender, MouseEventArgs e)
{
TextBox ObjTextBox = (TextBox)sender;
ObjTextBox.SelectAll();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

04-Nombre de Aplicacin: Fecha y Hora Actualizados


Foto:

Descripcin: Aplicacin que usa un Timer para ir actualizando la hora cada segundo.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio04 : Form
{
public Ejercicio04()
{
InitializeComponent();
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = DateTime.Now.ToString();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

05-Nombre de Aplicacin: Evitar Cierre Equivocado


Foto:

Descripcin: Aplicacin que sirve para controlar el Evento Closing de una aplicacin y
poder
cancelar
el
cierre.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio05 : Form
{
public Ejercicio05()
{
InitializeComponent();
}
private void Form1_FormClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
DialogResult dialogo = MessageBox.Show(" Desea Salir de la Aplicacion S/N ?",
"Salir de Aplicacion", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (dialogo == DialogResult.OK) { }
else { e.Cancel = true; }
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

06-Nombre de Aplicacin: Form con Transparencia


Foto:

Descripcin:
using
using
using
using
using
using
using
using

Sencilla

Aplicacin

para

poner

transparencia

una

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio06 : Form
{
public Ejercicio06()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Opacity = 0.70;
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

Forma.

07-Nombre de Aplicacin: Mostrar Varios tipos de Mensajes


Foto:

Descripcin: Aplicacin que nos muestra diferentes formas de mostrar un Mensaje en una
aplicacin.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio07 : Form
{
public Ejercicio07()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Mostrando un Mensaje por Medio de un Label";
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("Este es un Ejemplo de Mensaje usando MessageBox");
}
private void button3_Click(object sender, EventArgs e)
{
textBox1.Text = "Esta es Otra manera de Enviar texto a un control";
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{

linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");


}
}
}

08-Nombre de Aplicacin: Link Label


Foto:

Descripcin:
using
using
using
using
using
using
using
using

Solo

nos

muestra

cmo

utilizar

un

Link

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio08 : Form
{
public Ejercicio08()
{
InitializeComponent();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

Label.

09-Nombre de Aplicacin: Mini Reproductor de Video


Foto:

Descripcin: un poco mas complejo es hacer uso de los componentes del Wmplayer para
crear una aplicacin que reproduce Video en formatos MPEG y Avi.
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Drawing;
System.Windows.Forms;
System.Runtime.InteropServices;
System.Text;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio09 : Form
{
[DllImport("winmm.dll")]
private static extern long mciSendString(string strCommand, StringBuilder strReturn, int
iReturnLength, IntPtr hwndCallback);
string ruta;
string comandoMCI;
public Ejercicio09()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Liberar si ya se encontraba cargado un video
mciSendString("stop miVideo", null, 0, IntPtr.Zero);
mciSendString("close miVideo", null, 0, IntPtr.Zero);
OpenFileDialog abrirArchivo = new OpenFileDialog();
abrirArchivo.Filter = "Archivos de video|*.avi;*.mpeg;";
abrirArchivo.ShowDialog();

ruta = abrirArchivo.FileName;
//Abrir el dispositivo MCI
comandoMCI = string.Format("open \"{0}\" type mpegvideo alias miVideo wait", ruta);
long respuesta = mciSendString(comandoMCI, null, 0, IntPtr.Zero);
comandoMCI = "window miVideo handle " + this.pictureBox1.Handle.ToString();
mciSendString(comandoMCI, null, 0, IntPtr.Zero);
comandoMCI = "put miVideo destination at 0 0 " + this.pictureBox1.Width.ToString() + " "
+ this.pictureBox1.Height.ToString() + " wait";
mciSendString(comandoMCI, null, 0, IntPtr.Zero);
}
private void button2_Click(object sender, EventArgs e)
{
comandoMCI = string.Format("play miVideo");
mciSendString(comandoMCI, new StringBuilder(), 0, IntPtr.Zero);
}
private void button3_Click(object sender, EventArgs e)
{
comandoMCI = string.Format("pause miVideo");
mciSendString(comandoMCI, new StringBuilder(), 0, IntPtr.Zero);
}
private void button4_Click(object sender, EventArgs e)
{
comandoMCI = "seek miVideo to start";
mciSendString(comandoMCI, null, 0, IntPtr.Zero);
}
private
void
linkLabel1_LinkClicked(object
System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

sender,

10-Nombre de Aplicacin: Mini Stock


Foto:

Descripcin: Aplicacin que nos da un ejemplo de cmo podemos agregar productos a un


Stock
si
desarrollamos
algn
proyecto
de
punto
de
Venta.

using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio10 : Form
{
public Ejercicio10()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int precio, cantidad, total;
precio = Convert.ToInt32(textBox1.Text);
cantidad = Convert.ToInt32(textBox2.Text);
total = precio * cantidad;
label4.Text = "$" + Convert.ToString(total);
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

11-Nombre de Aplicacin: Modificar Propiedades Controles


Foto:

Descripcin: aplicacin que nos enseara a jugar un poco con las propiedades de los
controles.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio11 : Form
{
public Ejercicio11()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Visible = true;
}
private void button4_Click(object sender, EventArgs e)
{
textBox1.Enabled = false;
}
private void button5_Click(object sender, EventArgs e)

{
textBox1.Enabled = true;
}
private void button6_Click(object sender, EventArgs e)
{
textBox1.Text = textBox2.Text;
}
private void button3_Click(object sender, EventArgs e)
{
try
{
textBox1.ForeColor = System.Drawing.Color.FromName(comboBox1.Text);
}
catch { MessageBox.Show ("Debe seleccionar un color de la lista");}
}
private void button7_Click(object sender, EventArgs e)
{
try
{
textBox1.BackColor = System.Drawing.Color.FromName(comboBox1.Text);
}
catch { MessageBox.Show("Debe seleccionar un color de la lista"); }
}
private void button8_Click(object sender, EventArgs e)
{
textBox1.Text = "";
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

12-Nombre de Aplicacin: Mover Form sin Bordes


Foto:

Descripcin: Nos ensea cmo mover un form que no tiene bordes.


using
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Runtime.InteropServices;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio12 : Form
{
[DllImport("user32.DLL", EntryPoint = "ReleaseCapture")]
private extern static void ReleaseCapture();
[DllImport("user32.DLL", EntryPoint = "SendMessage")]
private extern static void SendMessage(System.IntPtr hWnd, int wMsg, int wParam, int lParam);
public Ejercicio12()
{
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
ReleaseCapture();
SendMessage(this.Handle, 0x112, 0xf012, 0);
}
private void button1_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}

private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)


{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

13-Nombre de Aplicacin: Proveedor de Errores


Foto:

Descripcin: Esta aplicacin nos ensea a usar el control ErrorProvider vern as sus
ventajas y desventajas (No conozco a nadie que le guste usarlo pero por si quieren saber
cmo se usa)
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio13 : Form
{
private double datoTextBox;
public Ejercicio13()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Focus();
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void CajaTexto_Validating(object sender, CancelEventArgs e)
{
TextBox objTextBox = (TextBox)sender;
try
{
datoTextBox = Convert.ToDouble(objTextBox.Text);
}
catch (Exception)
{
e.Cancel = true;
textBox1.SelectAll();
proveedorError.SetError(objTextBox, "Tiene que ser numrico");
}
}
private void CajaTexto_Validated(object sender, EventArgs e)
{
Debug.WriteLine("Caja de texto validada");
proveedorError.Clear();
}

private void button1_Click(object sender, EventArgs e)


{
textBox1.SelectAll();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

14-Nombre de Aplicacin: Realizar un Clic


Foto:

Descripcin: Nos ensea como ejecutar un clic presionando la tecla enter estando dentro de
un Textbox.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio14 : Form
{
public Ejercicio14()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Me haz dado un Clic");
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)(Keys.Enter))
{
e.Handled = true;
button1.PerformClick();
}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}

15-Nombre de Aplicacin: Realizar copia y cambiar formato


Foto:

Descripcin: Muy til quiero hacer una versin ms completa cuantos no hemos necesitado
cambiar el formato de una imagen y a veces necesitamos algo sumamente simple para
hacerlo, es una aplicacin que carga una imagen en un picturebox y nos deja realizar una
copia en C: con el formato que escojamos.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio15 : Form
{
public Ejercicio15()
{
InitializeComponent();
}
private void button5_Click(object sender, EventArgs e)
{
OpenFileDialog BuscarImagen = new OpenFileDialog();
BuscarImagen.Filter = "Archivos de Imagen|*.jpg";
BuscarImagen.FileName = "";
BuscarImagen.Title = "Realizar Copia de Imagen";
BuscarImagen.InitialDirectory = "C:\\";
BuscarImagen.FileName = this.txtImagen.Text;
if (BuscarImagen.ShowDialog() == DialogResult.OK)
{
this.txtImagen.Text = BuscarImagen.FileName;
this.pictureBox1.ImageLocation = txtImagen.Text;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}

}
private void button1_Click(object sender, EventArgs e)
{
String NombreArchivo;
NombreArchivo = System.IO.Path.GetFileNameWithoutExtension(txtImagen.Text);
Bitmap Picture = new Bitmap(txtImagen.Text);
Picture.Save(@"C:\" + NombreArchivo + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
MessageBox.Show("Se guardo la Copia correctamente en C:");
}
private void button2_Click(object sender, EventArgs e)
{
String NombreArchivo;
NombreArchivo = System.IO.Path.GetFileNameWithoutExtension(txtImagen.Text);
Bitmap Picture = new Bitmap(txtImagen.Text);
Picture.Save(@"C:\" + NombreArchivo + ".bmp", System.Drawing.Imaging.ImageFormat.Bmp);
MessageBox.Show("Se guardo la Copia correctamente en C:");
}
private void button3_Click(object sender, EventArgs e)
{
String NombreArchivo;
NombreArchivo = System.IO.Path.GetFileNameWithoutExtension(txtImagen.Text);
Bitmap Picture = new Bitmap(txtImagen.Text);
Picture.Save(@"C:\" + NombreArchivo + ".png", System.Drawing.Imaging.ImageFormat.Png);
MessageBox.Show("Se guardo la Copia correctamente en C:");
}
private void button4_Click(object sender, EventArgs e)
{
String NombreArchivo;
NombreArchivo = System.IO.Path.GetFileNameWithoutExtension(txtImagen.Text);
Bitmap Picture = new Bitmap(txtImagen.Text);
Picture.Save(@"C:\" + NombreArchivo + ".gif", System.Drawing.Imaging.ImageFormat.Gif);
MessageBox.Show("Se guardo la Copia correctamente en C:");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

16-Nombre de Aplicacin: Recorrer con Enter


Foto:

Descripcin: Para aplicaciones que requieran captura de datos sin usar el Mouse.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio16 : Form
{
public Ejercicio16()
{
InitializeComponent();
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)(Keys.Enter))
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)(Keys.Enter))
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)(Keys.Enter))
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
private void textBox4_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)(Keys.Enter))

{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

17-Nombre de Aplicacin: Valor DateTimePicker


Foto:

Descripcin: Obtener el valor de un datetimepicker y pasarlo a otro control.

using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio17 : Form
{
public Ejercicio17()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
label2.Text = dateTimePicker1.Text;
}
}
}

18-Nombre de Aplicacin: Valor MonthCalendar


Foto:

Descripcin:
using
using
using
using
using
using
using
using

Lo

mismo

que

el

anterior

pero

usando

un

MonthCalendar.

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio18 : Form
{
public Ejercicio18()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = monthCalendar1.SelectionStart.Day.ToString() + "/" +
monthCalendar1.SelectionStart.Month.ToString() + "/" +
monthCalendar1.SelectionStart.Year.ToString();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}

19-Nombre de Aplicacin: Uso del SaveFile Dialog


Foto:

Descripcin: Saca una captura de pantalla y nos da la posibilidad de guardarla donde le


indiquemos.
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio19 : Form
{
public Ejercicio19()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Bitmap bmpCaptura = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
Graphics Captura = Graphics.FromImage(bmpCaptura);
//se toma la impresion de pantalla
Captura.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0,
0, Screen.PrimaryScreen.Bounds.Size);
pictureBox1.Image = bmpCaptura;
}
private void button2_Click(object sender, EventArgs e)
{
SaveFileDialog saveFile = new SaveFileDialog();
saveFile.Filter = "Imagenes BMP|*.bmp";
if (saveFile.ShowDialog() == DialogResult.OK)
{
Bitmap picture = new Bitmap(pictureBox1.Image);
picture.Save(saveFile.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
}
}

private void Form1_Load(object sender, EventArgs e)


{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

20-Nombre de Aplicacin: Validar texto Introducido


Foto:

Descripcin: Nos ensea a usar el evento Keypress y validarlo para indicarle que letras
queremos que acepte un control.
using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio20 : Form
{
public Ejercicio20()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox3.Text == "") { MessageBox.Show("No debe dejar espacios en Blanco"); }
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (Char.IsLetter(e.KeyChar))
{
e.Handled = false;
}
else if (Char.IsControl(e.KeyChar))
{
e.Handled = false;
}
else if (Char.IsSeparator(e.KeyChar))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
{

if (char.IsDigit(e.KeyChar))
{
e.Handled = false;
}
else if (char.IsControl(e.KeyChar))
{
e.Handled = false;
}
else
{
e.Handled = true;
}
}
}
}

21-Nombre de Aplicacin: Convertir String a Int


Foto:

Descripcin: Minicalculadora que nos enseara a convertir el valor de un textbox a Int.


using
using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio21 : Form
{
public Ejercicio21()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
int v1, v2;
double resultado;
v1 = Convert.ToInt32(textBox1.Text);
v2 = Convert.ToInt32(textBox2.Text);
resultado = v1 + v2;
label2.Text = Convert.ToString(resultado);
}
catch { MessageBox.Show("Debe indicar ambos valores y asegurese que sean valores
numericos"); }
}
private void button2_Click(object sender, EventArgs e)
{
try
{
int v1, v2;
double resultado;
v1 = Convert.ToInt32(textBox1.Text);
v2 = Convert.ToInt32(textBox2.Text);
resultado = v1 - v2;
label2.Text = Convert.ToString(resultado);
}
catch { MessageBox.Show("Debe indicar ambos valores y asegurese que sean valores
numericos"); }
}

private void button3_Click(object sender, EventArgs e)


{
try
{
int v1, v2;
double resultado;
v1 = Convert.ToInt32(textBox1.Text);
v2 = Convert.ToInt32(textBox2.Text);
resultado = v1 * v2;
label2.Text = Convert.ToString(resultado);
}
catch { MessageBox.Show("Debe indicar ambos valores y asegurese que sean valores
numericos"); }
}
private void button4_Click(object sender, EventArgs e)
{
try
{
int v1, v2;
double resultado;
v1 = Convert.ToInt32(textBox1.Text);
v2 = Convert.ToInt32(textBox2.Text);
resultado = v1 / v2;
label2.Text = Convert.ToString(resultado);
}
catch { MessageBox.Show("Debe indicar ambos valores y asegurese que sean valores
numericos"); }
}
}
}

22-Nombre de Aplicacin: Dar Formato a Texto


Foto:

Descripcin: Nos ayuda a usar los controles para cambiar la fuente y el color de un texto
dentro de un Textbox.
using
using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio22 : Form
{
public Ejercicio22()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ColorDialog color = new ColorDialog();
if (color.ShowDialog() == DialogResult.OK)
{
richTextBox1.ForeColor = color.Color;
}
}
private void button2_Click(object sender, EventArgs e)
{
FontDialog font = new FontDialog();
font.Font = richTextBox1.Font;
if (font.ShowDialog() == DialogResult.OK)
{
richTextBox1.Font = font.Font;
}
}
}
}

23-Nombre de Aplicacin: Maysculas y Minsculas


Foto:

Descripcin: Nos ayudara a cambiar el texto de un control a Maysculas o Minsculas, sirve


de vez en cuando.

using
using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio23 : Form
{
public Ejercicio23()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
String MyString = label1.Text;
label1.Text = MyString.ToUpper();
}
private void button2_Click(object sender, EventArgs e)
{
String MyString = label1.Text;
label1.Text = MyString.ToLower();
}
}
}

24-Nombre de Aplicacin: Usar RadioButton y Checkbox


Foto:

Descripcin: Una aplicacin que nos ensea cmo usar los Radiobutton y Checkbox muy
util y sencilla.
using
using
using
using
using
using
using

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

namespace Semana07B
{
public partial class Ejercicio24 : Form
{
public Ejercicio24()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
label3.Visible = true;
label4.Visible = true;
if (radioButton1.Checked) label3.Text = "Hombre";
if (radioButton2.Checked) label3.Text = "Mujer";
if (checkBox1.Checked) label4.Text = checkBox1.Text;
if (checkBox2.Checked) label4.Text = checkBox2.Text;
if (checkBox3.Checked) label4.Text = checkBox3.Text;
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}

25-Nombre de Aplicacin: CambiaEstiloVentana


Foto:

using
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Runtime.InteropServices;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio25 : Form
{
[DllImport("UxTheme")]
private static extern void SetWindowTheme(IntPtr hWnd, string pszSubAppName, string
pszSubIdList);
[DllImport("UxTheme")]
private static extern void SetWindowTheme(IntPtr hWnd, int pszSubAppName, int pszSubIdList);
public Ejercicio25()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
}
private void button2_Click(object sender, EventArgs e)
{
SetWindowTheme(this.Handle, 0, 0);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
}
}

26-Nombre de Aplicacin: EjecutarSonidos


Foto:

using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
System.Diagnostics;

namespace Semana07B
{
public partial class Ejercicio26 : Form
{
public Ejercicio26()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
System.Media.SystemSounds.Beep.Play();
}
private void button2_Click(object sender, EventArgs e)
{
System.Media.SystemSounds.Exclamation.Play();
}
private void button3_Click(object sender, EventArgs e)
{
System.Media.SystemSounds.Asterisk.Play();
}
private void button4_Click(object sender, EventArgs e)
{
System.Media.SystemSounds.Hand.Play();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ProcessStartInfo sInfo = new ProcessStartInfo(e.Link.LinkData.ToString());
Process.Start(sInfo);
}
private void Form1_Load(object sender, EventArgs e)
{
linkLabel1.Links.Add(0, linkLabel1.Text.Length, "http://www.cfallasd.com/");
}
}
}

Desarrollo de aplicaciones con .NET y WPF


Andrs Marzal
Departamento de Lenguajes y Sistemas Informticos
Universitat Jaume I
Decharlas, 24 de mayo de 2010

Qu es .NET? Qu es C#? Qu es WPF? Qu es Visual


Studio? Qu es Expression Blend?

odemos simplificar mucho y decir que .NET es la respuesta de Microsoft a Java. .NET ofrece un entorno
de ejecucin con mquina virtual para un lenguaje de mquina propio: IL, por Intermediate Language.
Diferentes lenguajes se traducen a ese lenguaje de mquina y un compilador de ltima hora genera
cdigo nativo, que es lo que realmente se ejecuta.
.NET sigue un estndar ECMA: Standard ECMA-335, Common Language Infrastructure (CLI). La
implementacin de Microsoft del CLI se conoce por CLR (Common Language Runtime). Hay una
implementacin libre de CLI desarrollada por Novell: Mono. Acompaa al entorno un conjunto de
libreras gigantesco, aspecto en el que .NET va significativamente por delante de Mono.
El lenguaje de preferencia para .NET es C# (se lee C Sharp), un lenguaje que se dise para superar
algunos problemas de Java. En particular, la diferencia sustancial entre valores y objetos y la carencia
de delegados que facilitaran la implementacin del patrn observador/observable. C# ha evolucionado
mucho desde su aparicin, pero mantiene una coherencia en el diseo que lo hace fcil de aprender.
Aunque es un lenguaje con herencia simple, implementacin de interfaces y memoria con
recoleccin automtica, como Java, se diferencia de ste en numerosos aspectos importantes. C# ha
integrado eficazmente varios conceptos de la programacin funcional, como las funciones annimas
y las clausuras. Cuenta adems con un mini-lenguaje para efectuar consultas a fuentes de datos, LINQ,
que facilita mucho la gestin de informacin proveniente de bases de datos, de colecciones en
memoria, de ficheros XML, etctera. Lo cierto es que LINQ facilita el trabajo con cualquier objeto que
proporcione una enumeracin de elementos. Las enumeraciones son muy corrientes en .NET, pues C#
facilita su diseo e implementacin mediante estructuras de control como yield return. C# evita,
adems, la verbosidad del patrn de consulta y asignacin de valor a campos (getters & setters) propia
de Java mediante las denominada propiedades. Finalmente cabe advertir que la implementacin de
tipos genricos en C# es mucho ms slida que la de Java, pues conserva informacin de tipos y
distingue entre valores y objetos en el parmetro de tipo, a diferencia de lo que ocurre en Java, que
basa su implementacin de genricos en el borrado de tipos. C# est estandarizado y su definicin se
encuentra en Standard ECMA-334 C# Language Specification. Va por la versin 4.0 tanto en .NET
como en Mono.
WPF son las siglas de Windows Presentation Foundation. Es un conjunto de libreras para
implementar aplicaciones interactivas. Arranc con el nombre en clave Avalon. Presenta muchos

Desarrollo de Aplicaciones con .NET y WPF

soporte del patrn orden (command), fcil conexin a


fuentes de datos va ligaduras (bindings), simplificacin

.NET y Software Libre


El principal problema de .NET para la
comunidad no es de carcter tcnico, sino el

de trabajo con objetos observables mediante

estigma de ser obra de Microsoft. Muchas de

propiedades de dependencia, herencia de valores para

las herramientas libres de uso comn en Java

propiedades por relacin jerrquica entre componentes,

estn disponibles para .NET: NHibernate,

acceso directo a hardware grfico, animaciones,


personalizacin completa de componentes mediante
plantillas, etctera.
La P de WPF viene de Presentation y es importante.
WPF soporta el patrn arquitectnico Modelo-VistaPresentador (frente al clsico Modelo-Vista-Controlador).
La versin WPF de este patrn es la que se conoce por
Modelo-Vista-Modelo de la Vista, o MVVM por
Model-View-ViewModel.

NAnt, NUnit, Spring.NET, etctera. Microsoft


abri en 2009 CodePlex, un espacio para dar
soporte a proyectos de software libre y
algunos de sus proyectos recientes se
distribuyen con licencias que permite acceder
al cdigo fuente (MEF e IronPython, por
ejemplo). Microsoft apoya oficiosamente la
iniciativa Moonlight, de Mono, con la que se
est desarrollando una versin abierta de
Silverlight.
Aunque CLI o C# se han publicado como

Hay una versin ligera de WPF diseada para correr

estndares ECMA y la actitud de Microsoft

incrustada en navegadores (aunque tambin puede

ante la comunidad de software libre ha

ejecutarse fuera del navegador): Silverlight. El proyecto


arranc con el nombre en clave WPF/E, por WPF
Everywhere, y muchas veces se habla de l en trminos
de competencia directa con Flex y Flash. Mono ofrece
una implementacin libre de Silverlight: Moonlight
(aunque suele ir retrasada con respecto a la de Microsoft:

evolucionado mucho, la comunidad mira con


recelo cualquier innovacin que provenga de
Microsoft. Por ejemplo, Miguel de Icaza, lder
del proyecto Mono, es frecuentemente
insultado o menospreciado por personas o
asociaciones respetadas en la comunidad del
software libre. (Se puede obtener informacin

La versin actual de Silverlight es la 3.0, con la 4.0 a

sobre uno de los ltimos rifi-rafes en

punto de salir, y Moonlight implementa la funcionalidad

http://www.fsf.org/blogs/rms/microsoft-

de la 2.0 y buena parte de 3.0).

codeplex-foundation,

WPF propone separar apariencia de lgica y lo lleva al

?ltsn=2009-09-21-028-35-OP-CY-EV).

extremo de ofrecer una herramienta para diseadores

Parece que ahora le toca a Microsoft sufrir

grficos que se integra en el proceso de desarrollo.


Cuando el programador crea una interfaz grfica se

http://www.linuxtoday.com/news_story.php3

una campaa de FUD como las que montaba


hace tiempo.

concentra en los elementos desde el punto de vista lgico y en cmo se comunican entre s y con los
datos de la aplicacin. Los ficheros generados son directamente accesibles con Microsoft Expression
Blend. All, el diseador encuentra una aplicacin con la que es sencillo cambiar el aspecto visual de los
elementos, aplicar efectos y disear animaciones. Blend es parte de la suite Microsoft Expression, que
incluye ms herramientas orientadas a diseadores grficos (como Microsoft Expression Design, una
herramienta en la lnea de Adobe Freehand).
Visual Studio es la plataforma de desarrollo por excelencia para .NET. Su ltima versin es Visual Studio
2010 (VS 2010) y ofrece soporte nativo para lenguajes .NET como C#, Visual Basic, F# (un lenguaje
funcional de la familia de Ocaml) y para proyectos clsicos con C o C++. VS 2010 es extensible y
cuenta con soporte (de terceras partes) para herramientas como Subversion o Mercurial. El proyecto
Mono cuenta con su propia plataforma de desarrollo: MonoDevelop. Y hay otra plataforma abierta,
SharpDevelop, aunque de uso marginal.

Qu es .NET? Qu es C#? Qu es WPF? Qu es Visual Studio? Qu es Expression Blend?

aspectos interesantes: separacin de apariencia y lgica,

Andrs Marzal

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Primeros pasos con WPF


elementos bsicos de WPF y C#. Nuestra aplicacin mostrar un formulario en el que el usuario puede
poner nombre y primer apellido. Tras pulsar un botn, se mostrar un cuadro de dilogo modal con un
saludo personalizado.
Empezamos iniciando VS 2010 (Figura 1a). Con la opcin FileNewProject creamos un nuevo
proyecto de tipo WPF Application al que denominamos HolaMundo (Figura 1b).

(a)

(b)

Figura 1. (a) Pantalla inicial de Visual Studio 2010. (b) Cuadro de dilogo para crear un nuevo proyecto.

Esto crea un directorio HolaMundo y una estructura de


ficheros y directorios que facilita el desarrollo de la
aplicacin. El explorador de soluciones muestra esta
estructura (Figura 2). Encontramos una carpeta para
propiedades, otra para referencias a DLLs y ficheros
lgicos de aplicacin que pueden desplegarse en uno o
ms ficheros fsicos.
Los ficheros App.* contienen el punto de entrada a la
aplicacin y definen/construyen una instancia de la clase
Application. Los ficheros MainWindow.* definen la ventana
principal de la aplicacin y App.* contiene una indicacin de
que MainWindow.* es la ventana principal o URI de inicio.
Los ficheros que nos interesan tienen extensin .xaml o
.cs. Los primeros son ficheros en un formato XML
denominado XAML (eXtensible Application Markup
Language); los segundos son ficheros C#.

Figura 2. Explorador de soluciones con el


proyecto WPF recin creado.

XAML sigue un esquema XML cuyos elementos se inscriben en el espacio de nombres


{http://schemas.microsoft.com/winfx/2006/xaml}. XAML es un lenguaje de marcado diseado para
facilitar la instanciacin de objetos .NET y la definicin de sus propiedades (o atributos, en jerga XML).
Hay una versin de XAML con un esquema cuyos elementos estn en el espacio de nombres

Primeros pasos con WPF

onstruyamos una aplicacin WPF extremadamente sencilla para empezar a entender algunos de los

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

{http://schemas.microsoft.com/winfx/2006/xaml} que permite instanciar objetos WPF y definir sus


propiedades.
XML es un formato jerrquico y, por tanto, los objetos en XAML siempre forman un rbol. Esa
estructura es natural en una interfaz de usuario. Veamos el aspecto de App.xaml:
<Application x:Class="HolaMundo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
El fichero instancia un objeto de la clase App (que es nuestra y hereda de otra, Application, propia de
WPF) en el espacio de nombres C# HolaMundo (lo indica el atributo x:Class). Define los dos espacios de
nombres XML que se usan: XAML y WPF y seala que la aplicacin empieza en la URI
MainWindow.xaml. A continuacin, define una seccin para los recursos de la aplicacin, pero no los
hay. La sintaxis es especial y merece que nos detengamos: la marca Application.Resources corresponde,
en realidad, a un atributo Resources de la marca Application. Es decir, en principio (pero slo en
principio), podramos haber encontrado algo de este estilo:
<Application x:Class="HolaMundo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
Resources="">
</Application>
Por qu se usa esa otra sintaxis, extraa en el mundo XML (aunque siga el estndar)? Porque as es
posible que el atributo Resources contenga nuevos elementos XML y no slo una simple cadena. Esta
sintaxis alternativa es muy corriente en los ficheros XAML y conviene acostumbrarse a ella.
El fichero App.xaml.cs no contiene gran cosa:
System;
System.Collections.Generic;
System.Configuration;
System.Data;
System.Linq;
System.Windows;

namespace HolaMundo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

Primeros pasos con WPF

using
using
using
using
using
using

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

La clase App, en el espacio de nombres HolaMundo, es una clase parcial (adjetivo partial), lo que
significa que parte de su cdigo est definido en otro fichero. Ese otro fichero contiene el cdigo autogenerado por VS 2010.
La clase est vaca (en nuestro fichero, pero no en el auto-generado). En nuestra parte podramos
definir, por ejemplo, comportamientos relacionados con el ciclo de vida de la aplicacin (creacin,
activacin, minimizacin, cierre, etctera).
Veamos ahora qu contiene MainWindow.xaml:
<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Se crea una instancia de un objeto de la clase MainWindow, que por herencia es tambin de la clase
Window. Se definen algunos atributos: el ttulo (Title), la altura (Height) y la anchura (Width). Las
unidades en que se indican las medidas son independientes de la pantalla. Una pulgada corresponde
siempre a 96 unidades. (Se escogi esta unidad de medida por facilitar la medida en monitores
convencionales, que suelen presentar una resolucin de 96 dpi, es decir, 96 puntos por pulgada.)
Dentro de la ventana hay un Grid. Es un elemento de maquetacin. Ms adelante lo estudiaremos con
detenimiento.
Ejecutemos la aplicacin (pulsamos la tecla F5) y aparece una ventana vaca (Figura 3). VS 2010 sigue
estando accesible, pero no permite editar el contenido del proyecto. Cerramos la aplicacin pulsando
en el botn de cierre de la ventana (o pulsando Shift-F5 en VS 2010) y volvemos a VS 2010.

Botones y eventos
con un saludo:
<Window x:Class="HolaMundo.MainWindow"

Title="MainWindow"
Height="350" Width="525">
<Grid>
<Button Click="Button_Click">
Saluda
</Button>
</Grid>
</Window>
El texto Saluda es el valor que se asigna a una
propiedad por defecto: Content. Es decir, este
cdigo XAML es equivalente:

Figura 3. Ventana de la aplicacin en ejecucin.

Botones y eventos

camos a crear un botn con el texto Saluda y haremos que al pulsarlo aparezca una ventana modal

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Saluda" Click="Button_Click" />
</Grid>
</Window>
Algunos objetos tienen campos privilegiados en tanto que el contenido de la marca XML (el texto o
cdigo XML que va entre las marcas de apertura y cierre) se les asigna automticamente. En un botn,
el campo especial es Content, en una caja de texto, el campo especial es Text.
El atributo Click corresponde a un evento. Cada vez que se pulse en el elemento de tipo Button con el
botn izquierdo del ratn y se levante en el interior del elemento, se invocar automticamente al
mtodo Button_Click. El asistente de VS 2010 nos ha preparado el mtodo Button_Click en
MainWindow.xaml.cs:
using
using
using
using
using
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Windows;
System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;
System.Windows.Navigation;
System.Windows.Shapes;

namespace HolaMundo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
El parmetro sender de Button_Click contendr una referencia al propio botn y el parmetro de tipo
RoutedEventArgs contendr ciertos datos relativos al evento cuando ste ocurra. Vamos a rellenar el
mtodo con la invocacin al dilogo modal:
private void Button_Click(object sender, RoutedEventArgs e)
{

Botones y eventos

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

MessageBox.Show("Hola");
}
El cdigo que acompaa al XAML se denomina cdigo trasero (code behind). Y aunque ahora
recurrimos a l, veremos que MVVM permite eliminar buena parte de l (si no todo).
Ejecutemos la aplicacin (F5). El botn ocupa toda el rea de trabajo (Figura 5a). Al pulsar el botn
aparece la ventana modal que bloquea el acceso a la ventana MainWindow (Figura 5b).

(a)

(b)

Figura 4. (a) Ventana con el botn Saluda, que ocupa toda la superficie de la ventana. Ventana de dilogo
modal que aparece al ejecutar el mtodo Button_Click.

Maquetacin con paneles


texto:
<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Label>Nombre:</Label>
<TextBox></TextBox>
<Button Click="Button_Click">Saluda</Button>
</Grid>
</Window>

En la ventana slo vemos el ltimo elemento. En realidad estn


todos, pero uno encima del otro (vase la Figura 5). Es cosa del
elemento Grid, que dejamos para luego por ser complejo: si dos o
ms elementos estn en la misma celda de un Grid, se
superponen.

Figura 5. Los elementos se tapan


unos a otros en el Grid, por lo que
slo se ve el botn, que est encima
del todo.

Maquetacin con paneles

eamos qu ocurre si tratamos de aadir elementos grficos nuevos, como una etiqueta o una caja para

Desarrollo de Aplicaciones con .NET y WPF


Empezamos por un elemento de maquetacin ms sencillo:
StackPanel. Un StackPanel apila vertical u horizontalmente sus
elementos.
<StackPanel>
<Label>Nombre:</Label>
<TextBox></TextBox>
<Button Click="Button_Click">
Saluda
</Button>
</StackPanel>

Andrs Marzal
Propiedades
Los elementos XAML tienen
numerosos atributos y al principio
cuesta un poco manejarse con
tantos. Puede venir bien invocar el
panel de edicin de atributos. Con el
cursor en el elemento XAML cuyos
atributos se desea editar, aparece un
panel de propiedades al pulsar F4.

La Figura 6 muestra el resultado de la nueva disposicin de


elementos en el StackPanel: uno sobre el otro, por orden de
aparicin en el fichero XAML.
Cada elemento WPF contiene decenas de atributos. Podemos
recurrir a un formulario para asignar valores distintos de los
por defecto, pero lo cierto es que a la larga resulta
conveniente usar el editor de XAML.
Hay ms elementos de maquetacin y se pueden incorporar
otros definidos por programadores. Los paneles que vienen de
serie son:

Grid: distribucin de elementos en una tabla, con la


posibilidad de fundir filas y columnas.

StackPanel: distribucin de elementos en sucesin


vertical u horizontal.

DockPanel: distribucin de elementos con anclaje a


punto cardinal y posible expansin del ltimo al rea

sobrante.

No obstante, a la larga es ms

WrapPanel: distribucin de elementos en sucesin

productivo usar el editor de XAML en

vertical u horizontal en lneas (como el texto, que

VS 2010, que asiste al programador

fluye de una lnea a la siguiente).

con los mens Intellisense.

UniformGrid: distribucin de elementos en una matriz


cuadrada.

Canvas: ubicacin precisa de elementos.

(Algunos paneles definidos por programadores disponen


elementos grficos de formas novedosas u ofrecen
elementos. Se pueden encontrar ejemplos en
http://www.codeproject.com/Articles/37348/CreatingCustom-Panels-In-WPF.aspx,
http://www.wpftutorial.net/CustomLayoutPanel.html o
http://www.codeproject.com/KB/WPF/Panels.aspx.)

Figura 6. Los tres elementos se muestran


uno sobre otro gracias al StackPanel.

Maquetacin con paneles

animaciones para el desplazamiento o seleccin de sus

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Las maquetas ms complejas suelen formarse combinando diferentes paneles. En nuestro caso,
podemos crear un StackPanel vertical en el que apilar dos StackPanels adicionales, estos horizontales, y
el botn.
<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Nombre:</Label>
<TextBox></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Apellido:</Label>
<TextBox></TextBox>
</StackPanel>
<Button Click="Button_Click">Saluda</Button>
</StackPanel>
</Window>
La ventana que hemos creado tiene espacio muerto bajo el botn Saluda. Esto es as porque hemos
creado la ventana con unas dimensiones fijas: 525 por 350 puntos. El problema es fcil de corregir:
<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525"
SizeToContent="Height">
El atributo SizeToContent puede tomar los valores
Manual (que es el que toma por defecto), Height,
Width y WidthAndHeight. El efecto de seleccionar
Height para SizeToContent es una ventana con

(a)

anchura fija y altura ajustada al contenido de la


ventana (Figura 7a).
Hay un par de problemas adicionales. Por una
parte, los campos de texto son pequeos (aunque
crecen automticamente conforme tecleamos
texto en ellos); por otra, el alineamiento de los
campos de texto no es perfecto y depende del

(b)
Figura 7. (a) La ventana con altura ajustada a su
contenido. (b) Efecto de mal alineamiento cuando las
etiquetas tienen texto de diferentes longitudes.

tamao de las etiquetas (Figura 7b).

solucionar los dos problemas. Un Grid consta de


una seccin de declaracin de filas y columnas. En
nuestro caso definiremos 3 filas y 2 columnas. La
primera fila contendr la etiqueta Nombre: y su
campo de texto; la segunda, la etiqueta Primer
apellido: y su campo de texto; y la tercera, el
botn Saluda. Las etiquetas se dispondrn en la

Figura 8. Ventana con espacio indeseado.

Maquetacin con paneles

El elemento de maquetacin Grid permite

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

primera columna y los campos de texto en la segunda. El botn Saluda ocupar una columna que ser
resultado de fundir las dos. La primera columna se ajustar al contenido y la segunda ocupar el resto
del espacio. Nuestra tabla presentar, pues, esta estructura:
Etiqueta Campo de texto ---------------------------------------------------------------------------------Etiqueta Campo de texto ------------------------------------------------------------------------------------------------------------------------------------------Botn -----------------------------------------------El cdigo XAML se complica un poco:
<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525" SizeToContent="Height">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0">Nombre:</Label>
<TextBox Grid.Column="1" Grid.Row="0"></TextBox>
<Label Grid.Column="0" Grid.Row="1">Primer apellido:</Label>
<TextBox Grid.Column="1" Grid.Row="1"></TextBox>
<Button Grid.Row="2" Grid.ColumnSpan="2" Click="Button_Click">
Saluda
</Button>
</Grid>
</Window>
Por fin vemos lo conveniente de fijar atributos con la sintaxis Elemento.Atributo: no es apropiado definir
los atributos RowDefinitions y ColumnDefinitions con una simple cadena de texto, pues son en realidad
listas de elementos XML, algunos con sus propios atributos.
Hay un nuevo elemento sintctico. Hay atributos (no elementos, como antes) con la sintaxis
Elemento.Atributo. Examinemos, por ejemplo, esta lnea:
<Label Grid.Column="0" Grid.Row="1">Primer apellido:</Label>
El atributo Grid.Column permite asignar un valor a una propiedad Column definida en Grid, no en Label.
propiedad de Grid. Los elementos WPF mantienen un diccionario que permite asociar valores a claves
(propiedades) de las que nada sabe. En este caso, esas propiedades permiten ubicar el elemento en una
fila/columna del Grid.
Si nosotros definisemos un panel propio, digamos que con una clase ClockPanel, en el que hubiese
que ubicar los elementos alrededor de una circunferencia, por ejemplo, necesitaramos que cada

Maquetacin con paneles

Los elementos de tipo Label no saben nada de los de tipo Grid y, aun as, pueden asociar un valor a una

10

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

elemento especificase los grados en los que debe aparecer en la esfera del reloj. Podramos, entonces,
usar una etiqueta como sta:
<Label ClockPanel.Angle="90">Primer apellido:</Label>
Ntese que Label no sabe nada de ClockPanel (de hecho, ClockPanel es una invencin nuestra y, de
momento, ni siquiera existe). Pese a ello, podemos gestionar atributos de este tipo, que reciben el
nombre de propiedades pegadas (attached properties).

Contenido rico, diseo grfico y animaciones


grfica de usuario hay serias limitaciones al contenido de los elementos. En algunas, los botones slo
pueden contener, por ejemplo, texto y, opcionalmente, un icono. Escapar de esta restriccin, cuando es
posible, obliga a construir nuevos elementos, lo que supone un incremento de complejidad enorme.
WPF, sin embargo, permite que muchos componentes contengan a otros componentes en su interior, lo
que facilita el diseo de aplicaciones con un acabado grfico espectacular (si se trabaja codo con codo
con diseadores grficos, claro est).
Podemos probar a aadir un smiley al botn Saluda. Lo haremos con ayuda de Miorosoft Expression
Blend. Arrancamos Blend y desde su men FileOpen Project/Solution abrimos el proyecto VS 2010
HolaMundo y nos encontramos con la aplicacin como se muestra en la Figura 9. La interfaz de Blend es
bastante compleja y no la analizaremos en detalle. Slo queremos llevarnos una impresin acerca de su
uso y ver que es una herramienta especializada en adaptar la apariencia de nuestra aplicacin, y no en
la lgica.
Podemos editar cdigo XAML desde Microsoft
Expression Blend del mismo modo que hacemos
con VS 2010. Basta con pulsar el icono <> que
hay en la parte superior derecha del panel central
(el que contiene la ventana de nuestra aplicacin).
Con el editor XAML de Microsoft Expression Blend
hemos escrito este texto en el fichero de texto
MainWindow.xaml, es decir, en el mismo fichero
que hemos estado editando en Visual Studio y
que forma parte del proyecto HolaMundo:

Figura 9. Microsoft Expression Blend 4 tras abrir el


proyecto HolaMundo.

<Window x:Class="HolaMundo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="525" SizeToContent="Height">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>

Contenido rico, diseo grfico y animaciones

PF sigue un modelo de contenido rico. En muchos sistemas de construccin de aplicaciones con interfaz

11

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Nombre:"/>
<TextBox Grid.Column="1" Grid.Row="0"/>
<Label Grid.Column="0" Grid.Row="1" Content="Primer apellido:"/>
<TextBox Grid.Column="1" Grid.Row="1"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Click="Button_Click">
<StackPanel>
<TextBlock TextAlignment="Center">Saluda</TextBlock>
<Canvas Width="64" Height="64">
</Canvas>
</StackPanel>
</Button>
</Grid>
</Window>
El botn contiene ahora un StackPanel con dos elementos: un bloque de texto y un panel de tipo
Canvas de tamao 64x64. Ah dibujaremos el smiley. Con ayuda de la paleta de herramientas y el panel
de propiedades creamos el grfico que se muestra en la Figura 10.
Blend permite crear efectos grficos y animaciones. Vamos a
hacer que cuando el ratn entre en la regin del botn el
smiley d una vuelta.
Empezamos creando una animacin. Seleccionamos el
Canvas y en el icono + del panel Objects and Timeline
seleccionamos New y creamos una historia (storyboard) a

Figura 10. Botn con dibujo creado con el


editor de Blend.

la que denominamos ZoomStoryboard. Con la marca de


tiempo (lnea amarilla) en el instante 0, fijamos a 0 la propiedad Angle en el panel Transform del Canvas
que contiene al smiley, y fijamos a 360 su valor en el instante 1. Con eso conseguiremos que el smiley
d una vuelta completa en un segundo.
Seleccionamos ahora el botn Saluda y seleccionamos el activo de tipo Behaviors denominado
ControlStoryBoardAction. En su panel, seleccionamos la historia ZoomStoryboard y el evento MouseEnter.
Podemos probar a ejecutar la aplicacin y comprobar que cada vez que el cursor entra en el botn, se
ejecuta la animacin. Bueno, no slo entonces: tambin se dispara cuando cargamos la aplicacin.
desde VS 2010).
Todo lo que hemos hecho con Blend se podra haber hecho directamente con VS 2010, pero hubiese
supuesto un esfuerzo considerablemente mayor, como comprobaremos en breve al analizar el XAML
generado.

Contenido rico, diseo grfico y animaciones

Luego eliminaremos este efecto indeseado (que aunque podemos eliminar desde Blend, eliminaremos

12

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Ms sobre XAML: recursos, disparadores, transformaciones y


referencias
el proyecto y solicita, por tanto, recargarlo.
Analicemos el XAML que se ha generado desde Blend:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="HolaMundo.MainWindow"
Title="MainWindow" Width="525" SizeToContent="Height">
<Window.Resources>
<Storyboard x:Key="ZoomStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateT
ransform.Angle)" Storyboard.TargetName="canvas">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource ZoomStoryboard}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Nombre:"/>
<TextBox Grid.Column="1" Grid.Row="0"/>
<Label Grid.Column="0" Grid.Row="1" Content="Primer apellido:"/>
<TextBox Grid.Column="1" Grid.Row="1"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Click="Button_Click">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<ei:ControlStoryboardAction
Storyboard="{StaticResource ZoomStoryboard}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<StackPanel>
<TextBlock TextAlignment="Center"><Run Text="Saluda"/></TextBlock>
<Canvas x:Name="canvas" Width="64" Height="64"
RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>

Ms sobre XAML: recursos, disparadores, transformaciones y referencias

s hora de volver a VS 2010. Cerramos Blend y volvemos a VS 2010, que detectar que hubo cambios en

13

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

</TransformGroup>
</Canvas.RenderTransform>
<Ellipse Fill="#FFFDFF00" Height="48" Canvas.Left="8"
Stroke="Black" Canvas.Top="8" Width="48"/>
<Ellipse Fill="#FFFF1700" Height="7.5" Canvas.Left="20"
Stroke="Black" Canvas.Top="21.12" Width="7.5"/>
<Ellipse Fill="#FFFF1700" Height="7.5" Canvas.Left="36"
Stroke="Black" Canvas.Top="21.12" Width="7.5"/>
<ed:Arc ArcThickness="0" ArcThicknessUnit="Pixel" EndAngle="-90"
Fill="#FFF4F4F5" Height="12" Canvas.Left="20"
Stretch="None" Stroke="Black" StartAngle="90"
Canvas.Top="36.12" Width="23.5"/>
</Canvas>
</StackPanel>
</Button>
</Grid>
</Window>

Complejo. Pero podemos analizar su contenido y comprobar que todo lo hecho con Blend, acaba
codificndose como texto en el fichero XAML.
Por una parte tenemos una seccin de recursos, que es cdigo XAML asignado a la propiedad Resources

<Window.Resources>
<Storyboard x:Key="ZoomStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2]
.(RotateTransform.Angle)" Storyboard.TargetName="canvas">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
La seccin de recursos es un diccionario que permite asociar elementos WPF a claves. La historia es que
hemos creado tiene por clave ZoomStoryboard y por valor una instancia de la clase
DoubleAnimationUsingKeyFrames. Las animaciones en WPF se forman con elementos simples. Una
DoubleAnimation, por ejemplo, es una animacin consistente en el cambio de un valor de tipo de
double a lo largo del tiempo. Si vincuamos el valor de una propiedad de un elemento de la interfaz
grfica a esa animacin (como la escala, la altura, la opacidad), su valor afectar al aspecto visual de
ese elemento. Una DoubleAnimationUsingKeyFrames es eso mismo, pero fijando tramas clave (key
frames) en las que el double debe tomar ciertos valores. En nuestro caso, en el instante 0 debe tener
valor 0 y en el instante 0:0:1 (un segundo despus), debe valer 360. La animacin tiene efecto sobre una
propiedad del Canvas en el que hemos puesto el smiley: el ngulo de rotacin.
Despus de la seccin de recursos hay otra con disparadores (triggers). Los disparadores permiten
asociar acciones a eventos (entre otras cosas).
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource ZoomStoryboard}"/>
</EventTrigger>
</Window.Triggers>

Ms sobre XAML: recursos, disparadores, transformaciones y referencias

de Window.

14

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Este disparador es el responsable de que tan pronto se carga la ventana se inicie la animacin
ZoomStoryboard. Los valores de atributos entre llaves son especiales. En este caso se indica que la
historia que debe ejecutarse se encuentra almacenada en el diccionario de recursos con la clave
ZoomStoryboard. Si eliminamos ese disparador (o, para el caso, su seccin completa), eliminaremos la
animacin indeseada.
Seguimos analizando el XAML. El botn tiene este cdigo que fija el valor de algunas propiedades
pegadas:
<Button Grid.Row="2" Grid.ColumnSpan="2" Click="Button_Click">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<ei:ControlStoryboardAction
Storyboard="{StaticResource ZoomStoryboard}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Los elementos en los espacios de nombres con sufijos i y ei son propios de Microsoft Expression Blend.
No entramos en detalles. Baste saber que los comportamientos (behaviors) que podemos fijar en Blend
se han incrustado en el XAML as.
El botn contiene un StackPanel y su primer elemento es un TextBlock, un bloque de texto. Su nico
componente es una instancia de Run.
<TextBlock TextAlignment="Center"><Run Text="Saluda"/></TextBlock>
Hay varios tipos de elemento que podemos poner en un TextBlock y entre ellos destacamos Run (texto
normal), Italic (texto en cursiva) y Bold (texto en negrita). Pero no son los nicos. Y si ponemos texto a
infraestructura de WPF nos permite eliminar algo de verbosidad en el cdigo XAML (que an as es muy
verboso).
Y llegamos por fin al Canvas:
<Canvas x:Name="canvas" Width="64" Height="64" RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<Ellipse Fill="#FFFDFF00" Height="48" Canvas.Left="8"
Stroke="Black" Canvas.Top="8" Width="48"/>
<Ellipse Fill="#FFFF1700" Height="7.5" Canvas.Left="20"
Stroke="Black" Canvas.Top="21.12" Width="7.5"/>
<Ellipse Fill="#FFFF1700" Height="7.5" Canvas.Left="36"
Stroke="Black" Canvas.Top="21.12" Width="7.5"/>
<ed:Arc ArcThickness="0" ArcThicknessUnit="Pixel" EndAngle="-90"
Fill="#FFF4F4F5" Height="12" Canvas.Left="20" Stretch="None"
Stroke="Black" StartAngle="90" Canvas.Top="36.12" Width="23.5"/>
</Canvas>

Ms sobre XAML: recursos, disparadores, transformaciones y referencias

pelo, WPF sabe que queramos poner una marca Run y la pone por nosotros. Mucha de la

15

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Hay un atributo interesante con identificador x:Canvas. Permite asociar un nombre a un componente y
as poder referenciarlo desde otros puntos. De hecho, ya hemos referenciado a ste desde uno recurso:
<Window.Resources>
<Storyboard x:Key="ZoomStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2]
.(RotateTransform.Angle)"
Storyboard.TargetName="canvas">
As es como aplica la historia a un componente concreto: fijando como valor del objetivo el
identificador o nombre del elemento. En el Canvas hay un grupo de elementos asignados al atributo
RenderTransform: con componentes que aplican una transformacin afn a un elemento en el momento
de visualizarse. La expresin
(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)
Estaba seleccionando el tercer componente (ndice 2) del grupo de transformacin, es decir, la rotacin,
y centrando el inters en la propiedad Angle, que corresponde al ngulo. La rotacin tiene lugar
centrada en el centro del Canvas gracias a este otro atributo:
<Canvas x:Name="canvas" Width="64" Height="64" RenderTransformOrigin="0.5,0.5">
El Canvas contiene tres elipses y un arco, elemento que forma parte de un espacio de nombres propio
de Microsoft Expression.
Cabe sealar otro aspecto interesante de los valores de ciertos atributos. En las elipses podemos ver
que hay varias formas de expresar un color:
<Ellipse Fill="#FFFDFF00" Height="48" Canvas.Left="8"
Stroke="Black" Canvas.Top="8" Width="48"/>
Una forma es con #AARRGGBB (alfa, rojo, verde, azul) y otra con el propio nombre del color. WPF sabe
interpretar apropiadamente el valor que se desea usar a partir de una cadena. Para ello usa un rico
juego de conversores. Con unidades de medida, por ejemplo, sabe que 48 es media pulgada, que
tambin podramos expresas con 0.5in. Y 1cm representa un centmeto, o lo que es lo mismo,
10mm.

esquina superior izquierda del rectngulo de inclusin de la elipse en el Canvas que la contiene. Ya
vimos algo parecido con Grid.Column y Grid.Row.

Acceso a propiedades desde cdigo trasero

amos a acceder desde cdigo trasero al valor que el usuario teclee en las cajas de texto. Para ello
necesitaremos poder acceder a las cajas de texto y debern tener un identificador. En el cdigo XAML
escribimos esto:
<Label Grid.Column="0" Grid.Row="0" Content="Nombre:"/>
<TextBox x:Name="nombre" Grid.Column="1" Grid.Row="0"/>
<Label Grid.Column="0" Grid.Row="1" Content="Primer apellido:"/>
<TextBox x:Name="apellido" Grid.Column="1" Grid.Row="1"/>

Acceso a propiedades desde cdigo trasero

Ah! Y fijmonos en los atributos Canvas.Left y Canvas.Top, que permite fijar las coordenadas X e Y de la

16

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Volvamos al cdigo trasero. En particular, al mtodo que se invoca al pulsar el botn. Hagamos que el
mtodo quede as:
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hola, " + nombre.Text + " " + apellido.Text + "!");
}
Estamos accediendo a la propiedad Text de los elementos nombre y apellido. Decimos propiedad y
no campo porque Text no es un campo de tipo cadena, sino una propiedad C#. Las propiedades C#
son pares de mtodos que permiten acceder o asignar un valor a, en principio, un campo. Lo cierto es
que detrs puede haber un campo o no haberlo, pero el programador que usa la propiedad tiene la
ilusin de que lo hay. En realidad se ejecutar cdigo que podra calcular el valor que se quiere leer a
partir de uno o ms campos (o de ninguno). El acceso a la propiedad Text no es una mera consulta a un
campo: probablemente consista en el acceso a un diccionario y en la aplicacin de operaciones que nos
permitan ver el resultado como una cadena. Pero no hay de qu preocuparse: es todo tarea de WPF y
para nosotros se crea la ilusin de acceso a un simple campo.

Enlaces
que se introduce en el formulario. Para eso hemos de asignar un nombre a la ventana y crear un enlace
(binding) que ligue su valor al del campo de texto que deseemos (y que ha de tener un nombre):
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

Title="{Binding ElementName=nombre, Path=Text}"


Width="525" SizeToContent="Height">
El enlace indica que hemos vincular el valor del campo Title de la ventana al del campo Text del
elemento llamado nombre. La sintaxis de las llaves puede reemplazarse por esta otra:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Width="525" SizeToContent="Height">
<Window.Title>
<Binding ElementName="nombre" Path="Text"/>
</Window.Title>
Al ejecutar la aplicacin podemos
comprobar que ttulo de ventana y
contenido de la caja de texto con el
nombre coinciden en todo
momento. Conforme tecleamos los
caracteres del nombre, el ttulo de la
ventana se va actualizando.
Figura 11. El ttulo de la ventana est vinculado al contenido de la
primera caja de texto.

Enlaces

odemos enlazar diferentes elementos grficos. Probemos a asociar el ttulo de la ventana con el nombre

17

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Vistas y modelos (o casi)


representacin de un objeto (la vista) y el propio objeto (el modelo). De hecho, lo ideal es que el
modelo dependa lo menos posible de la interfaz grfica. Vamos a hacerlo ahora en nuestra aplicacin,
aunque resultar un tanto impostado por ser sta muy sencilla.
Creamos una nueva clase Persona. En el men contextual del proyecto HolaMundo seleccionamos
AddClass y creamos la clase Persona, que pasa a definirse as:
using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.ComponentModel;

namespace HolaMundo
{
class Persona : INotifyPropertyChanged
{
private string _nombre;
private string _apellido;
public string NombreCompleto
{
get { return Nombre + " " + Apellido; }
}
public string Nombre
{
get { return _nombre; }
set
{
if (value != _nombre)
{
_nombre = value;
NotifyChange("Nombre");
NotifyChange("NombreCompleto");
}
}
}
public string Apellido
{
get { return _apellido; }
set
{
if (value != _apellido)
{
_apellido = value;
NotifyChange("Apellido");
NotifyChange("NombreCompleto");
}
}
}
void NotifyChange(string id)

Vistas y modelos (o casi)

o usual en una aplicacin interactiva bien diseada es que haya una clara separacin entre la

18

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(id));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
La idea ahora es que el campo Nombre y el campo Apellido de una instancia de Persona estn siempre
sincronizados con las cajas de texto correspondientes en el formulario. Hemos creado cierta
infraestructura al efecto. Por una parte, Persona implementa la interfaz INotifyPropertyChanged. La
interfaz obliga a que se implemente un evento que se disparar cada vez que alguien modifique el valor
de una propiedad, avisando as a los suscriptores del evento. Ntese que el cambio del nombre o el
apellido no slo cambia Nombre y Apellido, respectivamente: tambin cambia NombreCompleto.
Hemos de crear una instancia de Persona e indicar que el contexto de datos (DataContext) de la
ventana principal es esa instancia:
using
using
using
using
using
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Windows;
System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;
System.Windows.Navigation;
System.Windows.Shapes;

public MainWindow()
{
InitializeComponent();
LaPersona = new Persona {
Nombre = "Tu nombre",
Apellido = "Tu apellido"
};
DataContext = LaPersona;
}
private void Button_Click(object sender, RoutedEventArgs e)

Vistas y modelos (o casi)

namespace HolaMundo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public readonly Persona LaPersona;

19

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

{
MessageBox.Show("Hola, " + LaPersona.NombreCompleto + "!");
}
}
}
Y ahora, vinculemos el contenido de las cajas de texto con los de LaPersona. De paso, vincularemos el
ttulo con el nombre completo:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="HolaMundo.MainWindow"
Width="525" SizeToContent="Height">
<Window.Title>
<Binding Path="NombreCompleto"/>
</Window.Title>

<Label Grid.Column="0" Grid.Row="0" Content="Nombre:"/>


<TextBox x:Name="nombre" Grid.Column="1" Grid.Row="0" Text="{Binding Nombre}"/>
<Label Grid.Column="0" Grid.Row="1" Content="Primer apellido:"/>
<TextBox x:Name="apellido" Grid.Column="1" Grid.Row="1" Text="{Binding Apellido}"/>

Y ya est. Nuestra ventana tiene por ttulo el nombre completo y la instancia LaPersona siempre est
sincronizada con el formulario.

Propiedades de dependencia
los cambios que experimentan: las propiedades de dependencia. De hecho, las propiedades de los
elementos WPF son realmente propiedades de dependencia. Estas propiedades no slo notifican de los
cambios que experimentan: tienen valores por defecto, se pueden heredar sus valores en la jerarqua de
objetos, se pueden ligar a otras propiedades de dependencia, pueden usarse en animaciones y, lo que
quiz es ms importante: no consumen memoria si no se les asigna un valor. Las propiedades de
dependencia se almacenan en un diccionario cuando se les asigna un valor. Si no lo tienen, WPF se
encarga de acceder al valor por defecto automticamente. Se trata de un factor muy importante si
tenemos en cuenta que un elemento WPF puede tener ms de medio centenar de propiedades.
Convirtamos nuestra persona en un objeto con propiedades de dependencia y, de momento, olvidemos
la sincronizacin del ttulo de la ventana con el nombre completo. Ms tarde recuperaremos esa
funcionalidad.
La nueva definicin de Persona es sta:
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;
System.ComponentModel;
System.Windows;

Propiedades de dependencia

PF ofrece una herramienta muy interesante para crear propiedades que notifican automticamente de

20

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

namespace HolaMundo
{
public class Persona : DependencyObject
{
public string NombreCompleto
{
get { return Nombre + " " + Apellido; }
}
public string Nombre
{
get { return (string)GetValue(NombreProperty); }
set { SetValue(NombreProperty, value); }
}
public static readonly DependencyProperty NombreProperty =
DependencyProperty.Register("Nombre", typeof(string),
typeof(Persona), new UIPropertyMetadata(""));
public string Apellido
{
get { return (string)GetValue(ApellidoProperty); }
set { SetValue(ApellidoProperty, value); }
}
public static readonly DependencyProperty ApellidoProperty =
DependencyProperty.Register("Apellido", typeof(string),
typeof(Persona), new UIPropertyMetadata(""));
}
}
Complicado, no? Afortunadamente VS 2010 nos ayuda con los denominados snippets, plantillas con
fragmentos de cdigo fcilmente utilizables. Si tecleamos propdp (por property: dependency property),
el editor nos ofrece una plantilla como sta:
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int),
typeof(ownerclass), new UIPropertyMetadata(0));
Con la ayuda del tabulador podemos asignar valor a los cuatro campos de la plantilla (que aparecen

La clase Persona hereda de DependencyObject, clase que ofrece la infraestructura necesaria para
soportar propiedades de dependencia. Una propiedad de dependencia es un objeto esttico que se
registra en un diccionario con el mtodo DependencyProperty.Register. Las instancias de un
DependencyObject pueden acceder al valor de su propiedad de dependencia con el mtodo GetValue,y
asignarle un valor con SetValue (ambos heredados de DependencyObject). La propiedad C# que da
acceso a estos mtodos nos facilita el acceso a su lgica.

Propiedades de dependencia

con fondo verde en este documento).

21

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

No hemos tenido que notificar los cambios porque las propiedades de dependencia ya se ocupan de
ello automticamente. Como NombreCompleto no es una propiedad de dependencia y ya no notifica de
los cambios, hemos perdido esa funcionalidad. No costara nada recuperarla volviendo a implementar el
notificador de cambios en propiedades. Pero es mejor que pasemos a hablar de un patrn de diseo
importante en el mundo WPF: el patrn Modelo-Vista-Modelo de la Vista, o MVVM, por Model-ViewViewModel.

Una introduccin al patrn arquitectnico MVVM


para ajustarlo a la vista hasta el punto de construirlo con componentes de WPF, y eso es muy mala
prctica. Normalmente el modelo nos viene dado y tenemos poca capacidad de influencia sobre l.
Este problema es crucial en el diseo de aplicaciones
interactivas, salvo en las ms triviales. Desde el inicio de la
programacin de aplicaciones interactivas se plantearon la
cuestin de la separacin de responsabilidades en este tipo
de sistemas. Un patrn exitoso es el conocido como
Modelo-Vista-Controlador o MVC, por Model-ViewController, que divide el sistema en tres componentes: el
modelo, la vista y el controlador. Podemos representar
grficamente el concepto como se muestra en la Figura 12.
Un patrn de diseo, como MVC, no es una receta estructa
acerca de cmo implementar cierta funcionalidad, sino una
serie de criterios que deben considerarse al disear la
arquitectura de la aplicacin e implementarla.

Figura 12. Diagrama del patrn ModeloVista-Controlador. (Imagen extrada de


Wikipedia.)

El usuario interacta con dispositivos que hablan con un


controlador, el cual manipula un modelo (los datos) cuyo cambio fuerza la actualizacin de una vista,
que es lo que percibe el usuario. Hoy da el modelo presenta ciertas dificultades y se considera
superado por otros, como el denominado MVP, por Model-View-Presenter. Tambin es un patrn con
tres componentes. En este caso se trata del modelo, la vista y el presentador. Vista y modelo parece
claro lo que son, pero qu es el presentador? Es una capa entre la vista y el modelo. Cuando la vista
necesita algo del modelo, se lo solicita al presentador, que ofrece mtodos que hacen cmodo para la
vista el acceso a los datos relevantes del modelo. Supongamos que el modelo tiene la fecha de
nacimiento de una persona, pero no la edad. El presentador podra ofrecer un mtodo o propiedad
Edad que accediese a la fecha de nacimiento y al da actual para proporcionar el dato deseado. Y en
sentido inverso, cuando la vista necesita modificar el modelo, lo hace a travs del presentador, que sabe
cmo traducir elementos de la vista en datos del modelo. Tambin modelo y presentador interactan:
el presentador lee y escribe sus datos y cuando el modelo cambia espontneamente (es decir, por
eventos no relacionados con la interaccin con el usuario), notifica al presentador de los cambios y ste
se encarga de actualizar la vista.
El patrn arquitectnico MVVM es una versin especializada de MVP para WPF. Establece mecanismos
propios de WPF para la comunicacin Vista-Presentador (que aqu se denomina Modelo de la Vista). En
particular y limita las herramientas que podemos usar en cada capa.

Una introduccin al patrn arquitectnico MVVM

a separacin entre vista y modelo que hemos hecho no es buena. Hemos acabado por tocar el modelo

22

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

La vista es XAML 100% (o casi).

El modelo de la vista expone lgica va rdenes (de las que nos ocupamos en breve), expone el
modelo mediante ligaduras entre propiedades y propiedades de dependencia y mantiene
informacin de estado de la interaccin (pero slo de la interaccin).

El modelo mantiene los datos y sus operaciones, pero no lgica dependiente de cmo se usa.
(Si cambia espontneamente, implementa un notificador de cambios en propiedades.)

Ahora vamos a seguir el patrn MVVM para que la aplicacin vuelva a funcionar. Devolvamos el modelo
a una versin minimalista. El fichero Persona pasa a contener este texto:
using
using
using
using

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

namespace HolaMundo
{
public class Persona
{
public string Nombre { get; set; }
public string Apellido { get; set; }
public string NombreCompleto { get { return Nombre + " " + Apellido; } }
}
}
El modelo no sabe nada de WPF ni de cmo se usar en la aplicacin. Se limita a mantener un par de
datos. Podra tener, adems, operaciones para serializar el objeto, almacenarlo en disco, etctera.
Preparemos una clase para el modelo de la vista: MainWindowViewModel. Recordemos que su papel es
hacer de puente entre la vista y el modelo.
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.ComponentModel;

namespace HolaMundo
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public Persona Model;
public string Nombre
{
get { return Model.Nombre;}
set { Model.Nombre = value; NotifyChange("Nombre", "NombreCompleto"); }
}
public string Apellido
{
get { return Model.Apellido; }
set { Model.Apellido = value;
NotifyChange("Apellido", "NombreCompleto"); }
}

Una introduccin al patrn arquitectnico MVVM

using
using
using
using
using

23

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

public string NombreCompleto


{
get { return Model.Nombre + " " + Model.Apellido; }
}
void NotifyChange(params string[] ids)
{
if (PropertyChanged != null)
foreach (var id in ids)
PropertyChanged(this, new PropertyChangedEventArgs(id));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Hemos de preocuparnos ahora de vincular Vista, Modelo y Modelo de la Vista. Lo haremos modificando
en App.xaml el arranque de la aplicacin. Su contenido actual es ste:
<Application x:Class="HolaMundo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Y pasa a ser este otro:

</Application.Resources>
</Application>
El mtodo Application_Startup se definir en App.xaml.cs as:
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Configuration;
System.Data;
System.Linq;
System.Windows;
System.Windows.Input;

namespace HolaMundo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>

Una introduccin al patrn arquitectnico MVVM

<Application x:Class="HolaMundo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
<Application.Resources>

24

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

public partial class App : Application


{
private void Application_Startup(object sender, StartupEventArgs e)
{
var model = new Persona { Nombre = "Tu nombre",
Apellido = "Tu apellido" };
var viewModel = new MainWindowViewModel {Model = model};
var view = new MainWindow {DataContext = viewModel};
view.Show();
}
}
}
Hemos creado un modelo, que se almacena en el modelo de la vista tan pronto se crea, y hemos creado
una vista cuyo contexto de datos es el modelo de la vista. La ltima accin consiste en mostrar la vista,
que es la ventana principal.
Para que la aplicacin funciones hemos de eliminar las referencias a LaPersona o sus campos. Una de
ellas est en el mtodo Button_Click, asociado al evento Click del Button. Vamos a deshacernos de los
eventos, pues no son recomendables en una aplicacin MVVM.

rdenes: el patrn Command


eventos crean referencias entre objetos que pueden prolongar la vida de stos ms all de lo que el
programador supone. Es la causa principal de las fugas de memoria en aplicaciones .NET, por lo que
conviene tomar medidas profilcticas.
WPF soporta el patrn de diseo orden (command) que permite asociar lgica a acciones interactivas.
Y no slo eso: permite tambin habilitar o deshabilitar la interaccin de ciertos componentes en funcin
del estado de los datos.
En principio hemos de definir una clase que implemente la interfaz ICommand para cada orden del
sistema. Pero resulta ms cmodo usar una clase nica a la que suministrar, mediante delegados o
funciones annimas, la lgica que deseamos. Esta clase suele denominarse RelayCommand o
DelegateCommand. Esta es nuestra versin:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}

rdenes: el patrn Command

l uso de eventos no es, en general, recomendable en aplicaciones de tamao moderado o grande. Los

25

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

public RelayCommand(Action<object> execute, Predicate<object> canExecute)


{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}

Antes de estudiarla, veamos cmo usarla. Creemos una orden que se dispare cuando pulsamos el botn
saluda. El lugar natural para las rdenes es el modelo de la vista. Este es el cdigo que le corresponde:
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.ComponentModel;
System.Windows;
System.Windows.Input;

namespace HolaMundo
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public Persona Model;
private readonly ICommand _saludaCommand;
public ICommand SaludaCommand
{
get { return _saludaCommand; }
}
public MainWindowViewModel()

rdenes: el patrn Command

using
using
using
using
using
using
using

26

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

{
_saludaCommand = new RelayCommand(
o => MessageBox.Show("Hola, " + NombreCompleto + "!"),
o => !string.IsNullOrEmpty(NombreCompleto.Trim()));
}
public string Nombre
{
get { return Model.Nombre;}
set { Model.Nombre = value; NotifyChange("Nombre", "NombreCompleto"); }
}
public string Apellido
{
get { return Model.Apellido; }
set { Model.Apellido = value;
NotifyChange("Apellido", "NombreCompleto"); }
}
public string NombreCompleto
{
get { return Model.Nombre + " " + Model.Apellido; }
}
void NotifyChange(params string[] ids)
{
if (PropertyChanged != null)
foreach (var id in ids)
PropertyChanged(this, new PropertyChangedEventArgs(id));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Al construir el RelayCommand proporcionamos dos funciones (annimas en este caso): una que indica
qu hacer y la otra qu nos permite saber si la orden puede ejecutarse. En nuestro caso, el qu hacer es
lo de siempre: mostrar el dilogo modal. Y la orden podr ejecutarse siempre que el nombre completo
contenga algn carcter no blanco.
El ICommand que se ejecutar al pulsar el botn se expone como propiedad de slo lectura. Falta

<Button Grid.Row="2" Grid.ColumnSpan="2" Command="{Binding SaludaCommand}">


Y ya est. Ejecutemos la aplicacin y
comprobemos que el botn funciona.
Y comprobemos tambin que cuando
no hay texto en las cajas de texto no
podemos pulsar el botn, como se
muestra en la Figura 13.
Figura 13. El botn aparece deshabilitado porque no hay texto en
las cajas.

rdenes: el patrn Command

vincular la orden a la pulsacin del botn en MainWindow.xaml:

27

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Podra pensarse que no hemos ganado mucho con las rdenes, pero el patrn orden aisla
efectivamente la lgica de la vista y la encapsula en una entidad que rene la accin con un mtodo
que permite saber cundo es posible ejecutarla. Si ahora quisisemos que otros elementos (opciones de
men, gestos de teclados, etctera) disparasen esa misma lgica, bastara con asociarlos al mismo
RelayCommand. Todos esos elementos se activaran y desactivaran gracias al mtodo CanExecute y
dispararan, cuando fuera posible, la misma accin va Execute.

An hay ms, pero no para hoy


mostrar algunos elementos bsicos e introducir, aunque sea someramente, el patrn arquitectnico de
preferencia: MVVM. Entre lo que nos dejamos en el tintero:

Infinidad de controles.

Diferentes tipos de animaciones.

Estilos y plantillas que permiten personalizar prcticamente todo.

Gestin de colecciones que facilita la interaccin con listas o rboles de datos.

La conexin a fuentes de datos provenientes de bases de datos o XML.

El modelo de navegacin.

La versin empotrable en pginas web (Silverlight).

Los componentes multimedia.

Los efectos de bitmap.

Diseo de controles y paneles personalizados.

El trabajo con elementos 3D.

El diseo de pruebas unitarias para componentes MVVM.

Libreras de ayuda para el diseo de aplicaciones MVVM.

Uso de inyeccin de dependencias para facilitar la asociacin entre elementos de la trada


MVVM.

Extensiones para tinta, tacto, etctera.

Conversores.

Eventos y rdenes enrutadas.

Espero haber despertado la curiosidad por WPF y C# para que acudis ahora a las fuentes bibliogrficas
o los blogs temticos para aprender ms.

An hay ms, pero no para hoy

s imposible presentar todos los elementos que conforman WPF en una simple charla. He pretendido

28

Desarrollo de Aplicaciones con .NET y WPF

Andrs Marzal

Fuentes bibliogrficas recomendables


Windows Presentation Foundation Unleashed, de Adam Natham.
(Sacar una nueva edicin en breve para cubrir WPF 4.0.)

Programming WPF, 2 edicin, de Chris Sells.

Essential Windows Presentation Foundations, de Chris Anderson.

Applications = Code + Markup: A Guide to the Microsoft Windows


Presentation Foundation, de Charles Petzold.

Hay tambin varios blogs recomendables. Entre ellos, os cito estos:

http://blogs.msdn.com/llobo/

http://sachabarber.net/

http://joshsmithonwpf.wordpress.com/

http://blogs.msdn.com/jgoldb/default.aspx

http://houseofbilz.com/Default.aspx

http://jesseliberty.com/

Fuentes bibliogrficas recomendables

lo queda recomendar algunos libros que permiten profundizar en WPF.

29

También podría gustarte