[EVENTO] Extiende tu aplicación con Managed Extensibility Framework (MEF)

Hola a todos!

El próximo martes de 1 de febrero junto a SecondNug podréis encontrarme en este evento, en el que trataremos sobre MEF y como extender y modularizar nuestras aplicaciones usando este Framework de extensibilidad.

Espero veros (o escucharos) en este Webcast.

Link de registro:

https://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032475442&EventCategory=4&culture=es-ES&CountryCode=ES

 

Un saludo y Happy Coding!

Foros oficiales de Windows Phone 7 en español

winphone

Hola a todos!

Hoy por la tarde desde Microsoft (gracias por el esfuerzo y persistencia a José Bonnin y todo su equipo) nos informaban de la apertura de los nuevos y flamantes foros de Windows Phone 7 en MSDN en la lengua de cervantes.

Creo que es un gran paso para que la comunidad hispanohablante alrededor de Windows Phone 7 crezca y gane fuerza y sobretodo una muestra de que, de vez en cuando, desde Microsoft escuchan nuestras peticiones y sugerencias.

Sin más, espero veros por los foros y que entre todos construyamos una bonita y útil comunidad:

http://social.msdn.microsoft.com/Forums/es-ES/category/windowsphone

Un abrazo a todos!

Silverlight 4: Carga dinámica de aplicaciones XAP usando MEF

Hola a todos!

Por cuestiones laborales he tenido que pelearme con MEF (Managed Extensibility Framework) para, desde una aplicación Silverlight poder cargar bajo demanda del usuario otras aplicaciones Silverlight secundarias e incrustarlas en la aplicación principal como si de UserControls se tratase.

¿Que es MEF?

MEF o Managed Extensibility Framework, es una librería para crear aplicaciones extensibles mediante plugins, sin tener que realizar complicadas operaciones.

MEF establece una forma de descubrimiento implícito de estos plugins. Generalmente una aplicación que use MEF para exponerse a otras aplicaciones, es llamada Part. Define sus dependencias (marcadas con el atributo Imports) y sus capacidades (conocidas como Exports).

De esta forma una aplicación Host puede descubrir y cargar estas partes en tiempo de ejecución, en ese momento el Composition Engine de MEF se encarga de satisfacer las dependencias (Imports) de la parte cargada usando ensamblados o referencias de si misma u otras partes ya cargadas.

MEF no se limita a Silverlight, es parte integral de .NET 4 y puede ser usado desde aplicaciones Winforms, WPF, ASP.NET o cualquier otro tipo de aplicación.

Un ejemplo sencillo

Vamos a realizar un ejemplo sencillo, compuesto por 3 aplicaciones Silverlight (2 aplicaciones para ser inyectadas y una tercera que actuará de Host), solo necesitamos el proyecto web de la aplicación Host, las otras dos no lo necesitan, deberíamos tener una solución con 4 proyectos (3 Silverlight y 1 Web), en mi caso no he creado el proyecto web porque luego he alojado la aplicación Host directamente en IIS:

image

Configurando los proyectos que actuarán de Plugins

Una vez creados nuestros proyectos, debemos empezar a configurar las aplicaciones que se comportarán como Plugins: DynLoadApp1 y DynLoadApp2. Lo primero que debemos hacer en estas dos aplicaciones es añadir las siguientes referencias:

System.ComponentModel.Composition.dll
System.ComponentModel.Composition.Initialization.dll

Podemos encontrarlas en “..Program FilesMicrosoft SDKsSilverlightv4.0LibrariesClient”, es muy importante que en las propiedades de cada una de las referencias le indiquemos el atributo Copy Local a False, para evitar referencias duplicadas que solo harían crecer nuestro paquete XAP.

Para poder tener disponible una clase, usercontrol o página desde estos proyectos en nuestro proyecto Host debemos marcar la clase deseada con un Export, para que el Composition Engine de MEF la pueda localizar y ponerla a disposición del Host, en nuestro caso marcamos MainPage.xaml.cs con este atributo (declarado en System.ComponentModel.Composition):

[Export(typeof(UserControl))]
public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
    }
}

Una vez hecho esto, podemos generar nuestros plugins, que deberemos copiar al directorio Bin de nuestro Host (el mismo donde estará el xap de nuestro host) para poder cargarlos.

Configurando la aplicación Host

Este paso es quizás el más largo y complicado en MEF, puesto que nos exige que llevemos a buen termino varias tareas.

Para empezar, en nuestro proyecto Host referenciaremos de nuevo los ensamblados:

System.ComponentModel.Composition.dll
System.ComponentModel.Composition.Initialization.dll

Esta vez sin embargo debemos dejar el atributo Copy Local a true, pues usaremos las referencias de nuestro host para satisfacer las de nuestros plugins.

Lo siguiente será crearnos una carpeta “Helpers” donde insertaremos dos clases: DeploymentCatalogServices.cs que contendrá el código encargado de Añadir y Eliminar paquetes XAP en nuestro catalogo MEF y LoadHelper.cs que se encargará de cargar los paquetes XAP desde el catalogo de MEF e incrustarlos en nuestra aplicación:

DeploymentCatalogServices.cs se compone de una clase y un interface, el interface se usa para inicializar el servicio de catálogo e indicar el contrato a usar entre el Catálogo de MEF y nuestra clase de carga de paquetes en catalogo:

public interface IDeploymentCatalogService
{
    // The class that will implement this interface will implement these two methods
    void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null);
    void RemoveXap(string uri);
}

[Export(typeof(IDeploymentCatalogService))]
public class DeploymentCatalogService : IDeploymentCatalogService
{
    private static AggregateCatalog _aggregateCatalog;
    Dictionary<string, DeploymentCatalog> _catalogs;

    public DeploymentCatalogService()
    {
        _catalogs = new Dictionary<string, DeploymentCatalog>();
    }

    public static void Initialize()
    {
        _aggregateCatalog = new AggregateCatalog();
        _aggregateCatalog.Catalogs.Add(new DeploymentCatalog());
        CompositionHost.Initialize(_aggregateCatalog);
    }

    public void AddXap(string uri, Action<AsyncCompletedEventArgs> completedAction = null)
    {
        // Add a .xap to the catalog
        DeploymentCatalog catalog;
        if (!_catalogs.TryGetValue(new Uri(App.Current.Host.Source.ToString().Substring(0, App.Current.Host.Source.ToString().LastIndexOf('/') + 1) + uri).ToString(), out catalog))
        {
            catalog = new DeploymentCatalog(new Uri(App.Current.Host.Source.ToString().Substring(0, App.Current.Host.Source.ToString().LastIndexOf('/') + 1) + uri));

            if (completedAction != null)
            {
                catalog.DownloadCompleted += (s, e) => completedAction(e);
            }
            else
            {
                catalog.DownloadCompleted += catalog_DownloadCompleted;
            }

            catalog.DownloadAsync();
            _catalogs[uri] = catalog;
            _aggregateCatalog.Catalogs.Add(catalog);
        }
    }

    void catalog_DownloadCompleted(object sender, AsyncCompletedEventArgs e)
    {
        // Chekcks for errors loading the .xap
        if (e.Error != null)
        {
            throw e.Error;
        }
    }

    public void RemoveXap(string uri)
    {
        // Remove a .xap from the catalog
        DeploymentCatalog catalog;
        if (_catalogs.TryGetValue(uri, out catalog))
        {
            _aggregateCatalog.Catalogs.Remove(catalog);
            _catalogs.Remove(uri);
        }
    }
}

Como podemos ver, nuestra clase está marcada también con un Export para que pueda ser accedida y usada desde MEF.

LoadHelper.cs se encargará de satisfacer las referencias y dependencias (Imports) de nuestros paquetes XAP y de obtener del catalogo e incrustar en nuestra aplicación los diferentes paquetes que tengamos disponibles (o le indiquemos):

public class LoadHelper : IPartImportsSatisfiedNotification
{
    [Import]
    public IDeploymentCatalogService CatalogService { get; set; }

    // Specifies that a property, field, or parameter should be populated with all
    // matching exports by the System.ComponentModel.Composition.Hosting.CompositionContainer.
    [ImportMany(AllowRecomposition = true)]
    public Lazy<UserControl>[] MEFModuleList { get; set; }

    public string XapSelected = "";
    public Grid GridSelected;

    public LoadHelper()
    {
        //Initialize DeploymentCatalogService.
        CompositionInitializer.SatisfyImports(this);
    }
    
    public void LoadSelectedModule()
    {
        // Ensure that we have a Panel to add the .xap to
        if (GridSelected != null)
        {
            // Create a name for the .xap without the ".xap" part
            string strRevisedSelectedXAPName = XapSelected.Replace(".xap", ".");

            // Determine if the .xap is already loaded
            var SelectedMEFModuleInPanel = (from Module in GridSelected.Children.Cast<UserControl>()
                                            where Module.ToString().Contains(strRevisedSelectedXAPName)
                                            select Module).FirstOrDefault();

            // If the .xap is not loaded
            if (SelectedMEFModuleInPanel == null)
            {
                // Clear the panel
                GridSelected.Children.Clear();

                // Get the selected .xap 
                var SelectedMEFModule = (from Module in MEFModuleList.ToList()
                                         where Module.Value.ToString().Contains(strRevisedSelectedXAPName)
                                         select Module).FirstOrDefault();

                // If the .xap is found
                if (SelectedMEFModule != null)
                {
                    // Add the .xap to the main page
                    GridSelected.Children.Add(SelectedMEFModule.Value);
                }
            }
        }
    }
    
    void IPartImportsSatisfiedNotification.OnImportsSatisfied()
    {
        LoadSelectedModule();
    }
}

Lo primero que hacemos en el constructor de esta clase es satisfacer los Imports y una vez hecho esto ejecutamos el método LoadSelectedModule que se encarga de obtener y cargar el paquete XAP indicado, posicionándolo dentro del Panel (Canvas, Grid…) indicado.

Ahora solo nos queda diseñar la UI de nuestro Host:

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=".5*"></ColumnDefinition>
            <ColumnDefinition Width=".5*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        
        <TextBlock Grid.Row="0" Grid.ColumnSpan="2" 
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   Text="On demand XAP Loader"
                   FontSize="36"
                   FontWeight="Bold">
        </TextBlock>
        
        <Button Name="btnApp1"
                Grid.Row="1" Grid.Column="0" Margin="5,0,5,5" 
                Content="Load DynLoadApp1.XAP Application"
                FontSize="14"
                FontWeight="Bold" Click="btnApp1_Click">
        </Button>

        <Grid Grid.Row="2" Grid.Column="0" Name="MEFPanelApp1"></Grid>

        <Button Name="btnApp2"
                Grid.Row="1" Grid.Column="1" Margin="5,0,5,5"
                Content="Load DynLoadApp2.XAP Application"
                FontSize="14"
                FontWeight="Bold" Click="btnApp2_Click">                
        </Button>
        
        <Grid Grid.Row="2" Grid.Column="1" Name="MEFPanelApp2"></Grid>        
    </Grid>

Como puedes ver es muy simple, hemos dividido la pantalla en dos columnas, tenemos dos botones, cada uno de los cuales realiza la petición de carga de un paquete xap y dos grids (MEFPanelApp1 y MEFPanelApp2) que contendrán su correspondiente paquete XAP.

En los eventos Click de los botones realizamos la petición de carga de los paquetes:

private void btnApp1_Click(object sender, RoutedEventArgs e)
{
    MefLoader.CatalogService.AddXap("DynLoadApp1.xap");
    MefLoader.XapSelected = "DynLoadApp1.xap";
    MefLoader.GridSelected = MEFPanelApp1;
    MefLoader.LoadSelectedModule();
}

private void btnApp2_Click(object sender, RoutedEventArgs e)
{
    MefLoader.CatalogService.AddXap("DynLoadApp2.xap");
    MefLoader.XapSelected = "DynLoadApp2.xap";
    MefLoader.GridSelected = MEFPanelApp2;
    MefLoader.LoadSelectedModule();
}

Pasos Finales

Para que todo esto funcione sin mayor problema, deberemos tener en cuenta las siguientes instrucciones:

  • Debemos copiar los paquetes XAP de los plugins al directorio Bin donde se encuentra el paquete XAP del Host.
  • Si lanzamos la aplicación desde una ruta física, obtendremos una excepción NotSupportedException al cargar los plugins, debemos realizar la carga desde un servidor IIS o un servidor de desarrollo de Visual Studio.
  • Una vez que hayamos ejecutado desde un servidor web, podemos instalar la app Out Of the Browser y funcionará perfectamente.

Si tenemos estos pasos en cuenta y ejecutamos nuestra aplicación, presionando los botones deberíamos ser capaces de cargar los paquetes XAP como si de UserControls se tratasen:

image

image

image

Con esto ya tenemos MEF funcionando a nuestra disposición y la posibilidad de, de manera sencilla, realizar aplicaciones extensibles mediante Plugins.

Os dejo el código fuente de la solución y cualquier duda, aquí estoy para ayudaros.

Un saludo y Happy Coding

WP7, WPF y Silverlight: Un ControlTemplate, Tres plataformas.

Hola a todos

Siguiendo con mi idea de que debemos reutilizar la mayor parte posible de nuestro código, hoy vamos a ver como poder escribir Styles y ControlTemplates que se reaprovechen entre diferentes plataformas como son Windows Phone 7, WPF y Silverlight.

Anteriormente ya vimos como gracias a MVVM podíamos reutilizar la lógica de presentación (ViewModel) de nuestras vistas entre estas tres plataformas (parte 1 y parte 2), para interfaces de usuario simples no existe problema en diseñar por separado para cada plataforma las views, pero cuando nuestras interfaces de usuario necesitan ser más elaboradas, incluir un esquema de color específico, animaciones o efectos, mantener estos cambios por separado puede causarnos muchos dolores de cabeza y, por supuesto estamos nadando contra corriente, desarrollando lo mismo por triplicado.

Para evitar esto vamos a hacer uso de la capacidad de Visual Studio 2010 llamada Multi targeting, por medio de la cual podemos enlazar un archivo de un proyecto A a otros proyectos B y C, no copiándolo, simplemente manteniendo un enlace que permita a todos los proyectos trabajar sobre el mismo archivo.

También vamos a hacer uso del VisualStateManager de Silverlight/WPF. En WPF es muy común usar Triggers dentro de un ControlTemplate para cambiar el aspecto de un control en respuesta a cambios en eventos o propiedades (IsEnabled, MouseOver, Click…) Sin embargo en Silverlight no existe esta riqueza de Triggers, teniendo a nuestra disposición solamente un trigger que se ejecuta junto con el evento Loaded de la página en la que reside el control.

Para solventar este problema, tanto WPF como Silverlight (y por supuesto Windows Phone 7) soportan el VisualStateManager, dentro del cual podemos especificar grupos (VisualStateGroups) que a su vez contienen estados (Disabled, MouseOver, Pressed) y nos permiten ejecutar animaciones cuando el control entra a un estado en concreto, lo veremos más adelante con detenimiento.

1: Solución de trabajo.

Vamos a empezar por el primer paso, definir la solución con la que vamos a trabajar. Se compone de 3 proyectos, un proyecto WPF, un proyecto Silverlight y un proyecto Silverlight for Windows Phone, dentro de cada uno vamos a crear una carpeta Templates, quedando algo parecido a esto:

image

Una vez hecho esto, debemos crear en alguno de los proyectos un Diccionario de recursos, normalmente suelo elegir el más restrictivo de los proyectos, en este caso WPF soporta muchas opciones de Templates y Styles que no están disponibles en Silverlight o Windows Phone 7 (Property Triggers, Event Triggers…), por lo que he decidido crear el archivo en el directorio Templates del proyecto Silverlight. Una vez creado el diccionario de recursos original, vamos a enlazarlo al resto de proyectos.

image

Simplemente debemos ir a la opción “Add Existing Item” en el proyecto de Windows Phone 7 y WPF y seleccionar el diccionario de recursos que hemos creado en el proyecto Silverlight, pero en vez de presionar el botón “Add”, presionamos la flecha que se encuentra justo a su lado y del desplegable que aparece seleccionamos “Add as a link”, con esto se añadirá una referencia al archivo, pero físicamente seguirá existiendo solo uno, con lo que todos los cambios que realicemos se refrescarán automáticamente para todos los enlaces.

2: VisualStateManager vs Triggers

El VisualStateManager se introdujo en Silverlight y con la versión 4 de .NET lo tenemos disponible en WPF también. Su función es la de controlar la apariencia de un control dependiendo de su estado visual, esto es, podemos definir dentro de nuestro Control Template que estados visuales soporta nuestro control, ya sean standard (normal, mouseover, pressed, disabled) o definidos por nosotros mismos. En el caso de los estados standard, automáticamente se entrará en cada estado dependiendo del control mientras que para los estados definidos propios deberemos ser nosotros quienes indiquemos al control que se encuentra en un estado concreto con el método GoToState. Así mismo también nos permite definir opciones para las transiciones entre estados como duración de la transición, a que combinación de origen/destino de estados queremos aplicar la configuración de transición o incluso una animación a ejecutar para llevar a cabo la transición.

<ControlTemplate TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <!--Take one half second to transition to the MouseOver state.-->
                    <VisualTransition From="Normal" To="MouseOver" GeneratedDuration="0:0:0.5" />
                </VisualStateGroup.Transitions>
                <!-- NORMAL VISUAL STATE -->
                <VisualState x:Name="Normal">
                </VisualState>
                <!-- MOUSE OVER VISUAL STATE -->
                <VisualState x:Name="MouseOver"> 
                    <Storyboard>
                        <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" 
                                        Storyboard.TargetProperty="Color" To="Cyan"/>
                    </Storyboard>
                </VisualState>
                <!-- PRESSED VISUAL STATE -->
                <VisualState x:Name="Pressed"> 
                    <Storyboard>
                        <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" 
                                        Storyboard.TargetProperty="Color" To="Red"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse>
            <Ellipse.Fill>
                <SolidColorBrush x:Name="borderColor" Color="Black"/>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse x:Name="defaultOutline" Stroke="{TemplateBinding Background}" StrokeThickness="2" Margin="2"/>
        <Ellipse x:Name="ButtonShape" Margin="5" Fill="{TemplateBinding Background}"/>
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</ControlTemplate>

¿Significa esto que ya no debemos usar los Triggers nunca más? No exactamente, cuando necesitemos disparar acciones o cambios que no impliquen explícitamente un cambio visual del control podemos seguir usando los Triggers perfectamente, pero VisualStateManager nos facilita la creación de animaciones y transiciones entre estados visuales de un control. Además tenemos la ventaja de que el VisualStateManager es totalmente compatible con Silverlight y Windows Phone 7, por lo que es un paso indispensable si pensamos compartir nuestras plantillas entre WPF, Silverlight y WP7. Por descontado, en un mismo Control Template podemos usar Triggers y VisualStateManager al mismo tiempo sin ningún problema.

3: Creando nuestro ControlTemplate común

Bueno, como la teoría siempre es sencilla, vamos a ver un ejemplo practico para ilustrar el uso real del Visual State Manager y las distintas peculiaridades para aplicarla a las tres plataformas objetivo (Silverlight, Windows Phone 7 y WPF).

Tenemos que recordar, que el VisualStateManager se debe declarar en el elemento raíz que componga nuestro Control Template:

    <ControlTemplate x:Key="template" TargetType="Button">
        <Grid x:Name="Grd">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal">
                        <Storyboard FillBehavior="HoldEnd">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).Color" Storyboard.TargetName="borde">
                                <EasingColorKeyFrame KeyTime="0:0:0.5" Value="DarkGray"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="MouseOver">
                        <Storyboard FillBehavior="HoldEnd">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).Color" Storyboard.TargetName="borde">
                                <EasingColorKeyFrame KeyTime="0:0:0.5" Value="Red"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>                    
                    <VisualState x:Name="Pressed">
                        <Storyboard FillBehavior="HoldEnd">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.Background).Color" Storyboard.TargetName="borde">
                                <EasingColorKeyFrame KeyTime="0:0:0.5" Value="LightBlue"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Disabled">
                        <Storyboard FillBehavior="HoldEnd">
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Grd">
                                <EasingDoubleKeyFrame KeyTime="0" Value=".3"/>
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>                    
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            
            <Border x:Name="borde" BorderBrush="Red" BorderThickness="1" Background="DarkGray" CornerRadius="10,0,10,0">            
            </Border>
            <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"></ContentPresenter>
        </Grid>
    </ControlTemplate>

En este caso, usaremos un Style para aplicar el Control Template y modificar algunas propiedades extras del botón relacionadas con la fuente de letra usada:

    <Style TargetType="Button" x:Key="ButtonTemplate">
        <Setter Property="Template" Value="{StaticResource template}"></Setter>
        <Setter Property="Foreground" Value="DarkBlue"></Setter>
        <Setter Property="FontWeight" Value="Bold"></Setter>
        <Setter Property="FontSize" Value="16"></Setter>
    </Style>

Fijaros que en el Style he especificado una Key, lo que hará que no se aplique de forma automática. Esto es así porque en Windows Phone 7 los estilos no se aplican por defecto (siempre se aplica el estilo por defecto de Windows) y hay que indicarlo de forma manual, el ControlTemplate si que se aplicaría instantáneamente, pero tendríamos las propiedades de fuente de letra standard y en esta plantilla deseaba que fuesen otras.

Una vez hecho esto en nuestro resource dictionary, si lo abrimos desde cualquiera de los 3 proyectos veremos que tenemos el mismo código, por lo que solo nos quedan unos pocos pasos para poder ejecutar nuestros tres front-ends:

Lo primero es que establezcamos en las propiedades de nuestro ResourceDictionary (en cada proyecto) la propiedad BuildAction a Resource y Copy to Output directory a Do Not Copy.

Ahora deberemos referenciar en cada app.xaml a nuestro nuevo ResourceDictionary:

    <Application.Resources>
        <ResourceDictionary Source="Templates/Templates.xaml"></ResourceDictionary>
    </Application.Resources>

 

Daros cuenta de que usamos la barra / para indicar la ruta, en vez de usar la barra de directorio normal , esto es así debido a que tenemos el archivo enlazado y no existe físicamente.

En cada proyecto tenemos una página/ventana principal con un botón, le establecemos nuestro nuevo estilo a ese botón como lo haríamos normalmente:

<Button Content="Button" Height="75" Margin="9,6,9,0" Name="button1" 
        VerticalAlignment="Top"  Style="{StaticResource ButtonTemplate}" />

 

Con esto hemos terminado de configurar nuestra plantilla y aplicaciones, podemos ejecutar o incluso abrir la vista de diseño de XAML de cada proyecto y veremos inmediatamente el resultado:

Capture

 

Aquí os dejo el código fuente del artículo para que podáis trastear con el y para cualquier duda aquí espero vuestros comentarios, igual que en twitter @JosueYeray o en MSDN.

Un abrazo y Happy Coding!

[FOROS] Mostrar y Obtener varios valores en un ComboBox / ListBox

Hola a todos!

Este post es una especie de “autoresumen” sobre como Mostrar varios valores al mismo tiempo en un item de un combobox (o cualquier otro control de listas) y poder obtenerlos en los eventos selectionchanged, he visto que hay gente en los foros de MSDN no sabe muy bien como usar las datatemplates y los databinding para hacer esto de forma sencilla.

Vamos a partir de una aplicación WPF con un combo, le añadimos una nueva clase que se llame ComboItem y que contenga tres propiedades públicas: Id, Nombre y Apellido:

public class ComboItem
{
    public ComboItem()
    {
    }
    public int Id { get; set; }

    public string Nombre { get; set; }

    public string Apellidos { get; set; }
}

 

Ahora, en la clase window donde tenemos nuestro combo, vamos a crear una lista de la clase ComboItem y rellenar algunos datos para poder verlos en el combo:

public partial class MainWindow : Window
{
    //Lista que actuará como itemssource de nuestro combo.
    List<ComboItem> datos = new List<ComboItem>();

    public MainWindow()
    {
        InitializeComponent();

        //Datos de prueba.
        datos.Add(new ComboItem() { Id = 1, Nombre = "Pepito", Apellidos = "Grillo" });
        datos.Add(new ComboItem() { Id = 2, Nombre = "Juan", Apellidos = "Cosa" });
        datos.Add(new ComboItem() { Id = 3, Nombre = "Yeray", Apellidos = "Julián" });

        //Establecemos el itemssource del combo a nuestra lista.
        comboBox1.ItemsSource = datos;
    }
}

Si ejecutamos ahora, veremos que efectivamente tenemos 3 items en el combo, pero nos muestra el tipo de objeto, no los datos. Esto es así por que todavía nos queda por indicarle a WPF la forma en la que queremos que los muestre, aquí entran en juego las DataTemplates, vamos a hacer una DataTemplate que nos muestre nombre y apellidos, en xaml, dentro del TAG del ComboBox vamos a crear un ItemTemplate y dentro de este un DataTemplate:

<ComboBox Height="23" HorizontalAlignment="Left" Margin="212,116,0,0" Name="comboBox1" 
            VerticalAlignment="Top" Width="120">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Nombre: "></TextBlock>
                <TextBlock Text="{Binding Path=Nombre}" Width="80"></TextBlock>
                <TextBlock Text="Apellidos: "></TextBlock>
                <TextBlock Text="{Binding Path=Apellidos}" Width="80"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Como podéis ver, un DataTemplate simplemente indica a WPF como componer la apariencia de un item y dentro de los textblock estamos enlazando el texto a la propiedad de nuestra clase que queremos que muestre.

Si ejecutamos de nuevo, veremos que ya salen correctamente los datos, y ahora… ¿Que pasa cuando queremos recuperar el item seleccionado? Pues como la DataTemplate solo es visual, realmente en el Selectionchanged vamos a obtener una referencia a un Item de la clase ComboItem que creamos con todos sus valores, es la gran ventaja de este sistema, aunque solo muestro Nombre y Apellidos, como en mi clase tengo un ID, al recuperar el item seleccionado puedo acceder a ese ID:

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ComboItem elemento = (ComboItem)e.AddedItems[0];
    MessageBox.Show("Has seleccionado el Item ID:" + elemento.Id.ToString());
}

De esta forma si ejecutamos, y seleccionamos un item, obtendremos un messagebox con el Id de ese Item.

 

Espero que os haya sido de utilidad este mini artículo sobre databinding y datatemplates, os dejo el código del ejemplo para que juguéis con el…

Un saludo y Happy Coding

Ultimo post del año: Recopilando experiencias

Hola a todos.

Este 2010 ha sido un gran año personal y profesionalmente hablando, con grandes cambios y logros.

Uno de esos grandes cambios, empezar a escribir en Geeks.ms. El más grande de todos, irme a Bilbao a trabajar con la estupenda gente de Plain Concepts.

Estas últimas semanas también han sido fructíferas, montar junto con José Fortes, Alberto Díaz y David Rodríguez el grupo de usuarios TenerifeDev y dar nuestras primeras charlas en La Universidad de La Laguna, en Tenerife.

2011 va a ser un año movidito, lleno de nuevos proyectos y experiencias, espero que todas tan positivas como las de este 2010. Ya os iré contando por aquí o por twitter todas las novedades que vaya teniendo.

Muchas gracias por leerme, porque de lo contrario no tendría sentido escribir este blog.

Os deseo un feliz final de año y buena entrada en el siguiente, un abrazo a todos!

[MATERIALES] Windows Azure y Windows Phone 7 en Tenerife.

TenerifeDev

winphone

winazure

pc_logo_white

 

Hola a todos!

DSC_0712

Ayer 22 de Diciembre celebramos en Tenerife la primera reunión del grupo de usuarios de .NET TenerifeDev, y para empezar con buen pie realizamos 3 charlas en la Universidad de La Laguna sobre Windows Azure y Windows Phone 7.

José Fortes y David Rodríguez nos ofrecieron una clase magistral sobre Windows Azure, yo por mi parte pude enseñar un poco de la nueva nave insignia de Microsoft: Windows Phone 7.

En medio de todos, organizando todo el evento y dinamizando las charlas estuvo Alberto Díaz, un gran profesional!

La gente respondió muy bien, en tan solo 24 horas de convocatoria se reunieron unas 30 personas en las charlas.

DSC_0720

Pudimos sacar fotos y grabar un video resumen de las mismas, aquí os dejo los enlaces:

Videos:

Primera Parte y Segunda Parte

Fotos:

http://bit.ly/TenerifeDev2010

 También os dejo los materiales de mi charla sobre Windows Phone 7:

Por ahora no tenemos web para el grupo de usuarios, pero podéis seguir toda nuestra actividad en la cuenta de Twitter: @TenerifeDev o escribiéndonos a nuestro correo: tenerifedev@live.com

 Quiero agradecer a mis compañeros de charla Alberto Díaz, José Fortes y David Rodríguez el haber estado allí, ha sido un verdadero placer compartir con ellos estás sesiones y aprender de mano de los mejores un poco más sobre azure.

También agradecer a todos los asistentes, fueron un público genial que se integró en las sesiones y con el que pudimos hablar y discutir ideales, posturas y acercarnos a un punto más en común para todos.

 

Un saludo, Felices Fiestas y Happy Coding!

[CHANNEL9] Construir aplicaciones Silverlight con MVVM

 

Hola a todos!

Acaban de publicar en Channel 9 Spain un vídeo que realice hace algunas semanas explicando los conceptos fundamentales sobre como construir aplicaciones Silverlight haciendo uso del patrón MVVM, si no has usado MVVM nunca, quizás te venga bien verlo y empezar a hacer que te código sea más limpio y reutilizable:

http://channel9.msdn.com/Blogs/channel9spain/Crear-aplicaciones-Silverlight-con-MVVM

 

Espero que os guste, un gran saludo y Happy Coding

[EVENTO] Windows Azure & Windows Phone 7 en Tenerife

winphone

winazure

pc_logo_white

 

Hola a todos!

Aprovechando que tengo unos días de vacaciones en Tenerife para ver a mi familia me he juntado con Alberto Díaz, José Fortes y David Rodríguez para montar unas sesiones sobre Windows Azure y Windows Phone 7

Será todo un placer estar al lado de Titanes como Alberto Díaz, José Fortes y David Rodríguez, si queréis enteraros de que está pasando en el mundo… no os lo podéis perder.

¿Cuando? Pues… mañana día 22… ha sido un visto y no visto y organizar todo lo más rápido posible.

Os podéis registrar, aunque no será obligatorio, en:

https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032473610&Culture=es-ES

Si os registráis nos ayudareis a demostrar a MSFT que en Tenerife también interesa .NET y que hay que venir más a menudo a las islas!

Agenda:

Día: 22 de diciembre de 2010

Hora comienzo/fin: 17:30-20:15

Lugar: Salón de Grados, Escuela Técnica Superior de Ingeniería Informática

Universidad de La Laguna, Camino San Francisco de Paula s/n
38271, La Laguna, Tenerife, SPAIN

La agenda es la siguiente:

  • 17:30 Bienvenida y presentación de las sesiones
  • 17:30 Cloud Computing con Windows Azure (José Fortes)
  • 18:15 Desarrolla con Windows Azure (Alberto Díaz)
  • 19:00 Descanso
  • 19.15 Desarrolla con Windows Phone 7 (Yeray Julián)

Nos vemos allí, un abrazo y Happy Coding!

[FOTOS] Programación en Windows Phone 7 con Silverlight

Hola a todos!

Aquí os dejo algunas fotos que me sacó Javier torrecilla durante la charla del día 14 sobre windows phone 7

2010-12-14 20.03.56

(En esta…. bueno, les estaba enseñando que aplicaciones NO usar bajo ningún concepto… xDDD)

 

2010-12-14 20.07.42

(enseñando el uso del acelerómetro en mi HTC 7 Trophy)

 

2010-12-14 20.09.04

(Decid conmigo… “Windows Phone 7 es mejor que iphone…”)

 

Muchas gracias Javier por sacar las fotos, a todos los que asistieron gracias por estar allí e interesaros por WP7 y esperemos vernos pronto en otra

 

Happy Coding!