domingo, 14 de junio de 2009

Data Binding WPF III

Cómo enlazar un Método :
El propósito del método es convertir Millas a Kilómetros. Recibe dos parámetros, un double especificando el valor de la distancia y un TipoDistancia especificando el tipo(Millas o Kilómetros).Retorna un String con el resultado de la conversión.
El metodo se llama Convertir y para eso he creado una clase llamada ConvertidorDistancia y también he creado la enumeración TipoDistancia:


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

namespace TestWPF_Data_Binding_II
{
public enum TipoDistancia
{
Millas,
Kilometros
}
public class ConvertidorDistancia
{
/// <summary>
/// Convierte Millas a Kilometros y viceversa.
/// </summary>
/// <param name="valorDist">Valor a convertir</param>
/// <param name="tipoDistancia">Tipo de Distancia</param>
/// <returns>Un string conteniendo el valor convertido</returns>
public string Convertir(double valorDist, TipoDistancia tipoDistancia)
{
if (tipoDistancia == TipoDistancia.Millas)
{
return (valorDist * 1.609344).ToString("0.##") + " km";
}
if (tipoDistancia == TipoDistancia.Kilometros)
{
return (valorDist * 0.621371192).ToString("0.##") + " m";
}
throw new ArgumentOutOfRangeException("tipoDistancia");
}

}

}


Para poder enlazar el método Convertir es necesario crear una instancia de la clase ConvertidorDistancia.Antes de hacerlo debemos agregar una referencia al espacio de nombre(namespace) donde se encuentra la Clase.En nuestro código Xaml sería la línea 4:
xmlns:local="clr-namespace:TestWPF_Data_Binding_II"

Donde local hace referencia al espacio de nombre TestWPF_Data_Binding_II.
Además he agregado system como referencia al espacio de nombres System(línea 5), pues ocuparé un objeto de tipo Double(línea 19).



   1:  <Window x:Class="TestWPF_Data_Binding_II.DataBinding_Method"

   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
"

   4:      xmlns:local="clr-namespace:TestWPF_Data_Binding_II"

   5:      xmlns:system="clr-namespace:System;assembly=mscorlib"

   6:      Title="DataBinding_Method" Height="300" Width="300">

   7:      <Window.Resources>

   8:          

   9:          <!-- Instancia de la clase DoubleToString que impl
ementa a IValueConverter -->

  10:          <local:DoubleToString x:Key="doubleToString" />

  11:          

  12:          <!-- ObjectDataProvider pone al metodo como fuente de datos(origen de enlace/binding source) -->

  13:              <ObjectDataProvider

  14:              x:Key="ConvertirDistancia"

  15:              ObjectType="{x:Type local:ConvertidorDistancia }"

  16:              MethodName="Convertir" >

  17:                  <!-- Declaramos los parametros que recibe el metodo Convertir-->

  18:                  <ObjectDataProvider.MethodParameters>

  19:                      <system:Double>10</system:Double>

  20:                      <local:TipoDistancia>Millas</local:TipoDistancia>

  21:                  </ObjectDataProvider.Method
Parameters>

  22:              </ObjectDataProvider>

  23:      </Window.Resources>

  24:      

  25:      <Grid Margin="10">

  26:          <Grid.ColumnDefinitions>

  27:              <
ColumnDefinition Width="0.5*"/>

  28:              <ColumnDefinition Width="0.5*"/>

  29:          </Grid.ColumnDefinitions>

  30:          <Grid.RowDefinitions>

  31:              <RowDefinition Height="31" />

  32:              <RowDefinition Height="31" />

  33:              <RowDefinition Height="31" />

  34:          </Grid.RowDefinitions>

  35:          

  36:          <TextBlock Margin="5" Grid.ColumnSpan="2"

  37:                     VerticalAlignment="Center"

  38:                     Text="Ingrese una distancia para convertir :"/>

  39:          

  40:          <!-- Este TextBox enlazara al primer parametro del metodo Convertir  -->

  41:          <TextBox


  42:              Grid.Row="1" Grid.Column="0" Margin="5"

  43:              Text ="{Binding Source={StaticResource ConvertirDistancia}, Path=MethodParameters[0],

  44:                      BindsDirectlyToSource=true, UpdateSourceTrigger=PropertyChanged,

  45:                      Converter={StaticResource doubleToString}}"/>

  46:          

  47:          <!-- Este ComboBox enlazara al segundo parametro del metodo Convertir -->

  48:          <ComboBox

  49:              Grid.Row="1" Grid.Column="1" Margin="5" Width="80"

  50:              HorizontalAlignment="Left"

  51:              SelectedValue="{Binding

  52:                              Source={StaticResource ConvertirDistancia},

  53:                              Path=MethodParameters[1],

  54:                              BindsDirectlyToSource=true}" >

  55:              <local:TipoDistancia>Millas</local:TipoDistancia>

  56:              <local:TipoDistancia>Kilometros</local:TipoDistancia>

  57:          </ComboBox>

  58:          <TextBlock Grid.Row="2" HorizontalAlignment="Right" Margin="5"

  59:                     Text="Resultado:"/>

  60:          

  61:          <!-- Este TextBlock enlaza al resultado del metodo Convertir, es decir, lo que retorna.-->

  62:          <TextBlock Grid.Row="2" Grid.Column="1" Margin="5" 

  63:                     Text="{Binding Source={StaticResource ConvertirDistancia}}"/>

  64:          

  65:      </Grid>

  66:  </Window>



Ahora ya podemos instanciar la clase ConvertidorDistancia y tenerlo como recurso.Para eso debe ir dentro de:
<Window.Resources>
y
</Window.Resources>
.Y puesto que voy a enlazar el metodo Convertir debo instanciar la clase ConvertidorDistancia mediante la clase ObjectDataProvider.Esta clase ajusta y crea un objeto que se puede utilizar como origen de enlace.La operación comienza en la línea 13 y termina en la línea 22.
En la línea 14 le asignamos una clave a nuestro origen de enlace: x:Key="ConvertirDistancia"
En la línea 15 definimos el tipo de la instancia que creamos mediante el objeto ObjectType:
ObjectType="{x:Type local:ConvertidorDistancia }"
Mediante la propiedad MethodName definimos el metodo que es invocado por ObjectDataProvider:
MethodName="Convertir"
Dado que el metodo Convertir, el que enlazaremos, recibe dos parametros, los definimos mediante la propiedad de ObjectDataProvider: MethodParameters(Línea 18):
<ObjectDataProvider.MethodParameters>
Esta propiedad puede contener una colección de parametros.
Definimos el valor inicial del primer parametro.Será el valor 10 y será de tipo Double, ya que el primer parametro que recibe el metodo Convertir es un tipo Double(linea 19):
<system:Double>10</system:Double>
Tambien definimos el valr inicial del segundo parametro, el cual será Millas de tipo TipoDistancia(línea 20) :
<local:TipoDistancia>Millas</local:TipoDistancia>

Ya tengo listo el objeto para poder usarlo como origen de enlace.

Tenemos un TextBox(línea 41), un ComboBox(línea 48) y un TextBlock(línea 58).
La propiedad Text del TextBox enlazará al primer parametro del metodo Convertir.La propiedad SelectedValue del ComboBox enlazará al segundo parametro.La propiedad Text del TextBlock enlazará al metodo Convertir, es decir a su valor de retorno, el cual será un String.
En la línea 43 le decimos que nuestro origen de enlace(o fuente de datos) es el recurso estático llamado ConvertirDistancia.Recordemos que ConvertirDistancia es la clave del metodo Convertir,asi que apunta a este.Luego, definimos el valor de la propiedad Path.Aqui ocurre algo interesante.Vemos que la propiedad que queremos enlazar no es una propiedad del objeto que envuelve ObjectDataProvider(ConvertidorDistancia), sino que es una propiedad del mismo objeto ObjectDataProvider, la propiedad MethodParameters, y es el primer valor de la colección de parametros, osea el primer parametro, por eso la posicion cero:
Path=MethodParameters[0]
A continuacion vemos la propiedad BindsDirectlyToSource:
BindsDirectlyToSource=true
Esta propiedad establece un valor que indica si se va a evaluar la propiedad Path con respecto al elemento de datos(en este caso ConvertidorDistancia) o el objeto DataSourceProvider(en este caso ObjectDataProvider, ya que deriva de la clase abstracta DataSourceProvider).
El valor por defecto de esta propiedad es false.Cuando es false evalua la ruta de acceso con respecto al elemento de datos propiamente dicho; de lo contrario, es true, es decir, en nuestro caso evuluará la ruta de acceso respecto al obejto ObjectDataProvider.Esto debe ser así, ya que la propiedad Path tiene el valor de una propiedad de ObjectDataProvider(MethodParameters) y no del elemento de datos.
Luego tenemos la propiedad UpdateSourceTrigger:
UpdateSourceTrigger=PropertyChanged
Esto significa que desde el TextBox podremos actualizar el valor del origen de enlace, es decir el valor del primer parametro del metodo Convertir.El valor PropertyChanged significa que tan pronto haga un cambio en el TextBox actualizará el origen de enlace con el valor de la propiedad Text del TextBox.Finalmente tenemos la propiedad Converter del cual me ocuparé mas adelante.
En cuanto al ComboBox sucede casi lo mismo,excepto que va a enlazar al segundo parametro del metodo Convertir.Se definen los itemes de tipo TipoDistancia:
<local:TipoDistancia>Millas</local:TipoDistancia>
<local:TipoDistancia>Kilometros</local:TipoDistancia>

La propiedad Text del TextBlock enlaza al resultado del metodo Convertir, es decir a su valor de retorno:
Text="{Binding Source={StaticResource ConvertirDistancia}}"

La propiedad Source del Binding se usa para referenciar a una instancia de un objeto creado como recurso.

Propiedad Converter:
Se usa cuando es necesario convertir valores de propiedades antes de asignarse al destino de enlace o al origen de enlace.Para eso es necesario crear una clase que implemente la interfaz IValueConverter.La inetrfaz IValueConverter tiene dos metodos:
Convert y ConvertBack, donde podemos implementar cualquier conversion compleja.El metodo Convert es llamado cuando el modo de enlace es OneWay, es decir, cuando el origen de enlace modifica al destino de enlace(destino de datos).El metodo ConvertBack es llamado en el modo TwoWay, es decir, cuando el destino de enlace modifica el origen de enlace.
Cuando se especifica la propiedad Converter en un enlace o binding el valor del destino de enlace no se actualiza directamente desde el origen de enlace, sino que pasa primero por el método Convert, ahi se hace la conversion para retornar el valor apropiado que recibe el destino de enlace.
Para poder usarlo como un recurso es necesario instanciarlo.En nuestro ejemplo lo hicimos en la línea 10:
<local:DoubleToString x:Key="doubleToString" />
A continuación la clase que implementa a IValueConverter, se llama DoubleToString, aunque en el metodo ConvertBack mas bien convierte de string a Double:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//para implementar ivalueconverter
using System.Windows.Data;
//necersario para CultureInfo
using System.Globalization;

namespace TestWPF_Data_Binding_II
{

[ValueConversion(typeof(double), typeof(String))]
public class DoubleToString: IValueConverter
{
/// <summary>
/// Convierte Double a String.Antes de actualizar el valor del destino de enlace
///el valor pasa por este metodo para ser convertido a String.
/// </summary>
/// <param name="value">Objeto double</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double val = (double)value;
return val.ToString();
}

/// <summary>
/// Covierte String a Double.Antes de actualizar el valor del origen de enlace
/// el valor pasa por este metodo para ser convertido a Double.
/// </summary>
/// <param name="value">Objeto string</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//throw new NotImplementedException();-->cuando no es necesario implementar el metodo.
double val = Double.Parse(value.ToString());
return val;
}


}
}


En nuestro ejemplo, el TextBox que enlaza al primer parámetro del metodo Convertir, recibe un string, ya que su propiedad Text soporta el tipo String.El modo del enlace es TwoWay(recordar la propiedad UpdateSourceTrigger), ya que podemos modificar el primer parámetro del metodo Convertir desde el TextBox.Aquí surge un inconveniente, la propiedad Text del TextBox contiene un String y el primer parametro del metodo Convertir es un tipo Double.En este momento entra en acción el metodo ConvertBack, es decir, antes de actualizar el origen de enlace(el primer parametro del metodo Convertir) el valor del destino de enlace (propiedad Text del TextBox) pasa por el metodo ConvertBack.Como notarán, en ese método el string se convierte a Double y se retorna un Double.Este Double es el que actualiza finalmente el primer parámetro del metodo Convertir.
Recordaran que se le habia asignado un valor inicial a los parametros del metodo Convertir(linea 19 y 20).Ocupemonos del primer parámetro:
Vemos que es el valor 10 de tipo Double.Tambien vemos que la propiedad que se enlazo al TextBox fue el primer parametro, es decir, un valor de tipo Double.Aqui entra en acción el metodo Convert.Antes de actualizar el TextBox con un Double desde el origen de enlace, el valor Double pasa por el metodo Convert, ahi se convierte a String y devuelve un String.Pues la propiedad Text del TextBox admite el tipo String.

Esa magia se logra implementando IValueConverter y especificando la propiedad Converter en el enlace:
Converter={StaticResource doubleToString}}
Aqui le decimos que Converter igual al recurso estático llamado doubleToString.Ya sabemos que es la clave de la clase DoubleToString.

captura:

No hay comentarios:

Publicar un comentario