Mostrando entradas con la etiqueta binding. Mostrar todas las entradas
Mostrando entradas con la etiqueta binding. Mostrar todas las entradas

domingo, 21 de junio de 2009

Data Binding V - LaTeX

Código LaTeX

Ejemplo usando MultiBinding y MultiValueConverter .Lo que muestra es el código para generar una tabla en LaTeX:



Descargar
Nota: Se requiere el framework 3.5

jueves, 18 de junio de 2009

Data Binding IV

Multi Binding - IMultiValueConverter :
MultiBinding :
La clase MultiBinding describe una colección de objetos Binding asociados a una sola propiedad de destino de enlace.
MultiBinding permite enlazar una propiedad del destino de enlace a una lista de propiedades de origen y, a continuación, aplicar la lógica para generar un valor con las entradas indicadas.

IMultiValueConverter:
Proporciona una forma de aplicar la lógica personalizada a una clase MultiBinding.
Para asociar un convertidor a una clase MultiBinding, se debe crear una clase que implemente la interfaz IMultiValueConverter y, a continuación, los métodos Convert y ConvertBack.

En otras palabras, podemos tener múltiples propiedades de origen de enlace para una propiedad de destino de enlace.Sin embargo, la propiedad de destino de enlace sólo recibe una propiedad que no necesariamente es una de las de origen de enlace, sino el resultado de la lógica de conversion implementada en una clase que implementa la interfaz IMultiValueConverter.

Por ejemplo, la propiedad RenderTransform de un objeto Image recibe un objeto Transform que puede ser un ScaleTransform,RotateTransform,etc...
Para instanciar la clase RotateTransform se requieren -no necesariamente- tres valores de tipo double(angulo,coordenada X i Y del punto central de la rotación), los cuales se envían como parámetro a su constructor.

Ahora bien, Qué hago si tuviera una objeto Image al cual quisiera rotar de manera que el angulo de rotación y las coordenadas X i Y cambien en función de otros procesos?.

Pues, la respuesta es: MultiBinding + IMultiValueConverter.

Para hecerlo mas comprensivo he hecho un pequeño ejemplo.Una pequeña aplicación que rota un objeto Image en base a los valores de tipo double de la propiedad Value de tres Slider´s.
La propiedad RenderTransform(destino de enlace) del objeto Image, enlaza a tres propiedades de origen de enlace: la propiedad Value de cada Slider(líneas 33,34 y 35).

Muestro el código XAML :


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

   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:      Title="DataBinding_MultiBinding" Height="740" Width="768">

   6:      <Window.Resources>

   7:          <local:DoubleRotateTransform x:Key="Convertidor"/>

   8:      </Window.Resources>

   9:      <Grid>

  10:          <Grid.RowDefinitions>

  11:          <RowDefinition Height="57*" />

  12:          <RowDefinition Height="124*" />

  13:          <RowDefinition Height="521*" />

  14:          </Grid.RowDefinitions>

  15:          

  16:          <TextBlock Text="MultiBinding" FontSize="22" Grid.Row="0" HorizontalAlignment="Center"/>

  17:          

  18:          

  19:          <Slider Grid.Row="1" Margin="53,11,125,0" Name="slider1"  VerticalAlignment="Top"

  20:                  Minimum="0" Maximum="1000"

  21:                  ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Value}"/>

  22:          

  23:          <Slider Grid.Row="1" Margin="53,50,125,52" Name="slider2" Minimum="0" Maximum="1000"

  24:                  ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Value}" />

  25:          

  26:         <Slider Grid.Row="1" Height="21" Margin="53,0,125,18" 

  27:                Name="slider3" VerticalAlignment="Bottom" Minimum="0" Maximum="360" 

  28:                 ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Value}"/>

  29:          

  30:         <Image Grid.Row="2" Margin="128,81,117,175" Name="image" Stretch="Fill" Source="/TestWPF_Data_Binding_II;component/rotsnake.gif">

  31:              <Image.RenderTransform>

  32:                      <MultiBinding Converter="{StaticResource Convertidor}">

  33:                          <Binding ElementName="slider1" Path="Value"/>

  34:                          <Binding ElementName="slider2" Path="Value"/>

  35:                          <Binding ElementName="slider3" Path="Value"/>

  36:                      </MultiBinding>

  37:              </Image.RenderTransform>

  38:          </Image>

  39:   

  40:      </Grid>

  41:  </Window>



Vemos que, entre las lineas 32 y 38, se han enlazado tres propiedades de origen de enlace, las propiedades Value de tres Slider´s, para una sola propiedad destino de enlace.Además, notamos que, en la línea 33, se especifica la propiedad Converter para el MultiBinding :
Converter="{StaticResource Convertidor}"
Esto significa que el recurso estático Convertidor esta apuntando a una clase que implementa la interfaz IMultiValueConverter.En efecto, la clase se llama DoubleToRotateTransform.Ha sido instanciada en la línea 7.

En el método Convert se desarrolla la lógica de conversion de los tres valores de tipo double de la propiedad Value de cada Slider y se retorna un objeto RotateTransform, el cuál si admite la propiedad RenderTransform del objeto Image.
Es conveniente señalar que, debido a que el modo del binding es OneWay,sólo es necesario implementar el metodo Convert.

A continuación la clase DoubleToRotateTransform :


using System;
//requerido para IMultiValueConverter:
using System.Windows.Data;
//requerido para ScaleTransform:
using System.Windows.Media;
//requerido para CultureInfo culture:
using System.Globalization;

namespace TestWPF_Data_Binding_II
{
public class DoubleToRotateTransform:IMultiValueConverter
{
/// <summary>
/// Metodo que recibe dos valores de tipo double
/// y retorna un ScaleTransform
/// </summary>
/// <param name="values">Colección de dos valores de tipo double</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns>Retorna un ScaleTransform para la prop. RenderTransform de un image</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double coorX = (double)values[0], coorY = (double)values[1], angulo=(double)values[2];

RotateTransform rt = new RotateTransform(angulo,coorX, coorY);
return rt;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
//Hacer conversiones si el modo es TWOWAY
throw new NotSupportedException("No se puede convertir");
}
}
}


Como se darán cuenta, los valores de las propiedades de origen de enlace pasan al metodo Convert como una colección de objetos(primer parámetro):
objetct[] values
Además, en la colección, estan en el orden en que fueron declarados en el enlace.Así, en la posición cero esta el valor dela propiedad Value de slider1; luego, en la posicion1, slider2 y finalmente slider3.


Nota: Cada propiedad Tooltip de cada Slider esta enlazando a la propiedad Value del mismo objeto al que pertenecen(líneas 21, 24 y 28):
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Value}"

Captura :

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:

sábado, 13 de junio de 2009

Data Binding WPF II

Enlazando un XML:



El codigo XAML:
namespace :TestWPF_Data_Binding_II
nombre de la ventana :DataBinding_XMLData



<Window x:Class="TestWPF_Data_Binding_II.DataBinding_XMLData"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBinding_XMLData" Height="300" Width="300">
<Window.Resources>
<!--Usar la propiedad Source de XmlDataProvider si tenemos el archivo xml
referenciandolo con la Uri apropiada, como se muestra en el sgte codigo: -->
<!--<XmlDataProvider x:Key="PerrosXML"
Source="Perros.xml"
XPath="Perros"/>-->

<!-- O podemos embeber directamente el xml : -->
<XmlDataProvider x:Key="PerrosXML">
<x:XData>
<!--Si -cuando el xml va embebido- no se deja vacío el atributo xmlns, se producirá un
error, la salida será : "System.Windows.Data.Error exception has occurred"-->
<Perros xmlns="" >
<Perro Nombre="Alco" Color="Blanco" />
<Perro Nombre="Güeso" Color="Gris" />
<Perro Nombre="Pellejo" Color="Negro" />
</Perros>
</x:XData>
</XmlDataProvider>
</Window.Resources>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80" />
<RowDefinition />
</Grid.RowDefinitions>

<TextBlock Text="Bind To XML Data" FontSize="15"
HorizontalAlignment="Center" Grid.Row="0" Margin="3" />
<TextBlock Text="Enlazando un archivo XML" FontSize="15"
HorizontalAlignment="Center" Grid.Row="0" Margin="29" />

<ListBox
ItemsSource="{Binding Source={StaticResource PerrosXML},
XPath=/Perros/Perro/@Nombre}"
Grid.Row="1" HorizontalAlignment="Center"/>
</Grid>
</Window>

domingo, 31 de mayo de 2009

Data Binding WPF

Explicación breve acerca de cómo usar data binding en wpf y ademas qué es data binding.
Disculpen las redundancias en la explicación, pero mejor dejar todo claro....

Modo One Way :



Modo Two Way :



Enlazar un objeto CLR - Modo Two Way :



Codigo - Visual Studio 2008:
Descargar