Guia C# 01
Guia C# 01
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:
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:
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).
&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
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";
}
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++;
};
}
}
}
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);
}
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/");
}
}
}
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);
}
}
}
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/");
}
}
}
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/");
}
}
}
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.
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)
{
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.
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,
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/");
}
}
}
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);
}
}
}
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/");
}
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();
}
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/");
}
}
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/");
}
}
}
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/");
}
}
}
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;
}
}
}
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/");
}
}
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);
}
}
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;
}
}
}
}
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"); }
}
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;
}
}
}
}
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();
}
}
}
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)
{
}
}
}
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);
}
}
}
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/");
}
}
}
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
http://www.fsf.org/blogs/rms/microsoft-
codeplex-foundation,
?ltsn=2009-09-21-028-35-OP-CY-EV).
http://www.linuxtoday.com/news_story.php3
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.
Andrs Marzal
Andrs Marzal
(a)
(b)
Figura 1. (a) Pantalla inicial de Visual Studio 2010. (b) Cuadro de dilogo para crear un nuevo proyecto.
onstruyamos una aplicacin WPF extremadamente sencilla para empezar a entender algunos de los
Andrs Marzal
namespace HolaMundo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
using
using
using
using
using
using
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:
Botones y eventos
camos a crear un botn con el texto Saluda y haremos que al pulsarlo aparezca una ventana modal
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
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.
eamos qu ocurre si tratamos de aadir elementos grficos nuevos, como una etiqueta o una caja para
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.
sobrante.
No obstante, a la larga es ms
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)
(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.
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
Los elementos de tipo Label no saben nada de los de tipo Grid y, aun as, pueden asociar un valor a una
10
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).
<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"/>
PF sigue un modelo de contenido rico. En muchos sistemas de construccin de aplicaciones con interfaz
11
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
Luego eliminaremos este efecto indeseado (que aunque podemos eliminar desde Blend, eliminaremos
12
Andrs Marzal
s hora de volver a VS 2010. Cerramos Blend y volvemos a VS 2010, que detectar que hubo cambios en
13
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>
de Window.
14
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>
pelo, WPF sabe que queramos poner una marca Run y la pone por nosotros. Mucha de la
15
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.
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"/>
Ah! Y fijmonos en los atributos Canvas.Left y Canvas.Top, que permite fijar las coordenadas X e Y de la
16
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"
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
Andrs Marzal
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)
o usual en una aplicacin interactiva bien diseada es que haya una clara separacin entre la
18
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)
namespace HolaMundo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public readonly Persona LaPersona;
19
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>
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
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
21
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.
a separacin entre vista y modelo que hemos hecho no es buena. Hemos acabado por tocar el modelo
22
Andrs Marzal
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"); }
}
using
using
using
using
using
23
Andrs Marzal
</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>
<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
Andrs Marzal
l uso de eventos no es, en general, recomendable en aplicaciones de tamao moderado o grande. Los
25
Andrs Marzal
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()
using
using
using
using
using
using
using
26
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
27
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.
Infinidad de controles.
El modelo de navegacin.
Conversores.
Espero haber despertado la curiosidad por WPF y C# para que acudis ahora a las fuentes bibliogrficas
o los blogs temticos para aprender ms.
s imposible presentar todos los elementos que conforman WPF en una simple charla. He pretendido
28
Andrs Marzal
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/
29