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

jueves, 3 de diciembre de 2009

Delegados C#

Un delegado o delegate es un tipo que hace referencia a un método.Cualquier método que coincida con la firma del delegado puede asignarse al delegado.Veamos un ejemplo:


//Declaro un delegado
public delegate int DelCalculo(int a,int b);

//Creo un Metodo para el delegado DelCalculo.
/*Nótese que coincide con la firma del delegado,
*tanto por el tipo de valor devuelto como por los
*tipos de los parametros*/
public static int Suma(int a, int b)
{
return (a + b);
}

/******************************************************************************/
/*Creo una instancia del delegado asignándole
* el metodo al que hará referencia*/
DelCalculo obj = Suma;

/*Llamo al delegado*/
int val = obj(3, 4);

/*Muestro en pantalla el valor devuelo:
*El valor devuelto es : 7 */
Console.WriteLine("El valor devuelto es : {0}",val);


Desde la versión 2.0 de C# se puede hacer uso de los métodos anónimos,esto es pasar un bloque de código como parámetro de delegado, de esta forma no tendría que crear un método para asignárselo en la instanciación del delegado como lo hice en el código mostrado lineas arriba.A continuación instaciaré al delegado usando un método anónimo y en vez de devolver la suma de los parámetros, devolverá la diferencia:


/*Creo instancia del delegado usando un metodo anonimo*/
DelCalculo obj2 = delegate(int a, int b) { return (a - b); };

/*llamo al delegado*/
val = obj2(10, 5);

/*Muestro en pantalla el valor devuelto:
*El valor devuelto por obj2 es : 5 */
Console.WriteLine("El valor devuelto por obj2 es : {0}", val);


Desde la versión 3.0 del framework .net es posible hacer uso de expresiones Lambda.A continuación instaciaré al delegado usando expresiones lambda y retornará el producto de sus parametros :


/*instanciando delegado usando expresiones lambda*/
DelCalculo obj3 = (int a, int b) => { return (a * b); };

/*llamando al delegado*/
val = obj3(6, 8);

Console.WriteLine("El valor devuelto por obj3 es : {0}", val);

/*Salida en pantalla: El valor devuelto por obj3 es : 48 */


Dado que un delegado con instancias es un objeto, se puede pasar como parámetro a un metodo para ser llamado posteriormente, lo que se conoce como devolución de llamada asincrónica.Veamos un ejmplo:


/*Utiliza el delegado DelCalculo como parámetro y lo invoca*/
public static void MetodoConCallBack(int x, int y, DelCalculo callback)
{
int val = callback(x,y);
Console.WriteLine("El resultado es : {0}",val);
}

/*llamo al método pasándole como parámetro una instancia del
* delegado DelCalculo, en este caso: obj*/

MetodoConCallBack(5, 4, obj);

/*La salida en pantalla será: El resultado es : 9 */


Un delegado puede llamar a mas de un método cuando se invoca.Sólo es necesario agregar los delegados -que llaman a los otros metodos- a su lista de invocación mediante el operador de suma "+" o "+=".Para el siguiente ejemplo crearé una nueva clase que contendrá dos metodos y tambien crearé un nuevo delegado que hará referencia a esos metodos:


using System;

namespace TestingDelegates
{
class MisMetodos
{
public void Metodo1(string cad)
{
Console.WriteLine(cad+" : Metodo1" );
}

public void Metodo2(string cad)
{
Console.WriteLine(cad + " : Metodo2");
}
}
}

/***********************************************************/

/*instancia de clase MisMetodos*/
public static MisMetodos metodos = new MisMetodos();
/*Delegado para metodos de MisMetodos*/
public delegate void nuevoDel(string cad);

/*asignando*/
nuevoDel del1 = metodos.Metodo1;
nuevoDel del2 = metodos.Metodo2;

nuevoDel contenedorDel = del1 + del2;

/*Invocando al delegado que llamará a los métodos*/
contenedorDel("Metodo ejecutado");

/*La salida será :
*Metodo ejecutado : Metodo1
*Metodo ejecutado : Metodo2*/


También se pueden quitar métodos de la lista de invocación.A continuación quitaré el método Metodo1:


/*quitando el metodo Metodo1 de la lista de invocación*/
contenedorDel -= metodos.Metodo1;

contenedorDel("Metodo ejecutado");

/*La salida será:
*Metodo ejecutado : Metodo2 */


Al principio decía que el método debe coincidir con la firma del delegado, en realidad no tienen que coincidir exactamente, esto se debe a la covarianza y contravarianza:
La covarianza permite que un método tenga un tipo de valor devuelto más derivado que lo que se define en el delegado. La contravarianza permite un método con tipos de parámetro que se deriven menos que en el tipo de delegado.

Veamos:


/*Tengo dos clases, una base y otra derivada:*/
public class ClasePadre
{

}

public class ClaseHija : ClasePadre
{

}

/***********************************************************************/

/*declaro un delegado para mostrar la Covarianza*/
public delegate ClasePadre CovarianzaDel();

/*declaro un delegado para mostrar la Contravarianza*/
public delegate void ContravarianzaDel(ClasePadre padre);

/************************************************************************/
//USANDO EXPRESIONES LAMBDA:

/*la covarianza nos permite esto:
*devolver un tipo derivado del tipo que especifica el delegado*/
CovarianzaDel cov = () => { return new ClaseHija(); };



ContravarianzaDel contrav = (ClasePadre padre) => {/*...*/};

ClaseHija hija = new ClaseHija();

/*La contravarianza nos permite mandar como parametro una
*instancia de la clase derivada
*de la que se especifica en el delegado*/

contrav(hija);

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 :

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>