Archive

Archive for avril 2011

[SL5] : Silverlight WPF’s {x:Static} MarkupExtension

Silverlight 5 Beta fournit à présent la classe de base MarkupExtension qui lorsqu’on implémente la méthode ProvideValue(IServiceProvider), permet de définir une MarkupExtension customisée, qui peut être interprétée par le parseur de XAML de Silverlight 5.

Ceci permet de réaliser des tâches qui n’étaient pas possible, plutot complexes ou verbeuse à mettre en place. Par exemple il est à présent possible de réaliser un comportement similaire à ce que l’on connait en WPF tel que {x:Type}, {x:Static}, etc.

Dans ce post nous allons voir comment réaliser l’équivalent de x:Static connu en WPF utilisé dans certains cas comme le référencement de constantes dans le XAML. Les adeptes de PRISM l’utilisent généralement pour nommer les régions suivant des constantes définies dans l’infrastructure du projet.

Généralités

Pour faire une MarkupExtension vous devez implémenter une classe dérivant de l’interface IMarkupExtension<T> ou la classe de base MarkupExtension.


    public class YourMarkupExtension: IMarkupExtension<Object>
    {
        public Object ProvideValue(IServiceProvider serviceProvider)
        {
                throw new NotImplementedException();
        }
    }

Implémentation du comportement de {x:Static}


using System;
using System.Windows.Markup;
using System.Reflection;
using System.Xaml;

namespace SL5MarkupExtension.MarkupExtensions
{
    /// <summary>
    ///  Classe permettant de faire une MarkupExtension XAML pour récupérer les valeurs de champs ou propriétés.
    /// </summary>
    public class Static : IMarkupExtension<Object>
    {
        #region Data
        private String _member;
        private Type _memberType;
        #endregion

        #region Ctors
        /// <summary>
        ///  Crée une instance de la classe permettant de faire une MarkupExtension XAML pour récupérer les valeurs de champs ou propriétés.
        /// </summary>
        public Static()
        {
        }
        #endregion

        #region MarkupExtension implementation
        /// <summary>
        ///  Obtient un objet correspondant au type fournit en paramètre. Permet de récupérer un champ ou une propriété
        /// </summary>
        /// <param name="serviceProvider">Service permettant de récuperer le service utilisé pour les MarkupExtension.</param>
        /// <returns>
        ///  Un objet correspondant à la chaine fournie en paramettre (Member)
        /// </returns>
        public Object ProvideValue(IServiceProvider serviceProvider)
        {
            Object ret = null;
            Boolean typeResolveFailed = true;
            Type type = MemberType;
            String fieldMemberName = null;
            String fullFieldMemberName = null;

            if (Member != null)
            {
                if (MemberType != null)//on a le type et le membre
                {
                    fieldMemberName = Member;
                    fullFieldMemberName = String.Format("{0}.{1}", type.FullName, Member);
                }
                else //on a pas le type, on regarde si la chaine est bien formatée ex : local:MyEnum.MyEnumeValue et on essaie de résoudre le type
                {
                    Int32 index = Member.IndexOf('.');

                    if (index >= 0)
                    {
                        string typeName = Member.Substring(0, index);

                        if (!String.IsNullOrEmpty(typeName))
                        {
                            IXamlTypeResolver xamlTypeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;

                            if (xamlTypeResolver != null)
                            {
                                type = xamlTypeResolver.Resolve(typeName);
                                fieldMemberName = Member.Substring(index + 1); //, Member.Length - index - 1
                                typeResolveFailed = String.IsNullOrEmpty(fieldMemberName);
                            }
                        }
                    }
                }

                if (typeResolveFailed)
                {
                    throw new InvalidOperationException("Member");
                }
                else
                {
                    if (type.IsEnum) //si c'est un enum alors on essaie de résoudre le membre
                    {
                        ret = Enum.Parse(type, fieldMemberName, true);
                    }
                    else //ce n'est pas un enum : probablement un champ ou une propriété
                    {
                        Boolean fail = true;

                        FieldInfo field = type.GetField(fieldMemberName, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static);

                        if (field != null)
                        {
                            fail = false;
                            ret = field.GetValue(null);
                        }
                        else//ce n'est pas un champ, on regarde si c'est une propriété
                        {
                            //on regarde si c'est une propriété
                            PropertyInfo property = type.GetProperty(fieldMemberName, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static);

                            if (property != null)//c'est une propriété
                            {
                                fail = false;
                                ret = property.GetValue(null, null);
                            }
                        }

                        if (fail)
                        {
                            throw new ArgumentException(fullFieldMemberName);
                        }
                    }
                }
            }
            else
            {
                throw new InvalidOperationException();
            }

            return ret;
        }
        #endregion

        #region Membres
        /// <summary>
        /// Obtient ou définit le membre statique à résoudre ex : local:MyEnum.MyEnumValue si pas de MemberType sinon MyEnumValue
        /// </summary>
        public string Member
        {
            get
            {
                return _member;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("Member value");
                }

                _member = value;
            }
        }

        /// <summary>
        /// Obtient ou définit le type du membre à résoudre
        /// </summary>
        public Type MemberType
        {
            get
            {
                return _memberType;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("MemberType value");
                }

                _memberType = value;
            }
        }
        #endregion
    }
}

Action !

Sample objects

Nous définissons ici une classe Module contenant 3 constantes


public class Modules
    {
        public static readonly string ModuleA = "NewsModule";
        public static readonly string ModuleB = "MediasModule";
        public static readonly string ModuleC = "CalendarModule";
    }

Puis un enum contenant 3 valeurs


    public enum Gender
    {
        Homme,
        Femme,
        Indefini
    }

Mise en pratique

Tout d’abord vous devez référencer votre MarkupExtension et les objets que vous souhaitez référencer.

xmlns:markup="clr-namespace:SL5MarkupExtension.MarkupExtensions"
xmlns:local="clr-namespace:SL5MarkupExtension"

Puis vous n’avez plus qu’à l’utiliser dans votre XAML avec la syntaxe suivante :

{markup:Static Member=local:Modules.ModuleA}

Exemple complet

<UserControl x:Class="SL5MarkupExtension.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:markup="clr-namespace:SL5MarkupExtension.MarkupExtensions"
             xmlns:local="clr-namespace:SL5MarkupExtension"
             xmlns:app="clr-namespace:System.Windows;assembly=System.Windows">

    <Grid x:Name="LayoutRoot"
          Background="White">
        <StackPanel VerticalAlignment="Center"
                    HorizontalAlignment="Center">
            <!--CONST-->
            <TextBlock Text="{markup:Static Member=local:Modules.ModuleA}" />
            <TextBlock Text="{markup:Static Member=local:Modules.ModuleB}" />

            <!--CLASS-->
            <TextBlock Text="{Binding Path=InstallState, Source={markup:Static Member=app:Application.Current}}" />

            <!--ENUM-->
            <TextBlock Text="{Binding Source={markup:Static Member=local:Gender.Homme}}" />
        </StackPanel>
    </Grid>
</UserControl>

Résultat

silverlight-x-static-markupextension-like-wpf

Le résultat est très intéressant et ouvre de nouvelles possibilités dans le développement Silverlight 🙂

 

Update 4/7/2012 :
Said Outgajjouft m’a fait part d’une refactorisation du code pour le rendre plus clair, merci à lui 😉

using System;
    using System.Reflection;
    using System.Windows.Markup;
    using System.Xaml;


    /// <summary>
    /// Provides WPFs StaticExtension to Silverligth.
    /// The Static class implements a markup extension that returns static field and property references. 
    /// </summary>
    public class StaticExtension : IMarkupExtension<object>
    {
        /// <summary>"StaticExtension  must have Member property set before ProvideValue can be called.</summary>
        private const string MarkupExtensionStaticMember = "StaticExtension must have Member property set before ProvideValue can be called.";
        /// <summary>'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property.</summary>
        private const string MarkupExtensionBadStatic = "'{0}' StaticExtension value cannot be resolved to an enumeration, static field, or static property.";
        /// <summary>Markup extension '{0}' requires '{1}' be implemented in the IServiceProvider for ProvideValue.</summary>
        private const string MarkupExtensionNoContext = "Markup extension '{0}' requires '{1}' be implemented in the IServiceProvider for ProvideValue.";

        private string _member;
        private Type _memberType;


        /// <summary>Gets or sets a member name string that is used to resolve a static field or property based on the service-provided type resolver.</summary>
        /// <exception cref="ArgumentNullException">Attempted to set <see cref="Member"/> to <b>null</b>.</exception>
        public string Member
        {
            get { return _member; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _member = value;
            }
        }


        /// <summary>Gets or sets the <see cref="Type"/> that defines the static member to return.</summary>
        /// <exception cref="ArgumentNullException">Attempted to set <see cref="MemberType"/> to <b>null</b>.</exception>
        public Type MemberType
        {
            get { return _memberType; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _memberType = value;
            }
        }


        /// <summary>
        /// Returns an object value to set on the property where you apply this extension.
        /// For <see cref="StaticExtension"/>, the return value is the static value that is evaluated for the requested static member.
        /// </summary>
        /// <param name="serviceProvider">
        /// An object that can provide services for the markup extension. The service provider is expected to provide a service 
        /// that implements a type resolver (<see cref="IXamlTypeResolver"/>).
        /// </param>
        /// <returns>The static value to set on the property where the extension is applied.</returns>
        /// <exception cref="InvalidOperationException">The <see cref="Member"/> for the extension is <b>null</b> at the time of evaluation.</exception>
        /// <exception cref="ArgumentException">
        /// <p>Some part of the <see cref="Member"/> string did not parse properly</p>
        /// <p>-or-</p>
        /// <p><paramref name="serviceProvider"/> did not provide a service for <see cref="IXamlTypeResolver"/></p>
        /// <p>-or-</p>
        /// <p><see cref="Member"/> value did not resolve to a static member.</p>
        /// </exception>
        /// <exception cref="ArgumentNullException"><paramref name="serviceProvider"/> is <b>null</b>.</exception>
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            var member = Member;
            var memberType = MemberType;
            string memberName;


            if (member == null)
                throw new InvalidOperationException(MarkupExtensionStaticMember);

            if (memberType != null)
            {
                memberName = member;
            }
            else
            {
                var index = member.IndexOf('.');
                if (index == -1)
                    throw new ArgumentException(string.Format(MarkupExtensionBadStatic, member));

                var qualifiedTypeName = member.Substring(0, index);
                if (qualifiedTypeName.Length == 0)
                    throw new ArgumentException(string.Format(MarkupExtensionBadStatic, member));

                if (serviceProvider == null)
                    throw new ArgumentNullException("serviceProvider");

                var xamlTypeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
                if (xamlTypeResolver == null)
                    throw new ArgumentException(string.Format(MarkupExtensionNoContext, GetType().Name, typeof(IXamlTypeResolver).Name));

                memberType = xamlTypeResolver.Resolve(qualifiedTypeName);
                memberName = member.Substring(index + 1);
                if (memberName.Length == 0)
                    throw new ArgumentException(string.Format(MarkupExtensionBadStatic, member));
            }

            if (memberType.IsEnum)
                return Enum.Parse(memberType, memberName, true);

            var field = memberType.GetField(memberName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
            if (field != null)
                return field.GetValue(null);

            var property = memberType.GetProperty(memberName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
            if (property != null)
                return property.GetValue(null, null);

            throw new ArgumentException(string.Format(MarkupExtensionBadStatic, memberType.FullName + "." + memberName));
        }
    }

 

Publicités
Catégories :Silverlight, Silverlight 5, WPF

[SL5] : Silverlight 5 Implicit DataTemplate

Très connu en WPF, la notion de DataTemplate implicites est nouvelle en Silverlight 5. Afin de comprendre la puissance et la simplicité de codage que cela apporte nous allons réaliser deux exemples représentatifs de son utilisation.

Cas d’utilisation pour une composition d’IHM

Ce mode là est souvent utilisé en WPF pour réaliser une composition de l’interface via le patter ViewModelFirst qui consiste à substituer une donnée (ex classe) par une vue spécifique.

Pour illustrer cela nous allons réaliser 3 classes basiques ClassA, ClassB et ClassC et une classe GlobalViewModel contenant 3 propriétées PropertyA de type ClassA, PropertyB de type ClassB et PropertyC de type ClassC.

Les classes

 public class ClassA
    {
    }

    public class ClassB
    {
    }

    public class ClassC
    {
    }

    public class GlobalViewModel
    {
        public GlobalViewModel()
        {
            PropertyA = new ClassA();
            PropertyB = new ClassB();
            PropertyC = new ClassC();
        }
        public ClassA PropertyA
        {
            get;
            set;
        }

        public ClassB PropertyB
        {
            get;
            set;
        }

        public ClassC PropertyC
        {
            get;
            set;
        }
    }

Controles Utilisateurs

Nous allons réaliser 3 UserControls UC1, UC2 et UC3 correspondants respectivement aux classes ClassA, ClassB et ClassC.

UC1
<UserControl x:Class="Silverlight5Tests.UC1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Background="GreenYellow">
        <TextBlock Text="A"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>
UC2
<UserControl x:Class="Silverlight5Tests.UC2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid Background="DarkCyan">
        <TextBlock Text="B"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>

UC3
<UserControl x:Class="Silverlight5Tests.UC3"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Background="BlanchedAlmond">
        <TextBlock Text="C"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>

La vue principale

Ensuite nous allons créer une vue principale dans laquelle nous souhaitons afficher les 3 contrôles de façon automatique

<navigation:Page x:Class="Silverlight5Tests.DataTypeTest"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:local="clr-namespace:Silverlight5Tests"
                 Title="DataType Page">
    <navigation:Page.Resources>
        <DataTemplate DataType="local:ClassA">
            <local:UC1 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassB">
            <local:UC2 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassC">
            <local:UC3 />
        </DataTemplate>
    </navigation:Page.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--UC1-->
        <ContentControl Content="{Binding PropertyA}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch" />
        <!--UC2-->
        <ContentControl Content="{Binding PropertyB}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch"
                        Grid.Column="1" />
        <!--UC3-->
        <ContentControl Content="{Binding PropertyC}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch"
                        Grid.Row="1"
                        Grid.ColumnSpan="2" />

    </Grid>
</navigation:Page>

Puis la magie s’effectue grâce a la nouvelle propriété DataType héritée de ce que nous connaissons en WPF. Vous pouvez mettre cela en ressource locale ou tout simplement en ressource globale à l’application si vous en avez besoin à d’autres endroits.
Nous réalisons 3 DataTemplate représentants nos 3 classes avec leurs UserControls associés.

  <navigation:Page.Resources>
        <DataTemplate DataType="local:ClassA">
            <local:UC1 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassB">
            <local:UC2 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassC">
            <local:UC3 />
        </DataTemplate>
    </navigation:Page.Resources>

Résultat

image

L’intérêt de cette solution est de s’abstraire le plus possible et de ne pas avoir à référencer un UserControl à chaque fois qu’on en a besoin. Ainsi le changement du DataTemplate provoquera automatiquement le changement du comportement de votre application sans avoir à modifier l’ensemble de l’application.

Cas d’une liste hétérogène

Prenons le cas d’un liste de User contenant quelques champs comme le nom, le prénom et une ville. Nous disposons aussi de deux autres classes dérivées nommées NormalUser et SuperUser.

Grace au DataTemplate explicites nous pouvons réaliser une affichage différent pour chaque types de User. Tout cela en quelques lignes de XAML…

Notre modèle de données


    public abstract class UserBase
    {
        public String LastName
        {
            get;
            set;
        }

        public String FirstName
        {
            get;
            set;
        }

        public String Town
        {
            get;
            set;
        }
    }

    public class NormalUser : UserBase
    {
    }

    public class SuperUser : UserBase
    {
        public String SampleProperty
        {
            get;
            set;
        }
    }

UserControl d’un utilisateur normal

<UserControl x:Class="Silverlight5Tests.UserItemView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Border BorderBrush="#FF8D8D8D" BorderThickness="0,0,0,1" Background="#FFCDD7FF">

		<StackPanel Margin="5">
			<TextBlock Text="{Binding LastName}" />
			<TextBlock Text="{Binding FirstName}" />
		</StackPanel>
	</Border>
</UserControl>

UserControl d’un super utilisateur

<UserControl x:Class="Silverlight5Tests.SuperUserItemView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Border BorderThickness="0,0,0,1" Background="#FFFFB396" BorderBrush="#FF8D8D8D">

		<StackPanel Margin="5">
			<TextBlock Text="{Binding LastName}" />
			<TextBlock Text="{Binding FirstName}" />
		</StackPanel>
	</Border>
</UserControl>

Affichage de la liste des utilisateurs

Pour cela il suffit de déclarer les DataTemplates explicites correspondant aux classes NormalUser et SuperUser.

XAML
<navigation:Page x:Class="Silverlight5Tests.ImplicitTemplates"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:local="clr-namespace:Silverlight5Tests"
                 Title="ImplicitTemplates Page">
    <Grid x:Name="LayoutRoot">
        <ItemsControl ItemsSource="{Binding}">
            <ItemsControl.Resources>
                <DataTemplate DataType="local:NormalUser">
                    <local:UserItemView />
                </DataTemplate>
                <DataTemplate DataType="local:SuperUser">
                    <local:SuperUserItemView />
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>
    </Grid>
</navigation:Page>

CS
using System;
using System.Collections.Generic;
using System.Windows.Controls;

namespace Silverlight5Tests
{
    public partial class ImplicitTemplates : Page
    {
        public ImplicitTemplates()
        {
            InitializeComponent();

            DataContext = new List()
            {
                new NormalUser()
                {
                    LastName = "LOUGE",
                    FirstName = "Stéphane",
                    Town = "Biarritz"
                },
                 new SuperUser()
                {
                    LastName = "GATES",
                    FirstName = "Bill",
                    Town = "Seattle"
                },
                 new NormalUser()
                {
                    LastName = "HULOT",
                    FirstName = "Nicolas",
                    Town = "Paris"
                },
                new NormalUser()
                {
                    LastName = "VIGIE",
                    FirstName = "Julie",
                    Town = "Iron"
                }
            };
        }
    }

}

Resultat

Silverlight5ImplicitDataTemplate

Catégories :Silverlight, Silverlight 5

[SL5] : Silverlight 5 TextSearch on ItemsControl

Autre nouveauté de Silverlight 5 est la faculté de pouvoir rechercher des informations sans un itérateur grace au clavier en tappant du texte.

A titre d’exemple voici un objet tout simple représentant un utilisateur :

 public class User
    {
        public String FirstName
        {
            get;
            set;
        }
        public Int32 Age
        {
            get;
            set;
        }
    }

Maintenant nous souhaiterions pouvoir selectionner un user dans la liste en foncion de ce que nous tappons au clavier, pour cela il suffit de rajouter la propriété attachée TextSearch.TextPath= »NomDeLaProprieteOuLaRechercheDoitEtreFaite » :

  <ListBox ItemsSource="{Binding}"
                 TextSearch.TextPath="FirstName"
                 Width="200"
                 Height="200"
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center"
                 DisplayMemberPath="FirstName" />

Puis une petit séquence de code pour notre jeu de données factice :

InitializeComponent();

            DataContext = new List<User>()
            {
                new User()
                {
                    FirstName= "Jacques",
                    Age=20
                },
                new User()
                {
                    FirstName= "Nicolas",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Pierre",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Paul",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Antoine",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Rémi",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Julie",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Stéphane",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Stéphanie",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Edmond",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Cynthia",
                    Age=20
                },
                 new User()
                {
                    FirstName= "Jean",
                    Age=20
                },
                new User()
                {
                    FirstName= "Bill",
                    Age=20
                },
                new User()
                {
                    FirstName= "Steve",
                    Age=20
                }
            }.OrderBy(x => x.FirstName);

Ainsi en tappant « S » cela a pour effet de sélectionner directement l’occurence Stéphane, si vous continuez jusqu’à « Stéphani » la ListBox sélectionnera « Stéphanie ».

ListBoxTextSearchSilverlight5

Catégories :Silverlight, Silverlight 5

[SL5] : Silverlight 5 Binding news

Dans cette nouvelle mouture, Silverlight 5 rattrape petit à petit une partie de son retard sur WPF. Nous allons voir ici que nous pouvons à présent utiliser les attributs FindAncestor et AncestorType afin d’effectuer une recherche sur un parent grace à une notion de niveau et de type recherché.

Quand la l’élément source est égal au plus proche des parents d’un type donné (ici renvoie Grille1)

<Grid x:Name="Grille1">
<StackPanel>
<TextBlock Text="{Binding Name,  RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}" />
</StackPanel>
</Grid>

Quand vous souhaitez le nième parent d’un type donné ex le deuxième parent du type Grid (ayant le nom Grille1 et non Grille2 comme dans l’exemple précédent)

<Grid x:Name="LayoutRoot">
        <Grid x:Name="Grille1">
            <Grid x:Name="Grille2">
                <StackPanel>
                    <TextBlock Text="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid, AncestorLevel=2}}" />
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
Catégories :Silverlight, Silverlight 5

[SL5] : Silverlight 5 XAML Debugging

Il est a présent possible d’ajouter un BreakPoint directement dans le XAML afin de pouvoir identifier les erreurs de Binding par exemple.

 

Ajout d’un BreakPoint sur le XAML

Pour cela il suffit d’ajouter de point d’arrêt comme vous le faite habituellement dans les classes .net.

image

 

Visionnage des informations d’un Binding

A l’exécution et au changement de valeur d’un Binding vous en serez notifié et vous pourrez identifier les erreurs qui autrefois auraient nécessité un converter…

image

 

Gestion des erreurs

Ici nous modifions volontairement Value en ValueA afin de provoquer une erreur de Binding.

Dans le détail de l’erreur nous pouvons voir explicitement l’erreur qui a provoqué le dysfonctionnement :

image

Ainsi dans Error nous pouvons voir :

BindingState    {Error: System.Exception: Erreur System.Windows.Data : erreur de chemin d’accès BindingExpression : propriété ‘ValueA’ introuvable sur ‘System.Windows.Controls.Slider Minimum:0 Maximum:1000 Value:0’ ‘System.Windows.Controls.Slider’ (HashCode=17271312). BindingExpression : Path=’ValueA’ DataItem=’System.Windows.Controls.Slider Minimum:0 Maximum:1000 Value:0′ (HashCode=17271312); l’élément cible est ‘System.Windows.Controls.TextBlock’ (Name= »); la propriété cible est ‘CharacterSpacing’ (type ‘System.Int32’)..}    object {System.Windows.Data.Debugging.BindingDebugState}

ValueA = NotFound Sourire

Catégories :Silverlight, Silverlight 5

[SL5] : Silverlight 5 Text improvements

Nouveauté de silverlight 5 étant de pouvoir manipuler l’espacement des caractères dans les éléments Control, TextBlock, etc.
Ceci est très simple ! il suffit de faire varier la valeur de la propriété CharacterSpacing et LineHeight :

CharacterSpacing

XAML

<UserControl x:Class="Silverlight5Tests.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Slider x:Name="CharacterSpacingSlider"
                Minimum="0"
                Maximum="1000"
                Value="0"
                Margin="5" />
        <TextBlock Text="Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
                   CharacterSpacing="{Binding Value, ElementName=CharacterSpacingSlider}"
                   Grid.Row="1"
                   TextWrapping="Wrap"
                   Margin="5" />
    </Grid>
</UserControl>

Normal
Silverlight 5 CharacterSpacing

Avec CharacterSpacing
Silverlight 5 CharacterSpacing augmented

LineHeight

La modification du LineHeight permet d’augmenter ou de diminuer l’espacement entre les lignes d’un texte. Pour cela il suffit de faire varier la propriété LineHeight.

Une subtilité supplémentaire est d’utiliser le LineStackingStrategy afin d’indiquer la façon dont le block de texte doit s’afficher : sur le haut de la ligne, sur le bas de la ligne, etc.

XAML

<UserControl x:Class="Silverlight5Tests.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <StackPanel>
            <TextBlock Text="Character spacing" />
            <Slider x:Name="CharacterSpacingSlider"
                    Minimum="-1000"
                    Maximum="1000"
                    Value="0"
                    Margin="5" />
            <TextBlock Text="Line height" />
            <Slider x:Name="LineHeightSlider"
                    Minimum="0"
                    Maximum="1000"
                    Value="0"
                    Margin="5" />
            <TextBlock Text="Line height strategy" />
            <ComboBox x:Name="LineHeightStrategy"
                      SelectedIndex="0">
                <system:LineStackingStrategy>BaselineToBaseline</system:LineStackingStrategy>
                <system:LineStackingStrategy>BlockLineHeight</system:LineStackingStrategy>
                <system:LineStackingStrategy>MaxHeight</system:LineStackingStrategy>
            </ComboBox>

        </StackPanel>

        <TextBlock Text="Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
                   CharacterSpacing="{Binding Value, ElementName=CharacterSpacingSlider}"
                   Grid.Row="1"
                   TextWrapping="Wrap"
                   LineHeight="{Binding Value, ElementName=LineHeightSlider}"
                   LineStackingStrategy="{Binding SelectedItem, ElementName=LineHeightStrategy}"
                   Margin="5" />
    </Grid>
</UserControl>

MaxHeight

silverlight5characterspacinglineheightandstrategy

BlockLineHeight

Silverlight5CharacterSpacingLineHeightAndStrategyBlockLineHeight

RichTextBoxOverflow – Sample 1

Gros manque depuis plusieurs années en Silverlight, à présent corrigé !, est la capacité d’avoir du texte liés et multi-colonnes. Cela permet d’afficher un texte comme dans les journaux ou les magazines !

Dans cet exmple nous allon faire un texte en multicolonne

XAML

<Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <RichTextBox OverflowContentTarget="{Binding ElementName=OverflowBox}"
                     VerticalScrollBarVisibility="Disabled" IsReadOnly="True"
                     HorizontalScrollBarVisibility="Disabled" BorderThickness="0">
            <Paragraph>
                Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
            </Paragraph>
        </RichTextBox>

        <RichTextBoxOverflow x:Name="OverflowBox"
                             VerticalScrollBarVisibility="Disabled"
                             HorizontalScrollBarVisibility="Disabled"
                             BorderThickness="0"
                             Grid.Column="1">

        </RichTextBoxOverflow>
    </Grid>

Resultat

Silverlight5RichTextBoxOverflow

RichTextBoxOverflow – Sample 2

Dans cet exemple, quelque chose de souvent demandé : pouvoir faire un texte flottant tout autour d’une image (comme on peut le faire avec des div en HTML)

XAML


 <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="200" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <!--SAMPLE IMAGE-->
        <Image Source="https://slouge.files.wordpress.com/2011/02/83983-silverlight-logo1.jpg"
               Margin="0,0,0,5"
               Stretch="Uniform" />

        <!--INITIAL TEXT-->
        <RichTextBox OverflowContentTarget="{Binding ElementName=OverflowBox}"
                     VerticalScrollBarVisibility="Disabled"
                     IsReadOnly="True"
                     HorizontalScrollBarVisibility="Disabled"
                     BorderThickness="0"
                     Grid.Column="1"
                     Margin="5,0,0,0">
            <Paragraph>
                Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
            </Paragraph>
        </RichTextBox>

        <!--OVERFLOW TEXT-->
        <RichTextBoxOverflow x:Name="OverflowBox"
                             VerticalScrollBarVisibility="Disabled"
                             HorizontalScrollBarVisibility="Disabled"
                             BorderThickness="0"
                             Grid.ColumnSpan="2"
                             Margin="0,-5,0,0"
                             Grid.Row="1" />
    </Grid>

Resultat

Silverlight5RichTextBoxOverflowWithImage

Cela ouvre donc de belles perspective pour le rendu de pages éditoriales en Silverlight 😀

Catégories :Silverlight, Silverlight 5

[SL5] : Silverlight 5 Beta is out !

Cette nuit Microsoft à sorti la première beta de Silverlight 5 incluant quelques innovations importantes comme :

Controles

Recherche dans un ItemsControl

Une propriété attachée “TextPath” est à présent disponible permettant de faire des recherches au clavier.

DrawingSurface

Permet de définir une zone affichant un contenu 3D, le rendu s’effectue dans l’évènement Draw.

Possibilité d’utiliser les librairies XNA afin de réaliser des rendus 3D comme des graphiques, etc.

Out-of-Browser

Possibilité d’afficher plusieurs instances de Window permettant d’afficher des fenetres non modales, faire du multi screen, etc.

UI

Mouse Double-Click

Ajout de la propriété ClickCount dans l’évènement MouseButtonEventArgs.

XAML

Ajout de la classe MarkupExtension, permettant grace à l’implémentation de la méthode ProvideValue(IServiceProvider) nous permet de faire des comportements tels que x:Type connu sur WPF.

Data Binding

Possibilité d’ajouter des “BreakPoints” directement dans le code XAML.

  • Ajout des manques de Silverlight par rapport à WPF comme :
  • Ajout de FindAncestor
  • Ajout de DataType permettant d’associer un DataTemplate directement à un type (ex un classe) au lieu d’utiliser explicitement le x:Key.

Ajout de la possibilité de faire un binding sur des propriétés générées dynamiquement grace à l’implémentation de l’interface ICustomTypeProvider sur les classes contenant des propriétés dynamiques.

Application

  • Ajout de nouvelles fonctionnalités au application en mode “Trusted”
  • Possibilité d’accéder aux système de fichier sans restrictions
  • Possibilité de créer plusieurs fenetres
  • Possibilité de définir que l’application en mode “trusted” s’éxecutera dans le navigateur, facilitant les déploiements, maj, etc. Cela permet aussi d’utiliser les fonctionnalités comme le WebBrowser et le fenetres de notifications.

Medias

Sound Effects

Inclusion de nouvelles classes pour les effets audios et possibilité d’utiliser l’assembly Microsoft.Xna.Framework.Audio pour effectuer des effets sur le son dans une application.

Variable Playback Rate

Possibilité d’utiliser la propriété PlaybackRate et l’évènement RateChanged dans le  MediaElement.

Textes

Character Spacing

Ajout de la propriété CharacterSpacing disponible dans Control, TextBlock, et TextElement permettant d’augment au réduire l’espacement entre les caractères.

Inter-line Spacing

Ajout des propriétés LineHeight et LineStackingStrategy dans Block, TextBlock, TextBox, et RichTextBox permettant d’augmenter ou diminuer l’écart entre les lignes.

Display Overflow Content in RichTextBox

In Silverlight 5 Beta, you can display the content that does not fit in a RichTextBox in a RichTextBoxOverflow control. For more information, see RichTextBoxOverflow.

 

En général

  • Amélioration de la rapidité du lancement de l’application sur les systèmes multi coeur (multi-core JIT)
  • ComboBox avec recherche
  • DefaultFileName dans le SaveFileDialog
  • Amélioration de la couche graphique partagée avec le code de Windows Phone.
  • Accélération matérielle pour une application en mode Windowless sur IE9
      • En prévision

    • impression vectorielle
    • Comportements en mode plein ecran (ex : ne pas mettre en veille, à l’image de WindowsPhone et son mode Idle)
    • Support du controle à distance pour les médias
    • Support du OpenType
    • Amélioration de la clartée du texte : pixel snapping
    • nouveau évènement DataContextChanged
    • Support des systèmes 64-bit
    • COM interop et P/Invoke en mode trusted
    • amélirations sur le PivotViewer et inclusion dans le SDK

    Liens

      Sourire
    Catégories :Silverlight, Silverlight 5