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

sábado, 21 de agosto de 2010

Obtener Icono de Aplicación Mediante Path o Ruta de Ejecutable

Una forma de obtener el ícono de una applicación es mediante el path o ruta de ejecutable de la aplicación, llamando a la función ExtractIconEx la cual se encuentra en la librería shell32.dll.
Dicha función obtiene el handle del ícono de una aplicación mediante la ruta de su ejecutable.La firma de la función ExtractIconEx es :


UINT WINAPI ExtractIconEx(
__in LPCTSTR lpszFile,
__in int nIconIndex,
__out_opt HICON *phiconLarge,
__out_opt HICON *phiconSmall,
__in UINT nIcons
);


//Para obtener un solo ícono
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
internal static extern int ExtractIconEx(string stExeFileName, int nIconIndex, ref IntPtr phiconLarge, ref IntPtr phiconSmall, int nIcons);

//Para obtener un array de íconos
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
internal static extern int ExtractIconEx(string stExeFileName, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, int nIcons);



Donde:
- lpszFile: indica el path del ejecutable de la aplicación.
-nIconIndex : Índice del icono a obtener.Si es cero se obtendrá el primer ícono
-*phiconLarge : un Array de handles dónde serán almacenados los iconos extraídos.También puede ser un solo handle.Si es null ningun ícono 'large' será extraído.
-*phiconSmall : un Array de handles dónde serán almacenados los iconos extraídos.También puede ser un solo handle.Si es null ningun ícono 'small' será extraído.
-nIcons :Número de íconos a ser extraídos.

En un post anterior ya se ha visto cómo recuperar la ruta o path del ejecutable de una aplicación mediante el Handle de ventana. A continuación muestro un método que retorna el handle del Icono de la aplicación:


private static IntPtr GetHicon(string exePath)
{
//sólo obtendré el largeIcon
IntPtr largeIcon = IntPtr.Zero;
IntPtr smallIcon = IntPtr.Zero;
ExtractIconExA(exePath, 0, ref largeIcon, ref smallIcon, 1);

DestroyIcon(smallIcon);

return largeIcon;
}



Captura:

Bajar solución completa AQUÍ.(VS2008/WPF)

sábado, 12 de diciembre de 2009

Cómo obtener lista de ventanas mediante API´s

Para obtener los manejadores o handles delas ventanas abiertas se puede hacer llamando a la función GetWindow, pero es mas seguro hacerlo mediante la función EnumWindows ya que GetWindow puede refenciar a un handle de una ventana ya destruida o quedarse en un loop`o ciclo infinito, es fácil darse cuenta de esto último mediante el debugger.
La funcion EnumWindows esta contenida en la librería user32.dll y su sintaxis es la siguiente:
BOOL EnumWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);


El primer parámetro es un puntero a una función callback.
El segundo parámetro de la función es un valor definido por la aplicación; es decir que puedo mandar cualquier parámetro que me sea necesario usar en la función callback.


Para el primer parámetro debemos declarar un delegado. Antes de hacerlo veamos como debe ser la función callback(EnumWindowsProc):
BOOL CALLBACK EnumWindowsProc(
HWND hwnd,
LPARAM lParam
);

El primer parámetro es el handle de la ventana, lo recibirá de su llamador,la función EnumWindows.
El segundo parámetro es un valor definido por la aplicación,lo recibirá de su llamador,la función EnumWindows.
Debe retornar TRUE para seguir enumerando, de lo contrario no lo hará.

Antes es necesario saber que para poder acceder a las API's es necesario agregar la directiva:
using System.Runtime.InteropServices;

Entonces declaramos el delegado y de una vez la función EnumWindows:

//declaro el delegado para la función callback
private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam);

//Declaro la función EnumWindows
private static extern bool EnumWindows(EnumWindowsCallback Callback, IntPtr lParam);


Ahora creamos el método al que apuntará el delegado EnumWindowsCallback y El método público que llamará a la función EnumWindows :



//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
hwndList.Add(hwnd);

return true; //continua la enumeración
}

//aqui se guradara la lista de handle's
private static List<IntPtr> hwndList;

public static List<IntPtr> GetWindowsList()
{
hwndList = new List<IntPtr>();

EnumWindowsCallback MyCallback = Callback;

bool val = EnumWindows(MyCallback, IntPtr.Zero);

/*retorno la lista de handle's*/
return hwndList;

}

Se recuperará todos los handle's de todas las ventanas abiertas, que son muchas.Si lo que se quiere es recuperar sólo las ventanas con borde y visibles -que en su mayoría son las que tienen los programas o aplicaciones mas comunes y de uso diario- se debe llamar a la función GetWindowLong para saber los estilos de la ventana; es decir, si tiene borde y si es visible, entre otros valores que se pueden consultar.
La función GetWindowLong recupera información de una ventana especificada.
Su sintaxis:
LONG GetWindowLong(
HWND hWnd,
int nIndex

);

El primer parámetro es el handle una ventana especificada, de la que se quiere saber su estilo.
El segundo parámetro sera el valor de GWL_STYLE, pues para este ejemplo es necesario.
En esta página se pueden ver las los valores de sus constantes.
El msdn dice que para que nuestro código sea compatible con 32bits y 64bits debemos llamar a la función GetWindowLongPtr que es prácticamente lo mismo que GetWindowLong -en su declaración-.Sin embargo, en Windows Vista Ultimate de 32bits la función GetWindowLonPtr no funciona, no existe.En cambio GetWindowLongA corre sin problemas en el S.O mencionado.Acerca de ésta dice el msdn que provee un comportamiento mas consistente entre los S.O's Windows. Así que para este ejemplo se llamara a la funcion GetWindowLongA, por lo que la función callback quedará:


//El valor standard de GWL_STYLE es -16
static readonly int GWL_STYLE = -16;
static readonly ulong WS_VISIBLE = 0x10000000;
static readonly ulong WS_BORDER = 0x00800000;
static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;

//Declaro la funcion GetWindowLongA
[DllImport("user32.dll")]
private static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);

//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
//agrego ventanas visibles con borde
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

return true; //continua la enumeración
}


En
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

le decimos si la ventana tiene bordes y es visible que agregue a la lista genérica hwndList.

Para hacer mas intersante la aplicación podemos recuperar también los titulos de las ventanas cuyos handle's hemos recuperado. Esto lo hacemos mediante la función GetWindowText contenida en la librería user32.dll :


/*Declaro la función GetWindowText*/

/// <summary>
/// Recupera el título de un
a ventana(de la barra de título), si es que tiene.
/// </summary>
/// <param name="hWnd">Handle de la ventana de la que se quiere recuperar el título.</param>
/// <param name="lpString">[out]Puntero al buffer que recibe el texto.Si el texto es
/// más largo que el buffer será truncado y terminado con caracter NULL.</param>
/// <param name="nMaxCount">Especifica el maximo´número de caracteres a escribir en
/// el buffer</param>
[DllImport("user32.dll")]
private static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


Para que se vea mas ordenado en una clase estática pondré las llamadas a las API's y mostraré en un listbox(WPF) los titulos de ventanas obtenidos.
Clase APICalls.cs :


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

//Requerido para las API´s:
using System.Runtime.InteropServices;

namespace GettingWindows
{
public static class ApiCalls
{
//aqui se guradara la lista de handle´s
private static List<IntPtr> hwndList;

//El valor standard de GWL_STYLE
static readonly int GWL_STYLE = -16;
static readonly ulong WS_VISIBLE = 0x10000000;
static readonly ulong WS_BORDER = 0x00800000;
static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;


/// <summary>
/// Enumera todas las ventanas de la pantalla.Para eso llama a la función
/// Callback.La funcion continua llamandose hasta
/// que EnumWindowsCallback retorne false.
/// </summary>
/// <param name="lpEnumFunc">Puntero a la funcion EnumWindowsCallback</param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsCallback Callback, IntPtr lParam);
/// <summary>
/// Defino EnumWindowsCallback a traves de un delegado.
/// Ver el metodo privado estatico Callback.
/// </summary>
/// <param name="hwnd">Handle de la ventana top-level que esta enumerando </param>
/// <param name="lParam"></param>
/// <returns>Para continuar enumeando debe retornar TRUE</returns>
private delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam);

/// <summary>
/// Recupera la información de una ventana especifica
/// </summary>
/// <param name="hWnd">Handle a la ventana y-indirectamente- a la clase a la cual
/// pertenece la ventana.</param>
/// <param name="nIndex"></param>
/// <returns>retorna cero(0) si la funcion falla</returns>
[DllImport("user32.dll")]
private static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);

/***/
/// <summary>
/// Recupera el título de una ventana(de la barra de título), si es que tiene.
/// </summary>
/// <param name="hWnd">Handle de la ventana de la que se quiere recuperar el título.</param>
/// <param name="lpString">[out]Puntero al buffer que recibe el texto.Si el texto es
/// más largo que el buffer será truncado y terminado con caracter NULL.</param>
/// <param name="nMaxCount">Especifica el maximo número de caracteres a escribir en
/// el buffer</param>
[DllImport("user32.dll")]
private static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);


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

//Metodo al que apunta el delegado EnumWinodwsCallback
private static bool Callback(IntPtr hwnd, IntPtr lParam)
{
//agrego ventanas visibles con borde
if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
{

hwndList.Add(hwnd);
}

return true; //continua la enumeración
}



public static List<string> GetWindowsList()
{
List<string> wndTitles = new List<string>() ;
StringBuilder sb = new StringBuilder(256);

hwndList = new List<IntPtr>();

EnumWindowsCallback MyCallback = Callback;

EnumWindows(MyCallback, IntPtr.Zero);

/*ya llena la lista de handle´s obtengo el titulo de cada ventana*/
foreach (IntPtr h in hwndList)
{
GetWindowText(h, sb, sb.Capacity);

wndTitles.Add(sb.ToString());
}

/*retorno la lista de titulos de ventanas*/
return wndTitles;

}
}
}

Código de la ventana MainWindow.xaml(en XAML):


<Window x:Class="GettingWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="363" Width="322">
<Grid>
<Button Height="27" Margin="26,29,133,0" Name="btnGetList" VerticalAlignment="Top" Click="btnGetList_Click">Mostrar lista de ventanas</Button>
<ListBox Margin="26,80,19,18" Name="lbxList" />
</Grid>
</Window>

Código de MainWindow.cs :

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

namespace GettingWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void btnGetList_Click(object sender, RoutedEventArgs e)
{
lbxList.ItemsSource = ApiCalls.GetWindowsList();

}
}
}


Una captura:

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:

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