# Thursday, June 03, 2010

Désactiver le bouton d’une ScrollBar quand elle est au Minimum ou au Maximum

Le contrôle ScrollBar de Silverlight ne désactive pas les boutons des ScrollBars lorsque le curseur arrive à une extrémité.

image

Ce design n’est en général pas satisfaisant dès l’instant ou on veut faire une application un peu jolie et ergonomique.

image

Pour activer ce comportement sur une ScrollBar, il faut éditer son template et utiliser la classe ScrollBarExtensions. Dans le Template nous commençons par activer la classe d’extensions sur la ScrollBar. Sur n’importe quel élément du template il suffit de déclarer l’AttachedProperty suivante:

                        <Grid x:Name="Root"

                             slec:ScrollBarExtensions.RegisterScrollbar="{Binding RelativeSource={RelativeSource TemplatedParent}}">

 

Une fois initialisée, la classe d’extensions calcule si la valeur est différente du Minimum ou Maximum et pose deux AttachedProperty sur la ScrollBar : CanIncrease et CanDecrease. Il ne reste plus en suite qu’à Binder les propriété IsEnabled des boutons à ces valeurs.

IsEnabled="{Binding (slec:ScrollBarExtensions.CanDecrease), RelativeSource={RelativeSource TemplatedParent}}"

La classe ScrollBarExtensions:

    /// <summary>

    /// Provide behavior extensions to a ScrollBar control. When registered add CanIncrease and CanDecrease attached property

    /// </summary>

    public static class ScrollBarExtensions

    {

        /// <summary>

        /// Get the epsillon value arround the Maximum or Minimum value that activate or deactive the CanIncrease and CanDecrease property

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        public static double GetEpsillon(DependencyObject obj)

        {

            return (double)obj.GetValue(EpsillonProperty);

        }

 

        /// <summary>

        /// Set the epsillon value arround the Maximum or Minimum value that activate or deactive the CanIncrease and CanDecrease property

        /// </summary>

        /// <param name="obj"></param>

        /// <param name="value"></param>

        public static void SetEpsillon(DependencyObject obj, double value)

        {

            obj.SetValue(EpsillonProperty, value);

        }

 

        // Using a DependencyProperty as the backing store for Epsillon.  This enables animation, styling, binding, etc...

        public static readonly DependencyProperty EpsillonProperty =

            DependencyProperty.RegisterAttached("Epsillon", typeof(double), typeof(ScrollBarExtensions), new PropertyMetadata(0.001d));

 

        /// <summary>

        /// Get the registered ScrollBar

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        public static ScrollBar GetRegisterScrollbar(DependencyObject obj)

        {

            return (ScrollBar)obj.GetValue(RegisterScrollbarProperty);

        }

 

        /// <summary>

        /// Register a ScrollBar

        /// </summary>

        /// <param name="obj"></param>

        /// <param name="value"></param>

        public static void SetRegisterScrollbar(DependencyObject obj, ScrollBar value)

        {

            obj.SetValue(RegisterScrollbarProperty, value);

        }

 

 

        public static readonly DependencyProperty RegisterScrollbarProperty =

            DependencyProperty.RegisterAttached("RegisterScrollbar", typeof(ScrollBar), typeof(ScrollBarExtensions), new PropertyMetadata(RegisterScrollbarChangedCallback));

 

        private static void RegisterScrollbarChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            var sb = (ScrollBar)e.OldValue;

            if (sb != null)

            {

                sb.ValueChanged -= storyBoardPropertyChanged;

                sb.RemoveChangedHandler("Maximum", storyBoardDependecyPropertyChanged);

                sb.RemoveChangedHandler("Minimum", storyBoardDependecyPropertyChanged);

            }

 

            sb = (ScrollBar)e.NewValue;

            if (sb != null)

            {

                sb.ValueChanged += storyBoardPropertyChanged;

                sb.AddChangedHandler("Maximum", storyBoardDependecyPropertyChanged);

                sb.AddChangedHandler("Minimum", storyBoardDependecyPropertyChanged);

                RefreshValue(sb);

            }

        }

 

        private static void RefreshValue(ScrollBar sb)

        {

            var epsillon = GetEpsillon(sb);

            SetCanDecrease(sb, (sb.Value - sb.Minimum) > epsillon);

            SetCanIncrease(sb, (sb.Maximum - sb.Value) > epsillon);

        }

 

        static void storyBoardDependecyPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)

        {

            RefreshValue((ScrollBar)sender);

        }

        static void storyBoardPropertyChanged(object sender, EventArgs e)

        {

            RefreshValue((ScrollBar)sender);

        }

 

        /// <summary>

        /// true when the Value of a ScrollBar is greater than the Minimum + the Epsillon value

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        public static bool GetCanDecrease(DependencyObject obj)

        {

            return (bool)obj.GetValue(CanDecreaseProperty);

        }

 

        public static void SetCanDecrease(DependencyObject obj, bool value)

        {

            obj.SetValue(CanDecreaseProperty, value);

        }

 

        public static readonly DependencyProperty CanDecreaseProperty =

            DependencyProperty.RegisterAttached("CanDecrease", typeof(bool), typeof(ScrollBarExtensions), null);

 

        /// <summary>

        /// true when the Value of a ScrollBar is loawer than the Maximum - the Epsillon value

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        public static bool GetCanIncrease(DependencyObject obj)

        {

            return (bool)obj.GetValue(CanIncreaseProperty);

        }

 

        public static void SetCanIncrease(DependencyObject obj, bool value)

        {

            obj.SetValue(CanIncreaseProperty, value);

        }

 

        public static readonly DependencyProperty CanIncreaseProperty =

            DependencyProperty.RegisterAttached("CanIncrease", typeof(bool), typeof(ScrollBarExtensions), null);

 

    }

#    Comments [1] |

Etre notifié du changement de valeur d’une DependencyProperty

Les contrôles n’implémentent pas tous des événements notifiant du changement de valeur de leurs propriétés. Un moyen d’être notifié est d’utiliser le Binding. Voici une classe fournissant des méthodes d’extensions sur les DependencyObject pour ajouter des handlers d’événements.

Ajouter un événement très simple :

            ScrollBar sb = new ScrollBar();

            sb.AddChangedHandler("Value", (o, e) =>

            {

                MessageBox.Show(Convert.ToString(e.NewValue));

            });

Code :

    public static class DependencyObjectExtensions

    {

        /// <summary>

        /// Inner class bound to the Property we want to monitor

        /// </summary>

        private class BindingSubscription : DependencyObject, IDisposable

        {

            /// <summary>

            /// Constructor

            /// </summary>

            /// <param name="source">The DependencyObject instance on which we want to monitor the property</param>

            /// <param name="name">The PropertyPath to the property we want to monitor</param>

            /// <param name="handler">The handler to invoke when the property changed</param>

            public BindingSubscription(DependencyObject source, string name, DependencyPropertyChangedEventHandler handler)

            {

                this.Handler = handler;

                binding = new Binding(name) { Source = source };

                this.PropertyPath = name;

                BindingOperations.SetBinding(this, ValueProperty, binding);

            }

 

            private Binding binding;

 

            /// <summary>

            /// The PropertyPath to the property we want to monitor

            /// </summary>

            public string PropertyPath { get; set; }

 

            /// <summary>

            /// The handler to invoke when the property changed

            /// </summary>

            public DependencyPropertyChangedEventHandler Handler { get; set; }

 

            /// <summary>

            /// Value depedency property.

            /// </summary>

            public static readonly DependencyProperty ValueProperty =

                DependencyProperty.Register(

                    "Value",

                    typeof(object),

                    typeof(BindingSubscription),

                    new PropertyMetadata((d, e) =>

                    {

                        var subscription = ((BindingSubscription)d);

                        var handler = subscription.Handler;

                        if (handler != null)

                            handler(subscription.source, e);

                    }));

 

            /// <summary>

            /// Release the binding from the source

            /// </summary>

            public void Dispose()

            {

                this.ClearValue(ValueProperty);

            }

        }

 

        /// <summary>

        /// Get the list of BindingSubscription stored on one object

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        private static List<BindingSubscription> GetListeners(DependencyObject obj)

        {

            var list = (List<BindingSubscription>)obj.GetValue(ListenersProperty);

            if (list == null)

            {

                list = new List<BindingSubscription>();

                SetListeners(obj, list);

            }

            return list;

        }

 

        /// <summary>

        /// Set the list of BindingSubscription on an object

        /// </summary>

        /// <param name="obj"></param>

        /// <param name="value"></param>

        private static void SetListeners(DependencyObject obj, List<BindingSubscription> value)

        {

            obj.SetValue(ListenersProperty, value);

        }

 

 

        /// <summary>

        /// Listeners dependency property

        /// </summary>

        private static readonly DependencyProperty ListenersProperty =

            DependencyProperty.RegisterAttached("Listeners", typeof(List<BindingSubscription>), typeof(DependencyObjectExtensions), null);

 

 

        /// <summary>

        /// Add a handler to a DependencyProperty for change notification

        /// </summary>

        /// <param name="source">The DependencyObject instance on which we want to monitor the property</param>

        /// <param name="name">The PropertyPath to the property we want to monitor</param>

        /// <param name="handler">The handler to invoke when the property changed</param>

        public static void AddChangedHandler(this DependencyObject source, string name, DependencyPropertyChangedEventHandler handler)

        {

            // Create the binding

            BindingSubscription subscriber = new BindingSubscription(source, name, handler);

            // Store the binding into de DependencyObject to prevent garbage collection of the subscriber

            GetListeners(source).Add(subscriber);

        }

 

        /// <summary>

        /// Remove a handler from a DependencyProperty

        /// </summary>

        /// <param name="source">The DependencyObject instance on which we want to monitor the property</param>

        /// <param name="name">The PropertyPath to the property we want to monitor</param>

        /// <param name="handler">The handler to invoke when the property changed</param>

        public static void RemoveChangedHandler(this DependencyObject source, string name, DependencyPropertyChangedEventHandler handler)

        {

            var subscribers = GetListeners(source);

            var subscriber = subscribers.FirstOrDefault(s => s.Handler == handler && s.PropertyPath == name);

            if (subscriber != null)

            {

                subscriber.Dispose();

                subscribers.Remove(subscriber);

            }

 

            if (subscribers.Count == 0)

                source.ClearValue(ListenersProperty);

        }

    }

#    Comments [0] |
# Sunday, May 30, 2010

SLExtensions – Création d’un player vidéo avec SLMedia – partie 1

La partie SLMedia du projet SLExtensions contient tout le nécessaire pour réaliser un player video. Contrairement à un projet comme SMF qui vous fourni un contrôle tout prêt, SLMedia vous fourni des classes “Controller” vous permettant de développer votre player à base de MVVM. Si un player packagé est simple à templater, il trouve vite ses limites lorsque vous voulez intégrer des comportements spécifiques.

Il y a quelque temps sur ce blog j’avais commencé la création d’un player video ici et ici. SLMedia a évolué je vais donc mettre à jour mes exemples.

Affichage de la vidéo

La classe principale de gestion reposant sur la notion de playlist, le passage d’un élément smoothstreaming à un élément video normal n’était pas aisé. Le conteneur affichant la vidéo (le MediaElement ou le SmoothStreamingMediaElement) et maintenant déterminé en fonction du IMediaElement courrant. Pour être plus facilement modulaire et pour permettre de séparer la librairie affichant du smoothstreaming, les éléments video reposent sur des VideoAdapters qui se chargent de la création et de s’attacher aux différents événements des composants videos.
Le design de l’application n’a plus qu’à créer un ContentControl et récupérer le contrôle d’affichage à partir du VideoController.

        <ContentControl Content="{Binding VideoAdapter.Content}"

                       VerticalContentAlignment="Stretch"

                       HorizontalContentAlignment="Stretch" />

Etats visuels

Grâce à Silverlight 4 qui permet maintenant de faire du binding sur des objets de type DependencyObject, l’écriture d’un MapState est un peu simple. Il suffit de binder la propriété Value sur l’objet à surveiller. Et lorsque la valeur change, en fonction des différents mappings définis, l’état visuel correspondant sera déclenché.

        <i:Interaction.Behaviors>

            <slint:MapState Value="{Binding IsPlaying}">

                <slint:MapStateMapping StateName="Playing"

                                      Value="true" />

                <slint:MapStateMapping StateName="Paused"

                                      Value="false" />

            </slint:MapState>

        </i:Interaction.Behaviors>

StringFormat

De même, grâce à Silverlight 4, exit le StringFormatConverter. Il est maintenant possible de directement spécifier la conversion en string dans l’objet Binding.

            <TextBlock Text="{Binding Path=BufferingProgress, Mode=TwoWay, StringFormat=p}" />

La playlist

Le MediaController fournit maintenant des commandes permettant de naviguer dans la playlist. Il suffit de relier deux boutons Précédent / Suivant à ces commandes pour passer d’un élément à l’autre. De même, pour enchainer les éléments à la fin de la lecture, il suffit d’activer la propriété IsChaining.

        <slvideo:VideoController x:Name="controller"

                                AutoPlay="True"

                                IsChaining="True">

            <slvideo:VideoController.Playlist>

                <slvideo:VideoItem Source="http://labs.ucaya.com/lake.wmv" />

                <slvideoSmooth:SmoothVideoItem Source="http://mediadl.microsoft.com/mediadl/iisnet/smoothmedia/Experience/BigBuckBunny_720p.ism/Manifest" />

                <slvideo:VideoItem Source="http://labs.ucaya.com/butterfly.wmv" />

                <slvideo:VideoItem Source="http://labs.ucaya.com/bear.wmv" />

            </slvideo:VideoController.Playlist>

        </slvideo:VideoController>

Navigation :

            <Button Content="Previous"

                   Command="{Binding PreviousItem}" />

            <Button Content="Next"

                   Command="{Binding NextItem}" />

Les sources

#    Comments [1] |
# Friday, April 02, 2010

Live meeting "Ma première appli Windows Phone 7"

imageTu es fan de Silverlight ou XNA, Microsoft te convie à un eEvent pour t’apprendre comment appliquer ta techno au développement d’application Windows Phone. Tu pourras te rendre compte de la facilité de mise en oeuvre d’une application et peut être te donner l’envie de réaliser l’application qui “déchire” et que tout le monde voudra sur son téléphone.

Je suis sur que tu n’as rien de mieux à faire le 29 avril à 18h, alors n’hésite pas à te connecter là : https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032448416&Culture=fr-FR

#    Comments [0] |
# Tuesday, March 16, 2010

Mix10 - Silverlight 4 RC - Windows Phone 7 - IE9 Preview – OData – Codename Dallas

Lors des 2 keynotes au Mix10 de Las Vegas, Microsoft à annoncé et mis à disposition plusieurs produits. Voici les points les plus intéressants que j'ai pu relever.

Silverlight 4 RC

La première annonce a été pour Silverlight 4, qui est disponible en version RC. Un an après le version finale de SL3, cette version apporte son lot d'amméliorations.
Entre autre:

  • Rich Text:
        - Propriété "Xaml" pour la sérialisation du contenu
  • Réseau
        - Les application FullTrust outofbrowser n'ont plus besoin de serveur de crossdomain
        - Le crossdomain pour les appels sockets est récupéré par http.
  • Out of Browser (Full Trust)
        - Signature des applications. La boite de dialogue d'installation des applications fulltrust vous indique si l'application est certifiée. Un utilisateur pourra donc s'assurer de la provenance de l'application qu'il est en train d'installer.
        - Personnalisation de la fenêtre principale
  • Media
        - Possibilité de garder une application fullscreen sur un deuxième écran
        - Support du multicast
  • Graphics and Printing
        - Les perspective transforms supportent l'accélération materielle
        - Accélération matérielle pour deepzoom
  • Data
        - Support du XPath pour le XML
  • Plugin
        - Support de Google Chrome sur windows
        - Support de MEF
        - Support de Windows Phone

@ScottGu nous annonce aussi une sortie finale pour le mois prochain.

Windows Phone 7 alias WP7

La deuxième grosse annonce est la disponibilité des kits de développements pour windows phone 7. Ces kits contiennent des outils gratuits qui permettront à tout le monde de pouvoir concevoir des applications pour WP7. Visual Studio 2010 Express fait son apparition ainsi qu'une marchine virtuelle faisant tourner un device WP7. Coté designer, une version beta de Blend 4 est disponible en téléchargement sur laquelle il est possible d'installer les outils pour windows phone.
http://developer.windowsphone.com

Internet Explorer 9

Le deuxième jour, Microsoft a présenté IE9, la nouvelle mouture de son navigateur. Les développements d'ie9 s'orrientent vers les besoins utilisateurs. Pour cette nouvelle version, au menu : performances et conformités aux standards (HTML5, CSS3).
Pour améliorer les perfomances javascript, Microsoft a analysé les 7000 premiers sites webs afin de déterminer les fonctions les plus fréquement utilisées et de les optimiser en priorité. Le nouveau moteur javascript compile les scripts pour gagner en performance d'exécution. Cette compilation est effectuée en arrière plan et est multi coeur.
Une autre piste pour l'amélioration des performances est l'utilisation de l'accélération graphique pour tout ce qui est rendu. L'utilisation de DirectX permet un gain non negligeable de performances pour l'affichage des images mais aussi pour le rendu cleartype du text.
Ce rendu DirectX permet d'afficher du SVG et des videos HD avec des performances impressionantes. Lors de la keynote, un netbook affichait 2 videos HD 720p avec la balise video de html5 sans ralentissement alors que chrome ne permettait pas un rendu fluide d'une seule.
La preview de ie9 est téléchargeable ici : http://www.ietestdrive.com

Open Data Protocol

odataLa dernière annonce concerne oData. Une standardisation d'un système de requêtage sur un modèle REST. Les détails de ce protocole sont accessibles sur http://www.odata.org. De nombreux clients sont capables d'utiliser oData. Microsoft fourni sur le site oData des clients pour différents langages (java, php, .net). Le code mis à disposition sur odata.org est publié sous licence Apache 2. Sur les environements Microsoft comme silverlight, un proxy est généré par visual studio est permet de traduire des requêtes linq en requêtes oData et de récupérer le résultat sous forme d'objets fortements typés. Ce type de services sont déjà exposés par Sharepoint 2010. Chaque liste de donnée sharepoint est récupérable par ce biais. Des sources oData peuvent être crées manuellement sur des webservices WCF mais aussi directement sur des bases de données SQL Azure.

Codename Dallas

Microsoft propose une place de marché où vous pouvez référencer vos services créés avec oData. Il devient alors très facile à un développeur de tirer parti de toutes ces sources et de construire une application (sur WP7 par exemple). Le market place est disponible ici : http://www.sqlazureservices.com

#    Comments [0] |
# Tuesday, September 01, 2009

Serialisation webservices Proto Buffer et client Silverlight

Un de mes amis m’a conseiller de jeter un oeil à la sérialisation Proto Buffer. Elle est rapide et légère. Idéale donc pour la consommer par webservice. Il existe plusieurs implémentation pour c#, je me suis intéressé à protobuf-net. Pour une comparaison des performances par rapport à du BinaryFormatter du Soap ou du Json, vous pouvez vous rendre sur cette page.

Utilisation avec WCF

Dans mes premiers tests, je voulais remplacer le DataContractSerializer de mes ServiceContract par le serializer protobuf-net, malheureusement le support de WCF de silverlight est assez léger et il n’est actuellement pas possible de le faire. Dans ses exemples protobuf-net contient un serveur et client RPC. J’ai donc configuré WCF avec une adaptation de ce serveur RPC et remplacé le client généré par le wizard de visual studio par le client protobuf-net.

Côté serveur

Pour avoir un serveur RPC compatible avec l’exemple fourni par protobuf-net, il faut rajouter à WCF un OperationSelector routant les urls entrantes vers le bon service contract. J’ai réalisé un EndPointBehavior paramettrant tout ça. La configuration ressemble à ceci:

    <system.serviceModel>

        <extensions>

            <behaviorExtensions>

                <add name="protoRPC" type="ProtoBuf.RPC.ProtoRpcEndpointBehaviorSection, protobuf-wcf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

            </behaviorExtensions>

        </extensions>

        <behaviors>

            <endpointBehaviors>

                <behavior name="rpcBehavior">

                    <protoRPC/>

                </behavior>

            </endpointBehaviors>

        </behaviors>

        <services>

            <service name="SilverlightApplication4.Web.TestService">

                <endpoint address="" binding="webHttpBinding" contract="TestServices.ITestService" behaviorConfiguration="rpcBehavior"/>

            </service>

        </services>

    </system.serviceModel>

La classe implémentant le ServiceContract doit également autoriser toutes les urls:

    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]

    public class TestService : ITestService

    {

        #region ITestService Members

 

        public TestServices.Customer DoWork(string a, TestServices.Customer c)

        {

            return TestServices.Customer.Invent();

        }

 

        #endregion

    }

J’ai choisi d’externaliser le ServiceContract dans une interface pour pouvoir générer simplement un client dans la partie Silverlight. Voici à quoi il ressemble:

    [ServiceContract]

    public interface ITestService

    {

        [OperationContract(Action = "dowork")]

        Customer DoWork(string a, Customer c);

    }

Côté Silverlight

Pour fonctionner avec Silverlight, il a fallu que je corrige un bug dans les sources de protobuf-net. Les classes temporaires de sérialization générées par protobuf-net sont privées et Silverlight ne permet pas de faire de réflection sur celles-ci. Il faut les mettres en internal.

DataContracts

imageDans l’implémentation de mon exemple, j’ai isolé mes datacontracts dans une assembly séparée. J’ai créé la même assembly pour Silverlight en référençant simplement les fichiers par liens, ce qui me permet de n’avoir qu’une seule version de ces fichiers. Pour ceux qui ne saurait pas comment faire et pour éviter de perdre 2 heures à chercher, il suffit lorsque vous ajouter un élément à la solution de cliquer sur la petite flêche à côté du boutton Add et de choisie Add as link. image

Génération du client

Nous voici donc avec la même assembly que côté serveur, mais compilée au format Silverlight. Cette assembly contenant notre interface de ServiceContract, nous allons rajouter dans la version Silverlight la génération du client. J’ai fais cette génération par l’utilisation des fichiers t4. Il suffit de copier coller le fichier .tt qui se trouve dans la solution et de le nommer du nom du fichier qui contient l’interface de ServiceContract pour que la génération s’effectue (ici ITestService.tt). La génération utilise le FileCodeModel pour déterminer les Méthodes de l’interface à implémenter dans le client. Le client généré contient des fonctions d’appels asynchrones et les événements completed qui correspondent.

Utilisation

L’utilisation de notre client reste aussi simple que l’utilisation d’un client WCF silverlight standard.

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            ITestServiceClient client = new ITestServiceClient(new Uri("../", UriKind.Relative));

            client.DoWorkCompleted += new EventHandler<ITestServiceClient.DoWorkResultEventArgs>(client_DoWorkCompleted);

            client.DoWorkAsync("aazer", Customer.Invent());

        }

 

        void client_DoWorkCompleted(object sender, ITestServiceClient.DoWorkResultEventArgs e)

        {

            var r = e.Result;

        }

Il y a plusieurs façons d’instancier le client. Tout dépend d’où vos webservices se trouvent.
Le constructeur par défaut va chercher un webservice se nommant du même nom que l’interface de service contract sans le I se trouvant de le même dossier que votre .xap. Sur une surcharge du constructeur, vous pouvez spécifier soit une Url donnant le dossier dans lequel va se trouver le webservice ou alors un url d’appel direct vers le fichier svc.

Les sources

#    Comments [0] |
# Monday, June 08, 2009

SLMedia et Smooth Streaming

Comme les français ont put le voir avec Roland Garros, le Smooth Streaming permet de diffuser des flux de grande qualité qui vont s’adapter à la connexion et à la puissance de la machine et de pouvoir faire du timeshifting (contrôle du direct) sur les emissions live.

Smooth Streaming c’est quoi ?

Le smooth streaming n’est pas un format de compression de video. C’est une technologie de diffusion qui permet de découper la vidéo et le son en morceaux de 2 secondes et diffuser le tout par http. La vidéo étant encodé en plusieurs niveau de qualité, nous obtenons des échantillons de différents poids qui vont être mis à disposition sur un serveur web.
La partie client web (silverlight) va récupérer ces différents paquets et les mettre bout à bout pour reconstituer le flux vidéo. L’application silverlight récupérera le paquet de poids correspondant au mieux à la qualité de la connexion à internet.
Pour la partie diffusion en live, un ou plusieurs encodeur envoyent au serveur web ces paquets de 2 secondes au fur et à mesure qu’ils sont reçu de la source et encodés.
Pour silverlight 2, la vidéo est au format VLC1 et nous devrions avoir droit au H264 avec silverlight 3.
Le découpage en paquets et la diffusion au format HTTP permettent de tirer partie des différents Cache HTTP qui existent sur internet.

Côté serveur

La diffusion des flux smooth streaming nécessitent des composants côté serveur. Actuellement, IIS7 est la seule plateforme supportant la diffusion de ce format. Deux versions existent. La première vous permet de diffuser des vidéos préenregistrées au format smooth streaming et la deuxième encore en beta permet la diffusion de flux live.

Côté client

Silverlight est actuellement le seul moyen de lire de la vidéo en Smooth Streaming. Des exemples existent pour Silverlight 2 et Silverlight 3.

Encodeur

Pour encoder des vidéos préenregistrées, vous devrez passer par Microsoft Expression Encoder SP1. Pour les flux live, des encodeurs hardwares sont en cours de développement. Pour Roland Garros, c’est la société Inlet qui les a fourni. Vous pouvez cependant simuler un flux live à partir d’une vidéo préencodée en Smooth Streaming. Allez sur le site officiel et téléchargez le starter kit. Vous trouverez 2 exécutables PushEncoder32.exe et PushEncoder64.exe qui poussent votre vidéo préencodée sur le point de distribution live.

SLMedia

La librairie de décodage du flux smoothstreaming (Microsoft.Web.Media.SmoothStreaming.dll) est récupérable dans le player d’exemple silverlight du starter kit.

Pour cet exemple, je vais réutiliser le VideoPlayer d’un précédent billet.
L’assignation d’une vidéo à un MediaElement ne se fait pas de la même manière que la video soit du smooth streaming ou non. Pour gérer ceci, la notion de VideoSourceAdapter et VideoAdapter a été ajoutée à SLExtensions. Le VideoSourceAdapter va instancier la bonne source en fonction de l’url de la vidéo. La première chose à faire dans le projet est de référencer le projet SLMedia.SmoothStreaming et d’initialiser le SmoothStreamingVideoSourceAdapter. Il suffit de rajouter la ligne ci-dessous au constructeur de la classe App.xaml.cs.

            SLMedia.SmoothStreaming.SmoothStreamingVideoSourceAdapter.Initialize();

Ensuite, changez le binding sur le source du MediaElement et l’url de la video.

        <MediaElement x:Name="mediaelement"

                    slvideo:VideoSourceAdapter.Source="{Binding Path=CurrentItem.SourceUri}"

                    slvideo:VideoController.MediaElement="{StaticResource controller}"

                    IsMuted="{Binding Path=IsMuted}"

                    Volume="{Binding Path=Volume}"

                    AutoPlay="{Binding Path=AutoPlay}"   

                    />

 

 

            controller.Playlist.Add("http://localhost/live.isml/Manifest");

Attention, le chemin d’accès au Manifest est sensible au crossdomain. Pensez à autoriser votre player à accéder à la video si le player n’est pas dans le même domaine (crossdomain.xml)

Poussez votre video en live  PushEncoder64.exe http://localhost/live.isml "Big Buck Bunny.ism" et lancez votre player. Vous devriez voir la video. Si vous voulez un contenu préencodé, téléchargez la video Big Buck Bunny ici.

Par défaut, la lecture du flux commence toujours au début de la vidéo qui a été encodée. Si vous voulez rejoindre le direct, vous pouvez appeler la fonction GoToLive du VideoAdapter ou créer un mediaitem de type LiveVideoItem et lui assigner sa propriété JoinLive.

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            controller.Playlist.Add(new LiveVideoItem

            {

                Source = "http://localhost/live.isml/Manifest",

                JoinLive = true

            });

            controller.Next();

        }


la timeline pour le Live Smooth Streaming

Pour les événements live, les informations de position ne démarrent pas forcément à 0. Il faut changer la déclaration des bindings du slider pour tenir compte des propriétés Start et LivePosition.

            <slec:Slider Maximum="{Binding Path=VideoAdapter.LivePosition, Converter={StaticResource timeSpanConverter}}"

                        Minimum="{Binding Path=VideoAdapter.Start, Converter={StaticResource timeSpanConverter}}"

                        MoveValue="{Binding Converter={StaticResource timeSpanConverter}, Mode=TwoWay, Path=Position}"

                        Width="200" Margin="20,0,0,0"

                        />

Quelle représentation pour la timeline

  1. Un slider simple

Ce contrôle est utilisé dans le majorité des players vidéos disponible sur internet. Il se prête bien pour des vidéos de taille connues et relativement petites. Dans le cas de diffusion de flux live, la taille du slider ne représente pas toujours la même durée. Au bout d’une heure d’émission, amener le slider au milieu reculera d’une demi heure alors qu’au bout d’une journée on reculera d’une demi journée.

image

  1. Un “slider inversé”

Le marqueur au milieu indique la position de lecture. La barre orange indique la durée de la vidéo. Cette barre grandit au fur et à mesure que le flux live est émis. Quand l’utilisateur regarde en live, la barre orange s’agrandit vers la gauche et rien ne dépasse à droite du marqueur.
Si l’utilisateur met en pause, le barre s’agrandit à droite du marqueur montrant qu’on est plus en live et indique la durée du décalage par rapport au live.
Quand la lecture reprend, la barre regrandit à gauche.

Contrairement au slider simple, ce type de contrôle permet d’avoir une échelle de temps constante tout au long de la diffusion. Lorsqu’on déplace la timeline, c’est la barre orange qui se déplace, le marqueur reste toujours au milieu. Peut être un peu déroutant au départ, un utilisateur en comprendra vite la logique

image

  1. Un slider sur une ligne de temps

C’est ce qui a été utilisé pour Roland Garros, une barre de temps représente la journée entière. La taille du slider grandit au fur et à mesure du live. Le slider garde une échelle constante mais est quasi invisible au départ. Le principe de déplacement du curseur est préservé.

image

N’oubliez pas de télécharger les sources de SLMedia sur le site de slextensions.

#    Comments [0] |

SLMedia le player Photo / Video de SLExtensions

Les librairies SLMedia regroupent tout ce qu'il vous faut pour gérer vos galeries photos et vidéo. Ces librairies sont séparées en plusieurs dlls gérant chacune un type de média.

  • SLMedia.Core : Librairie centrale prenant en charge les fonctions communes.
  • SLMedia.Picture : Librairie vous permettant de réaliser des slideshows d’images.
  • SLMedia.Deepzoom : Librairie vous permettant d’afficher des galeries deepzoom de vos images.
  • SLMedia.Video : Librairie gérant les galeries vidéos.
  • SLMedia.SmoothStreaming : Gestion des vidéos en Smooth Streaming

Création d’un player video avec SLMedia

La réalisation d’un player avec SLMedia repose sur les concepts de Binding et de Behavior. Les Bindings vont vous servir à lier les informations provenant du controller (ici un VideoController) et les différents éléments graphiques qui vont vous permettre d’afficher et d’agir sur votre vidéo.

MediaController

Cette classe appartenant à SLMedia.Core sert de base au VideoController. Elle regroupe les fonctionnalités communes aux différents controllers.

  • Gestion de la playlist (Next, Previous, IsPlaying, SelectedItem)
  • Gestion du fullscreen (IsFullscreen)
  • Gestion des progressions pour les slideshow / videos (Position, Duration)
  • Etc..

VideoController

Cette classe hérite du MediaController et lui rajoute des fonctions spécifiques pour la vidéo.

  • MediaElement
  • Buffering
  • Support de différentes source par les VideoAdapter

Etape 1 : Affichage d’une video de la playlist

Dans votre page xaml, référencez le namespace correspondant à SLMedia.Video et ajouter le VideoController aux ressources.

<UserControl x:Class="VideoPlayer1.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:slvideo="clr-namespace:SLMedia.Video;assembly=SLMedia.Video"

   >

    <UserControl.Resources>

        <slvideo:VideoController x:Name="controller" />

    </UserControl.Resources>

Ensuite posez un MediaElement et associez sa source avec la propriété CurrentItem de notre controller. Le CurrentItem sera du type IMediaItem et possèdera donc une propriété SourceUri contenant l’url de la vidéo à afficher.

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource controller}}" >

        <MediaElement x:Name="mediaelement"

                    Source="{Binding Path=CurrentItem.SourceUri}"

                    />

    </Grid>

Il ne reste plus qu’à renseigner un nouvel item dans la playlist et de le sélectionner pour que la video se lancer.

using SLMedia.Video;

using SLMedia.Core;

 

namespace VideoPlayer1

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

            controller = (VideoController)Resources["controller"];

 

            this.Loaded += new RoutedEventHandler(Page_Loaded);

        }

 

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            controller.Playlist.Add("http://labs.ucaya.com/lake.wmv");

            controller.Next();

        }

    }

}

Pour rendre complétement opérationnel notre videocontroller, nous devons lui indiquer quel est le MediaElement qui affiche la vidéo. De cette manière il pourra récupérer les états de lecture, de progression et de buffering. Nous allons également lier quelques propriétés supplémentaires qui nous permettrons de tout contrôler depuis le VideoController.

        <MediaElement x:Name="mediaelement"

                    Source="{Binding Path=CurrentItem.SourceUri}"

                    slvideo:VideoController.MediaElement="{StaticResource controller}"

                    IsMuted="{Binding Path=IsMuted}"

                    Volume="{Binding Path=Volume}"

                    AutoPlay="{Binding Path=AutoPlay}"   

                    />

Etape 2 : Lecture / Pause

L’état lecture / pause est géré dans notre controller par la propriété IsPlaying. Un binding entre une checkbox et cette propriété permetra de contrôler facilement l’état de lecture.

        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Background="#50000000" Margin="30">

            <CheckBox IsChecked="{Binding Path=IsPlaying, Mode=TwoWay}" />

        </StackPanel>

Etape 3 : Position / Durée

Un slider Bindé sur les propriétés Duration et Position fera l’affaire. Les propriétés Duration et Position étant du type TimeSpan, nous allons utiliser un IValueConverter pour transformer la valeur en Double.

<UserControl x:Class="VideoPlayer1.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:slvideo="clr-namespace:SLMedia.Video;assembly=SLMedia.Video"

   xmlns:slec="clr-namespace:SLExtensions.Controls;assembly=SLExtensions.Controls"

   xmlns:sled="clr-namespace:SLExtensions.Data;assembly=SLExtensions.Data"

   Foreground="White"

    >

    <UserControl.Resources>

        <slvideo:VideoController x:Name="controller" />

        <sled:TimeSpanConverter x:Name="timeSpanConverter"/>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource controller}}">

        <MediaElement x:Name="mediaelement"

                    Source="{Binding Path=CurrentItem.SourceUri}"

                    slvideo:VideoController.MediaElement="{StaticResource controller}"

                    IsMuted="{Binding Path=IsMuted}"

                    Volume="{Binding Path=Volume}"

                    AutoPlay="{Binding Path=AutoPlay}"   

                    />

        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Background="#50000000" Margin="30">

            <CheckBox IsChecked="{Binding Path=IsPlaying, Mode=TwoWay}" />

            <slec:Slider Maximum="{Binding Converter={StaticResource timeSpanConverter}, Path=Duration}"

                        MoveValue="{Binding Converter={StaticResource timeSpanConverter}, Mode=TwoWay, Path=Position}"

                        Width="200" Margin="20,0,0,0"

                        />

            <TextBlock Text="{Binding Converter={StaticResource timeSpanConverter}, Path=Position}"

                    Margin="20,0,0,0"/>

            <TextBlock Text=" / " />

            <TextBlock Text="{Binding Converter={StaticResource timeSpanConverter}, Path=Duration}" />

        </StackPanel>

    </Grid>

</UserControl>

Si vous utilisez le slider de base de silverlight, vous noterez des problèmes lorsque vous déplacez le curseur. Pendant que vous déplacez le curseur, la propriété Value peut être remise à jour par le binding et le curseur donne l’impression de sauter.

Etape 4 : Volume

De même que pour la position, nous allons utiliser un slider pour le volume et une checkbox pour le mute

            <CheckBox IsChecked="{Binding Path=IsMuted, Mode=TwoWay}" Margin="20,0,0,0"/>

            <Slider Maximum="1" SmallChange="0.1" LargeChange="0.1"

                   Value="{Binding Path=Volume, Mode=TwoWay}" Width="50" />

Etape 5 : Buffering - Downloading

Encore du binding

            <TextBlock Text="Buffering:" />

            <TextBlock Text="{Binding Path=BufferingProgress, Mode=TwoWay, Converter={StaticResource strConverter}, ConverterParameter='{0:p}'}" />

            <Slider Maximum="1"

                   Value="{Binding Path=BufferingProgress, Mode=TwoWay}" Width="50" IsHitTestVisible="False" Margin="5,0,0,0"/>

 

            <TextBlock Text="Downloading:" Margin="20,0,0,0"/>

            <TextBlock Text="{Binding Path=DownloadProgress, Mode=TwoWay, Converter={StaticResource strConverter}, ConverterParameter='{0:p}'}" />

            <Slider Maximum="1"

                   Value="{Binding Path=DownloadProgress, Mode=TwoWay}" Width="50" IsHitTestVisible="False"

                   Margin="5,0,0,0"/>

Etape 6 : Gestion des états visuels

Le VideoController centralise les propriétés de notre player video. Il est possible de changer l’état visuel de nos contrôles en fonction des propriétés du VideoController. Nous allons utiliser le behaviors fournis par SLExtensions pour mapper ces propriétés à nos VisualStates.

<UserControl x:Class="VideoPlayer1.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:slvideo="clr-namespace:SLMedia.Video;assembly=SLMedia.Video"

   xmlns:slec="clr-namespace:SLExtensions.Controls;assembly=SLExtensions.Controls"

   xmlns:sled="clr-namespace:SLExtensions.Data;assembly=SLExtensions.Data"

 

   xmlns:inter="clr-namespace:Microsoft.Expression.Interactivity;assembly=Microsoft.Expression.Interactivity"

   xmlns:slint="clr-namespace:SLExtensions.Interactivity;assembly=SLExtensions.Interactivity"

 

   xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"

 

   Foreground="White"

    >

    <UserControl.Resources>

        <slvideo:VideoController x:Name="controller" />

        <sled:TimeSpanConverter x:Name="timeSpanConverter"/>

        <sled:StringFormatConverter x:Name="strConverter"/>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource controller}}">

        <vsm:VisualStateManager.VisualStateGroups>

            <vsm:VisualStateGroup x:Name="PlayingStates">

                <vsm:VisualStateGroup.Transitions>

                    <vsm:VisualTransition GeneratedDuration="00:00:00.3000000"/>

                </vsm:VisualStateGroup.Transitions>

                <vsm:VisualState x:Name="Playing">

                    <Storyboard>

                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="stackPanel" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#5039FF00"/>

                        </ColorAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

                <vsm:VisualState x:Name="Paused">

                    <Storyboard>

                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="stackPanel" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#50D56700"/>

                        </ColorAnimationUsingKeyFrames>

                    </Storyboard>

                </vsm:VisualState>

            </vsm:VisualStateGroup>

        </vsm:VisualStateManager.VisualStateGroups>

        <inter:Interaction.Behaviors>

            <slint:MapState Source="{StaticResource controller}" Property="IsPlaying" >

                <slint:MapStateMapping StateName="Playing" Value="true"/>

                <slint:MapStateMapping StateName="Paused" Value="false"/>

            </slint:MapState>

        </inter:Interaction.Behaviors>

 

        <MediaElement x:Name="mediaelement"

                    Source="{Binding Path=CurrentItem.SourceUri}"

                    slvideo:VideoController.MediaElement="{StaticResource controller}"

                    IsMuted="{Binding Path=IsMuted}"

                    Volume="{Binding Path=Volume}"

                    AutoPlay="{Binding Path=AutoPlay}"   

                    />

        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Background="#50000000" Margin="30" x:Name="stackPanel">

            <CheckBox IsChecked="{Binding Path=IsPlaying, Mode=TwoWay}" />

            <slec:Slider Maximum="{Binding Converter={StaticResource timeSpanConverter}, Path=Duration}"

                        MoveValue="{Binding Converter={StaticResource timeSpanConverter}, Mode=TwoWay, Path=Position}"

                        Width="200" Margin="20,0,0,0"

                        />

            <TextBlock Text="{Binding Converter={StaticResource timeSpanConverter}, Path=Position}"

                    Margin="20,0,0,0"/>

            <TextBlock Text=" / " />

            <TextBlock Text="{Binding Converter={StaticResource timeSpanConverter}, Path=Duration}" />

 

            <CheckBox IsChecked="{Binding Path=IsMuted, Mode=TwoWay}" Margin="20,0,0,0"/>

            <Slider Maximum="1" SmallChange="0.1" LargeChange="0.1"

                   Value="{Binding Path=Volume, Mode=TwoWay}" Width="50" />

 

            <TextBlock Text="Buffering:" />

            <TextBlock Text="{Binding Path=BufferingProgress, Mode=TwoWay, Converter={StaticResource strConverter}, ConverterParameter='{0:p}'}" />

            <Slider Maximum="1"

                   Value="{Binding Path=BufferingProgress, Mode=TwoWay}" Width="50" IsHitTestVisible="False" Margin="5,0,0,0"/>

 

            <TextBlock Text="Downloading:" Margin="20,0,0,0"/>

            <TextBlock Text="{Binding Path=DownloadProgress, Mode=TwoWay, Converter={StaticResource strConverter}, ConverterParameter='{0:p}'}" />

            <Slider Maximum="1"

                   Value="{Binding Path=DownloadProgress, Mode=TwoWay}" Width="50" IsHitTestVisible="False"

                   Margin="5,0,0,0"/>

 

            <CheckBox IsChecked="{Binding Path=IsFullscreen, Mode=TwoWay}" Margin="20,0,0,0" Content="Fullscreen" Foreground="White"/>

 

        </StackPanel>

    </Grid>

</UserControl>

#    Comments [0] |
# Friday, May 08, 2009

Testez moonlight sous Windows

 moonlight_logoMême si au final vous aurez une fenêtre faisant tourner moonlight sur votre windows, ce ne sera pas réellement le cas. L’idée expliquée dans ce post de Chris Cavanagh est d’utiliser Portable Ubuntu. Cette distribution d’Ubuntu tourne en tant que programme windows. Vous pourrez donc installer Moonlight sur le firefox d’ubuntu.

#    Comments [0] |
# Friday, April 24, 2009

Gestion de la mémoire sous Silverlight / .Net

On m’a interrogé récemment sur la libération de la mémoire des objets en silverlight. Je vais essayer de synthétiser ici ce que j’ai répondu.

Silverlight réagi exactement de la même manière que le framework .Net. La mémoire est gérée par un garbage collector (ramasse-miettes) qui libère les allocations mémoires des objets une fois qu’ils ne sont plus référencés. 

L’application de test

Voila le principe de mon application de test. Au clic sur un bouton, mon application ajoute un usercontrol dans l’arbre visuel et au clic sur un autre bouton, je l’enlève. La seule référence vers mon contrôle est faite par l’arbre graphique. Dans le constructeur de mon usercontrol, j’incrémente un compteur qui m’indique le nombre d’instances en cours et dans le destructeur je le décrémente. Ce compteur est affiché par binding dans l’interface graphique.

Petite parenthèse pour le fonctionnement de l’application, la destruction des objets ne se passe pas dans le thread graphique, pour que la mise a jour du binding fonctionne, je lève l’événement PropertyChanged de AppController par le SynchronizationContext récupéré du thread graphique.

Contrôle sans références

image

A l’ajout de mon usercontrol, on note bien l’incrémentation du compteur.

image

Je clic sur le bouton Remove et je vois que mon compteur est toujours à 1. Le garbage collector ne passe pas tout de suite. Il y a un algorithme qui détermine quand le garbage collector doit passer sur les objets. Pour le besoin de mon application, je vais forcer l’appel au garbage collector au clic sur le bouton Force GC. Il faut noter que c’est une mauvaise pratique que de vouloir appeler soit même de garbage collector. GC.Collect() ne doit être appelée que si vous en avez réellement l’utilité. Un appel trop fréquent peut dégrader les performances de votre application.

image

Le clic sur Force GC ramène bien le compte de référence à 0.

Contrôle s’abonnant à un événement

Comme je vous le précisait dans le fonctionnement de mon application, je ne maintient aucune référence vers mes usercontrol, je n’ai théoriquement rien qui va empêcher le garbage collector de passer. Cependant le contrôle lui même peut faire qu’il ne sera pas libéré. Ici ma classe AppController fourni un événement auquel mes contrôles peuvent s’abonner. MyControl2 va s’abonner à cet événement. Au clic sur le bouton Raise Test event, vous verez que la couleur du usercontrol change.

image

Si vous ajoutez le usercontrol MyControl2, que vous l’enlevez et que vous forcez le garbage collector, vous verrez que l’instance ne se libère pas. Que se passe-t-il ?
Lorsque le contrôle s’est abonné à l’événement de AppController, il a créé un EventHandler (une référence vers une fonction) et l’a ajouté à la liste des fonctions à appeler lorsque l’événement est levé. Un événement n’est rien d’autre qu’une liste de références qui pointe vers les fonctions à appeler. Pour que notre contrôle se libère, il faut donc supprimer cette référence. Un clic sur Clear Test event list demande à AppController de vider sa liste d’événement, un clic sur Force GC remettra les compteurs à 0.

IDisposable

Une bonne pratique pour gérer la libération de ressources internes et le désabonnement d’évenement est d’implémenter l’interface IDisposable. Cette interface vous demande d’implémenter la fonction Dispose dans laquelle vous pouvez remettre à null les références externes, vous désabonnez d’événements, etc…
Le usercontrol MyControl3 implémente cette interface. Lorsque le bouton Remove First Item est cliqué, mon application vérifie si le contrôle à enlever implémente IDisposable et l’appel le cas échéant.

image

Les clics successifs sur Add MyControl3, Remove First Item et Force GC incrémentent et décrémentent bien notre compteur de références.

WeakReference et proxy d’événement

Si vous ne maitrisez pas la logique d’ajout suppression des contrôles, vous ne pouvez pas toujours appeler la IDisposable.Dispose. (A noter que ce n’est pas parce qu’un contrôle n’est plus dans l’arbre d’affichage qu’il faut nécessairement le disposer. Un tabcontrol ajoute et enlève chaque tab de l’abre graphique.)

La classe WeakReference permet de maintenir une référence “qui ne compte pas” du point de vue du garbage collector. La propriété Target de la WeakReference vaut null quand le garbage collector est passé et renvoi l’instance de l’objet sinon.

Ici pour gérer notre abonnement à l’événement, nous allons utiliser ces WeakReference pour pouvoir quand même être libéré. La fonction à appeler en retour de l’événement est : MyControl4.Instance_TestEvent. Cette fonction est privée. WeakListener est une classe interne à MyControl4 et va donc pouvoir appeler cette fonction privée. C’est WeakListener.Instance_TestEvent que nous allons abonner à l’événement de AppController. Lors de l’appel de l’événement, elle va vérifier que sa WeakReference vers MyControl4 est toujours valide, si tel est le cas, cette fonction va transmettre l’appel vers la fonction MyControl4.Instance_TestEvent, sinon elle se désabonne de l’appelant et pourra donc aussi être collecté par le garbage collector.

    public partial class MyControl4 : UserControl

    {

 

        private class WeakListener

        {

            public WeakListener(MyControl4 target)

            {

                weakReference = new WeakReference(target);

            }

 

            private readonly WeakReference weakReference;

 

            public void Instance_TestEvent(object sender, EventArgs e)

            {

                MyControl4 ctrl = (MyControl4)weakReference.Target;

 

                if (ctrl != null)

                {

                    ctrl.Instance_TestEvent(sender, e);

                }

                else

                {

                    ((AppController)sender).TestEvent -= this.Instance_TestEvent;

                }

            }

        }

 

        void Instance_TestEvent(object sender, EventArgs e)

        {

            this.LayoutRoot.Background = new SolidColorBrush(GetRandomColor());

        }

 

       

    }

image

Un clic sur Add MyControl4, Remove et Force GC vous donnera un compte de référence de MyControl4 à 0. Il faut noter qu’à ce stade, MyControl4 a été libéré mais le WeakListener est toujours présent en mémoire. Sa libération ne sera effectué que lorsque l’événement sera appelé, la classe s’autodésabonnera toute seule de l’événement et pourra est collectée.

WeakEventList

J’ai ajouté dans slextensions une WeakEventList permettant de gérer une liste d’événements basés sur des WeakReference. A la place de stocker les événements par le processus standard de .Net, nous créons une liste de WeakReference directement sur les delegates abonnés.

Voila la déclaration de l’événement de ma class AppController.

        private WeakEventList<EventArgs> weakTestEvent;

 

        private event EventHandler<EventArgs> testEvent;

        public event EventHandler<EventArgs> TestEvent

        {

            add

            {

                if(UseWeakEvent)

                {

                    weakTestEvent.Event += value;

                }

                else

                {

                    testEvent += value;                   

                }

            }

            remove

            {

                if (UseWeakEvent)

                {

                    weakTestEvent.Event -= value;

                }

                else

                {

                    testEvent -= value;

                }

            }

        }

image

En cochant la case UseWeakEvent et en reproduisant le cas qui posait problème au début de ce post ( Add MyControl2, Remove, Force GC), on voit que la référence est bien libérée.

Vous trouvez plusieurs autres manières de créer des “weakevent” sur internet. Celle-ci est la seule qui marche en silverlight pour des appels à des fonctions privées. De nombreuses utilisent la réflexion pour pouvoir accéder à la fonction finale à appeler par l’événement, ceci n’est pas permit en silverlight si la déclaration est privée. Le mode MediumTrust de silverlight nous en empêche.

#    Comments [7] |
# Friday, April 17, 2009

Silverlight Extensions et Silverlight Contrib

Comme nous vous le disions dans un précédent post et comme vous avez pu le voir sur les sites respectifs des deux projets, les 2 projets sont en cours de fusion. Le nom du projet résultant de cette fusion a été publié par Page Brooks sur un son blog. Un vote était organisé et c’est le nom silverlight extensions qui a remporté la majorité des voix. Retrouvez le post de Page Brooks

image[1] 

En attendant que la fusion avance, n’oubliez pas d’aller sur le site web des deux projets. slextensions et silverlight contrib

#    Comments [0] |
# Tuesday, April 14, 2009

Utilisation des behaviors pour localiser une application silverlight ou WPF

Il y a quelques temps déjà, j’ai ajouté à slextensions une classe permettant de localiser simplement une application à partir d’attachedproperty. La mode étant aux behaviors, j’ai rajouté une classe permettant cette localisation à la branche SL3 de slextensions. Cette classe possède 4 propriétés :

  • Key : la clé de la ressource provenant de votre ResourceManager
  • PropertyName : Le nom de la propriété à localiser
  • ResourceManagerKey : La clé avec laquelle retrouver le ResourceManager dans les ressources de l’application
  • ConvertXaml : Si la resource contient du Xaml, permet d’instancier ce xaml par le XamlReader.

L’utilisation est très simple voici un exemple de localisation d’un texte

            <TextBlock  >

                <i:Interaction.Behaviors>

                    <SLExtensions_Interactivity:Localize Key="WelcomeMessage" PropertyName="Text" ResourceManagerKey="MyResource" />

                </i:Interaction.Behaviors>

            </TextBlock>

et d’un ContentControl

            <ContentControl Height="121" Width="244" Canvas.Left="25" Canvas.Top="206" >

                <i:Interaction.Behaviors>

                    <SLExtensions_Interactivity:Localize Key="Welcome" PropertyName="Content" ResourceManagerKey="MyResource" ConvertXaml="True"/>

                </i:Interaction.Behaviors>

            </ContentControl>

Le code de la classe :

using System;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using Microsoft.Expression.Interactivity;

using System.Resources;

using System.Globalization;

using System.Windows.Markup;

using SLExtensions.Globalization;

 

namespace SLExtensions.Interactivity

{

    public class Localize : Behavior<FrameworkElement>

    {

 

        private string key;

 

        public string Key

        {

            get

            {

                return this.key;

            }

 

            set

            {

                if (this.key != value)

                {

                    this.key = value;

                    Refresh();

                }

            }

        }

 

        private bool convertXaml;

 

        public bool ConvertXaml

        {

            get

            {

                return this.convertXaml;

            }

 

            set

            {

                if (this.convertXaml != value)

                {

                    this.convertXaml = value;

                    Refresh();

                }

            }

        }

 

 

        private DependencyProperty dependencyProperty;

 

        private string propertyName;

 

        public string PropertyName

        {

            get

            {

                return this.propertyName;

            }

 

            set

            {

                if (this.propertyName != value)

                {

                    this.propertyName = value;

                    this.dependencyProperty = null;

                    Refresh();

                }

            }

        }

 

        private string resourceManagerKey;

 

        public string ResourceManagerKey

        {

            get

            {

                return this.resourceManagerKey;

            }

 

            set

            {

                if (this.resourceManagerKey != value)

                {

                    this.resourceManagerKey = value;

                }

            }

        }

 

        private void Refresh()

        {

            if (AssociatedObject == null

                || string.IsNullOrEmpty(propertyName)

                || string.IsNullOrEmpty(Key)

                || string.IsNullOrEmpty(ResourceManagerKey))

                return;

 

            if (dependencyProperty == null)

            {

                Type t = AssociatedObject.GetType();

                var field = t.GetField(propertyName + "Property", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);

                if (field != null)

                {

                    dependencyProperty = field.GetValue(null) as DependencyProperty;

                }

            }

 

            if (dependencyProperty != null

                && Application.Current != null)

            {

                ResourceManager resourceManager = null;

                var obj = Application.Current.Resources[ResourceManagerKey];

                if (obj is ResourceLoader)

                {

                    resourceManager = ((ResourceLoader)obj).GetResourceManager();

                }

                else

                {

                    resourceManager = obj as ResourceManager;

                }

 

                var resourceValue = resourceManager.GetObject(Key, CultureInfo.CurrentCulture);

                if (convertXaml && resourceValue is string)

                {                   

                    resourceValue = XamlReader.Load((string)resourceValue);

                }

                AssociatedObject.SetValue(dependencyProperty, resourceValue);

            }

        }

 

        protected override void OnAttached()

        {

            Refresh();

            base.OnAttached();

        }

 

        protected override void OnDetaching()

        {

            base.OnDetaching();

        }

    }

}

#    Comments [0] |
# Tuesday, April 07, 2009

Les behavior en Silverlight 3 - MapState behavior

Exemple

Les behaviors introduits par blend 3 servent à étendre les fonctionnalités d’un contrôle à l’aide d’une nouvel élément que l’on peut poser graphiquement dans l’interface de blend. L’ajout de comportement n’est pas nouveau, c’est utilisé dans de nombreux projets (par exemple, la navigation deepzoom de slextensions est géré de cette manière), mais ici, l’intégration à blend est plutôt pratique et permet à un intégrateur de gérer du comportement sans écrire de code.

La classe MapState me permet de mapper l’état d’une propriété d’un contrôleur à un VisualState posé sur mon contrôle.

Ma classe contrôleur de test n’a rien de particulier. Voici sa définition.

    public class ApplicationController : INotifyPropertyChanged

    {

 

        #region INotifyPropertyChanged Members

 

        public event PropertyChangedEventHandler PropertyChanged;

 

        protected void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

 

        #endregion

 

        private TestStates state;

        public TestStates State

        {

            get { return state; }

            set

            {

                if (state != value)

                {

                    state = value;

                    OnPropertyChanged(StatePropertyName);

                }

            }

        }

 

        public const string StatePropertyName = "State";

 

    }

J’instancie ce contrôleur en l’injectant dans les ressources de l’application

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

            x:Class="TestBehavior.App"

            xmlns:controllers="clr-namespace:TestBehavior.Controllers"

            >

    <Application.Resources>

        <controllers:ApplicationController x:Key="appController" />

    </Application.Resources>

</Application>

Je rajoute les références qui vont bien à mon projet

image

Je peux maintenant rajoute mon behavior à l’aide de blend

image

Je remplie le mapping et la source de mon behavior avec blend

image

Le morceau de code qui change l’état du contrôleur.

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            ApplicationController appController = Application.Current.Resources["appController"] as ApplicationController;

            appController.State = (TestStates) ((1 + (int)appController.State) % 3);

        }

Le code

Ma classe MapState hérite d’une classe ancêtre Behavior que vous trouverez dans l’assembly Microsoft.Expression.Interactivity.dll qui se trouve dans le dossier Libraries/Silverlight de Blend 3.

J’ai déclaré une propriété Source qui me sert à récupérer l’objet métier qui va me donner mon état et une propriété Property me donnant le nom de la propriété d’état. Ma source doit bien sur implémenter INotifyPropertyChanged pour que MapState soit notifiée du changement de propriété.
J’ai également une collection de MapStateMapping qui me fournisse un couple Valeur / VisualState.
Au changement de valeur sur ma source, je n’ai plus qu’à comparer par rapport aux valeurs de mapping est à déclancher le visualstate sur mon élément associé.

Le code complet de la classe :

namespace SLExtensions.Interactivity

{

    [ContentProperty("Mappings")]

    public class MapState : Behavior<FrameworkElement>

    {

        public MapState()

        {

            Mappings = new List<MapStateMapping>();

        }

 

        #region Source

 

        private PropertyInfo propertyInfo = null;

 

        public object Source

        {

            get

            {

                return (object)GetValue(SourceProperty);

            }

 

            set

            {

                SetValue(SourceProperty, value);

            }

        }

 

        /// <summary>

        /// Source depedency property.

        /// </summary>

        public static readonly DependencyProperty SourceProperty =

            DependencyProperty.Register(

                "Source",

                typeof(object),

                typeof(MapState),

                new PropertyMetadata((d, e) => ((MapState)d).OnSourceChanged((object)e.OldValue, (object)e.NewValue)));

 

        /// <summary>

        /// handles the SourceProperty changes.

        /// </summary>

        /// <param name="oldValue">The old value.</param>

        /// <param name="newValue">The new value.</param>

        private void OnSourceChanged(object oldValue, object newValue)

        {

            propertyInfo = null;

            INotifyPropertyChanged notifyingObject = oldValue as INotifyPropertyChanged;

            if (notifyingObject != null)

            {

                notifyingObject.PropertyChanged -= new PropertyChangedEventHandler(notifyingObject_PropertyChanged);

            }

            notifyingObject = newValue as INotifyPropertyChanged;

            if (notifyingObject != null)

            {

                notifyingObject.PropertyChanged += new PropertyChangedEventHandler(notifyingObject_PropertyChanged);

            }

            if (newValue != null && !string.IsNullOrEmpty(Property))

            {

                propertyInfo = newValue.GetType().GetProperty(Property);

            }

            RefreshState();

        }

 

        #endregion Source

 

        #region Property

 

        public string Property

        {

            get

            {

                return (string)GetValue(PropertyProperty);

            }

 

            set

            {

                SetValue(PropertyProperty, value);

            }

        }

 

        /// <summary>

        /// Property depedency property.

        /// </summary>

        public static readonly DependencyProperty PropertyProperty =

            DependencyProperty.Register(

                "Property",

                typeof(string),

                typeof(MapState),

                new PropertyMetadata((d, e) => ((MapState)d).OnPropertyChanged((string)e.OldValue, (string)e.NewValue)));

 

        /// <summary>

        /// handles the PropertyProperty changes.

        /// </summary>

        /// <param name="oldValue">The old value.</param>

        /// <param name="newValue">The new value.</param>

        private void OnPropertyChanged(string oldValue, string newValue)

        {

            if (Source == null)

                propertyInfo = null;

            else

                propertyInfo = Source.GetType().GetProperty(newValue);

 

            RefreshState();

        }

 

        #endregion Property

 

        public List<MapStateMapping> Mappings { get; private set; }

 

 

 

        void notifyingObject_PropertyChanged(object sender, PropertyChangedEventArgs e)

        {

            if(e.PropertyName == Property)

                RefreshState();

        }

 

        private void RefreshState()

        {

            // Do not refresh at desing time

            if (!HtmlPage.IsEnabled)

                return;

 

            if (Source != null && propertyInfo != null )

            {

                object propValue = propertyInfo.GetValue(Source, null);

 

                if (propValue == null)

                {

                    var nullMappings = from m in Mappings

                                       where m.Value == null

                                       || ((m.Value is string) && string.Empty.Equals(m.Value))

                                       select m.StateName;

 

                    try

                    {

                        SLExtensions.Controls.Animation.VisualState.GoToState(AssociatedObject, UseTransitions, nullMappings.ToArray());

                    }

                    catch

                    {

                    }

                }

                else

                {

                    MapStateMapping mapping = null;

                    foreach (var item in Mappings)

                    {

                        var value = item.Value;

                        if (value != null)

                        {

                            if (object.Equals(value, propValue))

                            {

                                mapping = item;

                                break;

                            }

 

                            if (propValue is IConvertible && value is IConvertible)

                            {

                                try

                                {

                                    var val2 = Convert.ChangeType(propValue, value.GetType(), CultureInfo.InvariantCulture);

                                    if (object.Equals(val2, value))

                                    {

                                        mapping = item;

                                        break;

                                    }

                                }

                                catch

                                {

                                }

 

                                try

                                {

                                    var val2 = Convert.ChangeType(value, propValue.GetType(), CultureInfo.InvariantCulture);

                                    if (object.Equals(val2, propValue))

                                    {

                                        mapping = item;

                                        break;

                                    }

                                }

                                catch

                                {

                                }

                            }

                        }

                    }

 

                    if (mapping != null)

                    {

                        try

                        {

                            SLExtensions.Controls.Animation.VisualState.GoToState(AssociatedObject, UseTransitions, mapping.StateName);

                        }

                        catch

                        {

                        }

                    }

                }

            }

        }

 

        /// <summary>

        ///

        /// </summary>

        public static readonly DependencyProperty UseTransitionsProperty = DependencyProperty.Register("UseTransitions", typeof(bool), typeof(MapState), new PropertyMetadata(true));

 

        /// <summary>

        /// True if transitions should be used for the state change.

        /// </summary>

        public bool UseTransitions

        {

            get { return (bool)this.GetValue(MapState.UseTransitionsProperty); }

            set { this.SetValue(MapState.UseTransitionsProperty, value); }

        }

 

        /// <summary>

        /// Hooks up necessary handlers for the state changes.

        /// </summary>

        protected override void OnAttached()

        {

            base.OnAttached();

 

            FrameworkElement element = this.AssociatedObject;

            if (element != null)

            {

                element.Loaded += new RoutedEventHandler(element_Loaded);

 

                RefreshState();

            }

            else

            {

                Dispatcher.BeginInvoke(delegate

                {

                    this.OnAttached();

                });

            }

        }

 

        void element_Loaded(object sender, RoutedEventArgs e)

        {

            RefreshState();

        }

    }

}

Les sources

En attendant de publier tout ça sur slextensions, vous pouvez télécharger le projet ici: 
Vous pouvez retrouver d’autres behavior sur le site de la gallery expression : http://gallery.expression.microsoft.com/

#    Comments [0] |
# Friday, April 03, 2009

silverlight 3 beta et silverlight 2 sur la même machine de dev

logo Travailler avec des versions beta de logiciels est parfois compliqué. Que faire avec les outils ? Est-ce que je les installe sur ma machine de développement, est-ce que je les installe sur une machine virtuelle ?

Vous trouverez ici : http://blogs.msdn.com/amyd/archive/2009/03/18/switching-from-silverlight-3-tools-to-silverlight-2-tools.aspx un petit script permettant de changer simplement la version des Silverlight tools utilisés par visual studio.

#    Comments [0] |
# Friday, March 13, 2009

Silverlight Contrib et Silverlight Extensions fusionnent

logo-slExt-ContrNous sommes fières d’annoncer aujourd’hui la fusion de Silverlight Contrib et Silverlight Extensions en un seul et unique projet ! Nous pensons que cette fusion apportera beaucoup de bénéfic es à la communauté. Cette combinaison permettra aux développeurs d’avoir un point unique pour rechercher des contrôles à intégrer dans leurs applications ainsi que pour en soumettre de nouveaux. Cette action réduira le temps passé dans chaque projet pour vous fournir des fonctionnalités équivalentes et nous permettra d’accélérer la cadence des releases.

Pendant la phase de consolidation du code, nous vous encourageons à nous soumettre vos retours ou problèmes à chaque projets. Nous fusionnerons les informations des deux projets lorsque le site commun sera ouvert et le nom final déterminé.

Quel nom allons-nous garder ? Cette décision vous appartient ! Vos votes détermineront le plus populaire. Assurez-vous que votre préférence soit prise en compte, votez ! Les votes seront acceptés jusqu’au 29 mars 2009 23:00 (GMT+1)

Nous vous tiendrons informé des évolutions sur chacun des blogs des 2 projets Silverlight Contrib et Silverlight Extensions.

Retrouvez ce post sur le site de Silverlight Contrib

Les équipes de slextensions et silverlight contrib.

#    Comments [0] |
# Friday, February 13, 2009

SLExtensions Converters – AlternateBackgroundConverter

Le projet SLExtensions commence à être assez conséquent. Je vais essayer de poster un peu plus régulièrement sur ce qu’il contient et compléter les exemples. Je commence donc cette série d’article par un des nombreux converter contenu dans le projet.

L’AlternateBackgroundConverter retourne un brush différent chaque fois que le converter est appelé.
Ce converter nécessite que la propriété ConverterParameter soit spécifiée lors du binding.
Le ConverterParameter peut être soit une string contenant une liste de couleurs séparées par des espaces soit une référence à un objet AlternateBackgroundConverterParameter.

  • Lorsqu’une string est passée, les couleurs peuvent être spécifiées au format argb sous forme hexadécimale (#RRGGBB ou #AARRGGBB) ou être un des nom de propriété existant dans la classe Colors (White, Red, Yellow, etc…)
  • Lorsqu’un AlternateBackgroundConverterParameter est passé, vous pouvez spécifier n’importe quel type de brush, SolidBrush, GradientBrush, VideoBrush, ImageBrush

 

<UserControl x:Class="SLExtensions.Showcase.PageAlternateConverter"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:sled="clr-namespace:SLExtensions.Data;assembly=SLExtensions"

   xmlns:scControllers="clr-namespace:SLExtensions.Showcase.Controllers;assembly=SLExtensions.Showcase"

  >

    <UserControl.Resources>

 

        <sled:AlternateBrushConverter x:Key="alternateConverter" />

 

        <sled:AlternateBrushConverterParameter x:Key="alternateParameter" >

            <sled:AlternateBrushConverterParameter.Brushes>

                <SolidColorBrush Color="White"/>

                <LinearGradientBrush StartPoint="0.45666,0.185989" EndPoint="1.22116,0.185989">

                    <LinearGradientBrush.RelativeTransform>

                        <TransformGroup>

                            <SkewTransform CenterX="0.45666" CenterY="0.185989" AngleX="0.135522" AngleY="0"/>

                            <RotateTransform CenterX="0.45666" CenterY="0.185989" Angle="82.8625"/>

                        </TransformGroup>

                    </LinearGradientBrush.RelativeTransform>

                    <GradientStop Color="#80FFFFFF" Offset="0"/>

                    <GradientStop Color="#808C8A8C" Offset="0.62345"/>

                    <GradientStop Color="#8019161A" Offset="1"/>

                </LinearGradientBrush>

                <SolidColorBrush Color="Gainsboro"/>

                <SolidColorBrush Color="#FFDD22"/>

            </sled:AlternateBrushConverterParameter.Brushes>

        </sled:AlternateBrushConverterParameter>

    </UserControl.Resources>

    <StackPanel x:Name="LayoutRoot" Background="White" >

        <TextBlock Text="Alternating brush converter"/>

        <ItemsControl ItemsSource="ABCDEFGH">

            <ItemsControl.ItemTemplate>

                <DataTemplate>

                    <Grid Background="{Binding Path='', Converter={StaticResource alternateConverter}, ConverterParameter={StaticResource alternateParameter}}">

                        <TextBlock Text="{Binding}" />   

                    </Grid>

                </DataTemplate>

            </ItemsControl.ItemTemplate>

        </ItemsControl>

 

        <ItemsControl ItemsSource="ABCDEFGH" Margin="0,40,0,0">

            <ItemsControl.ItemTemplate>

                <DataTemplate>

                    <Grid Background="{Binding Path='', Converter={StaticResource alternateConverter}, ConverterParameter='White Gray #FFDD22'}">

                        <TextBlock Text="{Binding}" />

                    </Grid>

                </DataTemplate>

            </ItemsControl.ItemTemplate>

        </ItemsControl>

    </StackPanel>

</UserControl>

#    Comments [0] |
# Saturday, October 25, 2008

Entity Framework – Génération du mapping et du sql

Ayant pas mal joué avec NHibernate, je voulais tester l’ORM de Microsoft : ADO.Net Entity Framework. Une bonne partie des exemples qui existent sur internet partent d’un model de base de données existant et génère le fichier de mapping. C’est très pratique pour partir d’un existant, mais ne sert pas lorsqu’on veut créer un nouveau projet.

J’ai apprécié le designer de classes, mais ne s’avère pas très pratique pour modeler un projet entier. Il ne permet pas par exemple d’avoir plusieurs diagrammes de classes. Ayant déjà créé mon propre outil de génération pour NHibernate (NHib.Wizard), je suis reparti de ce projet pour faire de même avec Entity Framework.

Je peux donc maintenant partir d’un modeleur UML (ArgoUML, j’ai pas trouvé mieux en gratuit pour l’instant) et générer mon fichier model.edmx ainsi que le script de base de données correspondant.

L’application en ClickOnce : http://labs.ucaya.com/EF.Wizard/publish.htm

Une video de démonstration : http://silverlight.services.live.com/49123/c3ca3d75-b05f-49d2-8522-b22131f2ee8d/Generation.wmv

#    Comments [0] |

Partager des DataContracts entre WebServices avec Silverlight

Les différents webservices d’une même application ont parfois besoin de partager les mêmes structures de données. Par défaut, lors de l’ajout de ces webservices à la solution, Silverlight duplique les classes de données générées.

Imaginons un DataContract User retourné par un webservice Users.svc qui gère les utilisateurs et utilisé dans webservice Rights.svc qui gère les droits de l’application.

    [DataContract]

    public class User

    {

        [DataMember]

        public string FirstName { get; set; }

 

        [DataMember]

        public string LastName { get; set; }

 

    }

 

    [ServiceContract(Namespace = "")]

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    public class Users

    {

        [OperationContract]

        public User GetUsers()

        {

            return new User { FirstName = "Thierry", LastName = "Bouquain" };

        }

    }

 

    [ServiceContract(Namespace = "")]

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    public class Rights

    {

        [OperationContract]

        public User AssignUserRights(User user)

        {

            return user;

        }

    }

solution Pour partager le datacontract User dans les classes générées, il suffit de créer la même classe User avec le même namespace dans un project silverlight et de le référencer dans l’application silverlight.
Une fois les références des webservices mise à jours, la classe User ne sera plus générée.

Pour éviter de dupliquer les datacontracts, il est possible de créer un projet silverlight qui utilise les mêmes fichiers côté client et côté serveur.
Evidement, dans ce cas, le code du DataContract ne peux référencer des objets serveurs qui n’existent pas côté silverlight.

Je vous invite à regarder le projet attaché à ce post.

 

 

 

 

 

 

 

#    Comments [0] |
# Tuesday, October 07, 2008

Captcha en Silverlight

Encore un nouveau venu dans SLExtensions. Voici un contrôle de captcha qui se branche sur le service gratuit www.recaptcha.net.

Le principe

recaptcha-api-diagram 

  1. 1 L’utilisateur télécharge l’application silverlight qui contient le contrôle de captcha.
  2. 2 Le contrôle de captcha récupère une image plus une clé de cryptage du serveur d’image de www.recaptcha.net.
  3. 3 Le contrôle de captcha renvoie au serveur web (par POST, Get, Webservice, comme vous voulez), la clé de cryptage optenue auprès de www.recaptcha.net ainsi que les mots saisis par l’utilisateur.
  4. 4 Le serveur web fait un appel au serveur www.recaptcha.net en passant les données venues de l’utilisateur (mots + clé) plus une clé privée. Le serveur reCaptcha valide ou non le captcha. Le serveur fait ensuite (ou non) son traitement.
  5. 5 Retour vers l’utilisateur pour l’informer du résultat.

Le contrôle

<UserControl x:Class="SLExtensions.Showcase.PageRecaptcha"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:slec="clr-namespace:SLExtensions.Controls;assembly=SLExtensions.Controls"

   Width="400" Height="300">

    <StackPanel x:Name="LayoutRoot" Background="White">

        <slec:ReCaptcha x:Name="captcha" PublicKey="6LesLAMAAAAAACjPBtyjD80bwTowvEQrslT8bJvT" HorizontalAlignment="Center" VerticalAlignment="Center" >

            <slec:ReCaptcha.Verifier>

                <slec:ReCaptchaHttpPostVerifier Url="/VerifyCaptcha.aspx" Success="ReCaptchaHttpPostVerifier_Success" Failed="ReCaptchaHttpPostVerifier_Failed"/>

            </slec:ReCaptcha.Verifier>

        </slec:ReCaptcha>

    </StackPanel>

</UserControl>

Pour utiliser le contrôle, vous devez fournir un objet qui implémente l’interface IReCaptchaVerifier. C’est lui qui va se charger de faire l’appel au webservice vers votre serveur. Dans SLExtensions, j’ai ajouté une classe d’exemple qui fait un POST http des informations ainsi que la page aspx qui valide les informations auprès de recaptcha.

#    Comments [0] |
# Friday, October 03, 2008

DeepZoom facile

J’ai rajouté dans le projet SLExtensions une librairie permattant de gérer facilement vos galleries DeepZoom. Il rajoute grâce aux méthodes d’extensions des fonctions utiles comme le positionnement automatique de votre collection, la gestion de la roulette de la souris, le glisser du deepzoom, le zoom sur une image de la collection, etc…

<UserControl x:Class="SLExtensions.Showcase.PageDeepZoom"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:sledz="clr-namespace:SLExtensions.DeepZoom;assembly=SLExtensions.DeepZoom"

   >

    <Grid x:Name="LayoutRoot" Background="White">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition/>

        </Grid.RowDefinitions>

 

        <StackPanel Orientation="Horizontal">

            <Button Content="Next" Click="Next_Click"/>

            <Button Content="Previous" Click="Previous_Click"/>

            <Button Content="All" Click="All_Click"/>

        </StackPanel>

 

        <MultiScaleImage x:Name="msi" Source="http://labs.ucaya.com/dznantes/dzc_output.xml"

                        Grid.Row="1"

                        sledz:DZExtensions.ArrangeOnFirstMotionFinished="True"

                        sledz:DZExtensions.IsMousePanEnabled="True"

                        sledz:DZExtensions.IsMouseWheelEnabled="True"/>

    </Grid>

</UserControl>

L’activation de la gestion de la souris ainsi que le positionnement automatique des images se fait par des AttachedProperty à déclarer sur le contrôle MultiScaleImage.
Dans cet exemple, j’ai rajouté 3 bouttons permettant de faire un simple slideshow des images deepzoom. Ils exécutent des fontions d’extensions disponibles dans le namespace SLExtensions.DeepZoom.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using SLExtensions.DeepZoom;

 

namespace SLExtensions.Showcase

{

    public partial class PageDeepZoom : UserControl

    {

        public PageDeepZoom()

        {

            InitializeComponent();

        }

 

        private int collectionIndex = -1;

 

        private void Next_Click(object sender, RoutedEventArgs e)

        {

            DZContext context = msi.EnsureContext();

            collectionIndex = (collectionIndex + 1) % context.ImagesToShow.Count;

            msi.ZoomFullAndCenterImage(collectionIndex);

        }

 

        private void Previous_Click(object sender, RoutedEventArgs e)

        {

            DZContext context = msi.EnsureContext();

            collectionIndex = (collectionIndex + context.ImagesToShow.Count - 1) % context.ImagesToShow.Count;

            msi.ZoomFullAndCenterImage(collectionIndex);

        }

 

        private void All_Click(object sender, RoutedEventArgs e)

        {

            msi.ZoomCenter();

        }

    }

}

#    Comments [1] |
# Saturday, August 16, 2008

Animations AgTweener vs Storyboard

Ces deux manières d'animer ont un comportement différent lorsque l'application silverlight effectue des traitements qui font baisser le taux d'images par seconde.
Petite comparaison du mode de fonctionnement.

Fonctionnement du storyboard

Les storyboards sont basés sur le temps et adaptent les valeurs des animations en fonction de la progression de l'animation.  Si le temps d'assignation de la valeur ou le traitement induit (binding par exemple) est supérieur au frame rate, le storyboard va sauter les frames intermédiaires.

Fonctionnement d'AgTweener

AgTweener utilise un système de "timer" pour animer les objets. L'implémentation aurait pu avoir le même comportement que les storyboard, mais les concepteurs en ont décidé autrement. Le timer interne est basé sur un storyboard de 20ms qui est redémarré chaque fois que l'assignation des valeurs est terminé. Le timing est effectué en comptant le nombre de frame. Même si l'application effectue un traitement coûteux lors de l'assignation de la valeur, on est sur d'avoir une animation de la valeur, au dépend du respect du timing de l'animation.

Conclusion

Lorsqu'une application effectue des traitement coûteux, une animation faite par AgTweener peut donner une impression de fluidité comparé à une animation par storyboard qui va sauter des frames. Si le timing de l'application est important, il vaut mieux alors utiliser des storyboards.

#    Comments [0] |
# Friday, July 11, 2008

Silverlight FlowLayout animé

J’ai porté un exemple du Kevin's WPF Bag-o-Tricks dans SLExtensions.
Voici un petit exemple d’utilisation.

        <ItemsControl Grid.Row="1" ItemsSource="azertyuiopsdfghjklazertyuiopsdfghjklazertyuiopsdfghjklazertyuiopsdfghjklazertyuiopsdfgh” >

            <ItemsControl.ItemsPanel>

                <ItemsPanelTemplate>

                    <slec:AnimatingTilePanel />

                ItemsPanelTemplate>

            ItemsControl.ItemsPanel>

        ItemsControl>

div>
#    Comments [0] |

Silverlight DockPanel

SLExtensions ajoute maintenant un DockPanel à la liste de ses contrôles.

        <slec:DockPanel Grid.Row="1" LastChildFill="True">

            <Button Content="Top" slec:DockPanel.Dock="Top" Height="60" />

            <Button Content="Left" slec:DockPanel.Dock="Left" Width="60" />

            <Button Content="Right" slec:DockPanel.Dock="Right" Width="60" />

            <Button Content="Bottom" slec:DockPanel.Dock="Bottom" Height="60" />

            <Button Content="Fill" />

        slec:DockPanel>

div>
#    Comments [0] |

France Télévision

playerinfo France Télévision sort son “player info”. Il permet de revoir les journaux de toutes les chaines du groupe France Télévision. Vous pourrez maintenant regarder le journal de votre région même si vous êtes en déplacement à l’autre bout du monde. Ucaya a participé à la réalisation de ce player. Il est développé en Silverlight 2 et utilise la librairie SLExtensions.

#    Comments [0] |
# Saturday, July 05, 2008

MVP isé

Me voici élevé au rang de MVP Client App Dev. J'ai reçu hier mon petit package MVP en provenance des USs. Je vous fais profiter de son contenu. En plus du diplôme, j'ai reçu un set bluetooth contenant une paire d'écouteurs, un dongle et une souris MoGo.

Je suis très fier de faire parti des 173 MVPs français actifs de 2008 et je remercie tous ceux qui m'ont permis de le devenir.

PIC-0024 PIC-0025 PIC-0026

#    Comments [0] |
# Sunday, June 29, 2008

Animer des propriétés non animables par Storyboard

Le moteur d'animation de silverlight ne permet d'animer que certains types de propriétés : des couleurs, des doubles et des points. Il n'est a priori pas simple d'animer une propriété comme Margin. En rusant un peu, on se rend compte qu'il est tout à fait possible de le faire. L'idée est de passer par une classe intermédiaire qui possède une propriété simple et de faire la conversion vers le type Thickness utilisé pour représenté une Margin. Afin de facilité l'utilisation de cette classe depuis le Xaml, elle hérite de Panel et peut donc être posée dans n'importe quel conteneur. Voici donc un petit exemple que vous retrouverez bien sur dans le projet SLExtensions.

La classe wrapper

namespace SLExtensions.Controls.Animation

{

    ...

 

    public class MarginWrapper : Panel

    {

        public MarginWrapper()

        {

            // Hide this control. It's just a wrapper and does not need to be shown in the GUI

            this.Visibility = Visibility.Collapsed;

            this.Loaded += new RoutedEventHandler(MarginWrapper_Loaded);

        }

 

        void MarginWrapper_Loaded(object sender, RoutedEventArgs e)

        {

            EnsureElement();

        }

 

        public string ElementName { get; set; }

 

        private FrameworkElement element;

 

        private void EnsureElement()

        {

            if (element == null)

            {

                element = this.FindName(ElementName) as FrameworkElement;

                if (element != null)

                {

                    Thickness margin = element.Margin;

                    if (margin != null)

                    {

                        MarginLeft = margin.Left;

                        MarginTop = margin.Top;

                        MarginRight = margin.Right;

                        MarginBottom = margin.Bottom;

                    }

                }

            }

        }

 

        #region MarginLeft

 

        public double MarginLeft

        {

            get

            {

                return (double)GetValue(MarginLeftProperty);

            }

 

            set

            {

                SetValue(MarginLeftProperty, value);

            }

        }

 

        /// <summary>

        /// MarginLeft depedency property.

        /// </summary>

        public static readonly DependencyProperty MarginLeftProperty =

            DependencyProperty.Register(

                "MarginLeft",

                typeof(double),

                typeof(MarginWrapper),

                new PropertyMetadata((d, e) => ((MarginWrapper)d).OnMarginLeftChanged((double)e.OldValue, (double)e.NewValue)));

 

        /// <summary>

        /// handles the MarginLeftProperty changes.

        /// </summary>

        /// <param name="oldValue">The old value.</param>

        /// <param name="newValue">The new value.</param>

        private void OnMarginLeftChanged(double oldValue, double newValue)

        {

            EnsureElement();

            if (element != null)

            {

                Thickness margin = element.Margin;

                if (margin == null)

                    margin = new Thickness();

 

                margin.Left = newValue;

                element.Margin = margin;

            }

        }

 

        #endregion MarginLeft

 

...

 

    }

}

Un exemple de Xaml

<UserControl x:Class="SLExtensions.Showcase.PageAnimation"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:anim="clr-namespace:SLExtensions.Controls.Animation;assembly=SLExtensions.Controls"

   xmlns:slei="clr-namespace:SLExtensions.Input;assembly=SLExtensions"

   >

    <UserControl.Resources>

        <Storyboard x:Name="anim1">

            <DoubleAnimation Storyboard.TargetName="marginWrapper1" Storyboard.TargetProperty="MarginLeft" To="150" Duration="0:00:03"/>

        </Storyboard>

    </UserControl.Resources>

    <StackPanel x:Name="LayoutRoot" Background="White">

        <StackPanel Orientation="Horizontal">

            <Button Content="Start" slei:CommandService.Command="BeginStoryboard" slei:CommandService.CommandParameter="anim1" />

            <Button Content="Stop" slei:CommandService.Command="StopStoryboard" slei:CommandService.CommandParameter="anim1"/>

                <Grid Width="400" Height="100">

                    <anim:MarginWrapper x:Name="marginWrapper1" ElementName="rect1" />

                    <Rectangle x:Name="rect1" Fill="Red" Width="50" Height="50" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,0,0,0" />

                </Grid>

            </StackPanel>

    </StackPanel>

</UserControl>

#    Comments [0] |
# Saturday, June 28, 2008

Silverlight Extensions

Vous trouverez les exemples de code que je poste sur ce blog dans un projet nommé SLExtensions qui est disponible sur CodePlex. En plus des exemples de codes, vous trouverez quelques contrôles silverlight comme un TreeView ou un StackPanel virtualisé. Un mini showcase est visible ici : http://labs.ucaya.com/slextensions

#    Comments [0] |

Envoyer un formulaire html en POST avec Silverlight

Je vous propose une méthode d'extension simple qui permet d'envoyer un formulaire vers une page html. Le beta 2 de silverlight nous simplifie la vie avec la fonction UploadStringAsync. Il ne reste plus que l'encodage des données et le content type à assigner.

Voici la méthode d'extension

    /// <summary>

    /// Method extensions for SLExtension framework

    /// </summary>

    public static class SLExtension

    {

 

....

 

        #region WebClient

        /// <summary>

        /// Sends an HTML form. The request is sent using POST method

        /// </summary>

        /// <param name="webclient">The webclient.</param>

        /// <param name="uri">The URI of the resource to receive the file. </param>

        /// <param name="data">The form data to upload.</param>

        public static void SendHtmlForm(this WebClient webclient, Uri uri, IEnumerable<KeyValuePair<string, string>> data)

        {

            StringBuilder dataBuilder = new StringBuilder();

            int cnt = 0;

            foreach (var item in data)

            {

                if (cnt > 0)

                {

                    dataBuilder.Append('&');

                }

 

                dataBuilder.Append(item.Key);

                dataBuilder.Append('=');

                dataBuilder.Append(HttpUtility.UrlEncode(item.Value));

            }

 

            webclient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";

            webclient.UploadStringAsync(uri, dataBuilder.ToString());

        }

 

        /// <summary>

        /// Sends an HTML form. The request is sent using POST method

        /// </summary>

        /// <param name="webclient">The webclient.</param>

        /// <param name="uri">The URI of the resource to receive the file. </param>

        /// <param name="data">The form data to upload.</param>

        /// <param name="userToken">user state</param>

        public static void SendHtmlForm(this WebClient webclient, Uri uri, IEnumerable<KeyValuePair<string, string>> data, object userToken)

        {

            StringBuilder dataBuilder = new StringBuilder();

            int cnt = 0;

            foreach (var item in data)

            {

                if (cnt > 0)

                {

                    dataBuilder.Append('&');

                }

 

                dataBuilder.Append(item.Key);

                dataBuilder.Append('=');

                dataBuilder.Append(HttpUtility.UrlEncode(item.Value));

            }

 

            webclient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";

            webclient.UploadStringAsync(uri, "POST", dataBuilder.ToString(), userToken);

        }

        #endregion WebClient

        #endregion Public Methods

    }

#    Comments [0] |

Unload des contrôles en Silverlight

Il est parfois nécessaire d'être notifié lorsqu'un contrôle n'est plus dans l'arbre graphique de silverlight. Il n'existe pas à l'heure actuelle (c'est à dire dans la beta 2) d'événement unload, cependant, l'événement LayoutUpdated est levé sur les contrôles lorsqu'ils sont enlevés de leur parent. Voici un petit exemple de code. Notez au passage l'utilisation des méthodes d'extension.

namespace SLExtensions

{

    using System;

    using System.Text;

    using System.Windows;

    using System.Windows.Browser;

    using System.Windows.Controls;

    using System.Windows.Documents;

    using System.Windows.Ink;

    using System.Windows.Input;

    using System.Windows.Interop;

    using System.Windows.Media;

    using System.Windows.Media.Animation;

    using System.Windows.Shapes;

    using System.Collections.Generic;

 

    /// <summary>

    /// Method extensions for SLExtension framework

    /// </summary>

    public static class SLExtension

    {

       ...

 

        /// <summary>

        /// Determines whether an element is in the visual tree of the current application.

        /// </summary>

        /// <param name="element">The element.</param>

        /// <returns>

        ///    <c>true</c> if element paramter is in visual tree otherwise, <c>false</c>.

        /// </returns>

        public static bool IsInVisualTree(this FrameworkElement element)

        {

            return IsInVisualTree(element, Application.Current.RootVisual as FrameworkElement);

        }

 

        /// <summary>

        /// Determines whether an element is in the visual tree of a given ancestor.

        /// </summary>

        /// <param name="element">The element.</param>

        /// <param name="ancestor">The ancestor.</param>

        /// <returns>

        ///    <c>true</c> if element paramter is in visual tree otherwise, <c>false</c>.

        /// </returns>

        public static bool IsInVisualTree(this FrameworkElement element, FrameworkElement ancestor)

        {

            FrameworkElement fe = element;

            while (fe != null)

            {

                if (fe == ancestor)

                    return true;

                fe = fe.Parent as FrameworkElement;

            }

            return false;

        }

 

       ...

    }

}

 

<UserControl x:Class="SLExtensions.Showcase.IsInVisualTree"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">

        <Rectangle x:Name="rect" Fill="Cornsilk" LayoutUpdated="rect_LayoutUpdated" />       

        <Button Content="Remove rect" Height="40" VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="100" Click="Button_Click" />

    </Grid>

</UserControl>

 

using SLExtensions;

 

namespace SLExtensions.Showcase

{

    public partial class IsInVisualTree : UserControl

    {

        public IsInVisualTree()

        {

            InitializeComponent();

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            LayoutRoot.Children.Remove(rect);

        }

 

        private void rect_LayoutUpdated(object sender, EventArgs e)

        {

            if (!rect.IsInVisualTree())

            {

                // Handle unload behavior

            }

        }

    }

}

xcvcx

#    Comments [0] |
# Tuesday, May 27, 2008

Silverlight Command pattern et AttachedProperty

Les AttachedProperty

Ce type de propriété permet d'enrichir les propriétés existantes d'un objet. Ce sont des propriétés qui n'ont pas forcément de sens au niveau du contrôle lui-même, mais qui en ont au vu d'un contexte externe. Les AttachedProperty les plus connues sont Canvas.Left, Canvas.Top, Grid.Column et Grid.Row. Vous remarquerez que la propriété colonne n'a aucun sens sur un contrôle en dehors du contexte de la grille.

Ces AttachedProperty nous permettent d'étendre le comportement de nos contrôles. Nikhilk nous donne un exemple d'implémentation d'un framework de comportement sur son blog.

Le Command pattern

Ce pattern a pour but de séparer le comportement de la partie IHM. L'idée est de ne plus s'attacher directement aux événements des contrôles et de passer par un objet Command qui va faire la liaison entre le comportement et la saisie des utilisateurs.
Vous trouverez une bonne description du pattern Command en WPF sur le site de microsoft. Cette séparation permet de pouvoir déclencher l'action depuis l'IHM mais aussi depuis d'autres classes métiers.

Le code

La classe centrale est CommandService. Elle défini les AttachedProperty, c'est elle qui devra être référencée dans le xaml pour relier un contrôle à une commande.

<UserControl  xmlns="http://schemas.microsoft.com/client/2007"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:sle="clr-namespace:SLExtensions.Input;assembly=SLExtensions"

...

        <Button Content="Open" Width="50" Height="30"

                      sle:CommandService.Command="Open"

                      sle:CommandService.CommandParameter="{Binding OpenFileInfo, Source={StaticResource controller}}" />

 

...

</UserControl>

Dans cet exemple, le bouton "Open" est bindé à la commande Open. On passe en paramètre à la commande un object OpenFileInfo qui est stocké sur le contrôleur de la page. Il sert à passer les paramètres d'exécution de la commande.

Du coté métier, le contrôleur de la page s'attache à l'événement Executed de la commande

...

 TestCommands.OpenCommand.Executed += new ExecutedEventHandler(OpenCommand_Executed);

...

void OpenCommand_Executed(object sender, ExecutedEventArgs e)

{

    OpenFileDialogInfo info = e.Parameter as OpenFileDialogInfo;

    if (info != null)

    {

        using (OpenFileDialog ofd = new OpenFileDialog())

        {

            ofd.EnableMultipleSelection = info.EnableMultipleSelection;

            ofd.Filter = info.Filter;

            ofd.ShowDialog();

        }

    }

}

 

Téléchargements Sources

#    Comments [0] |
# Monday, May 26, 2008

Copy source as html VS 2008

J'ai cherché durant le weekend un moyen satisfaisant de poster du code sur mon blog sans perdre le formatage. J'ai fais plusieurs essais plus ou moins réussis. Mon choix s'est arrêté sur CopySourceAsHtml. Il a bien sur fallu que je trouve une version compatible avec Visual Studio 2008 contenant ce patch corrigeant un problème d'accès au presse-papier. Finalement, je suis reparti des sources. Je publie donc ici les sources modifiées ainsi que les binaires à mettre dans

Voici un exemple de ce que ça peut donner "%userprofile%\Documents\Visual Studio 2008\Addins"

namespace SilverlightApplication5

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

        }

    }

}

Téléchargements :

#    Comments [0] |
# Sunday, May 25, 2008

Le data binding en Silverlight

Le data binding est une des fonctionnalités les plus puissantes de Silverlight et WPF. Il n'est plus nécessaire de passer du temps à faire du code pour la synchronisation des données. Cela contribue à la séparation de la partie visuelle de la partie métier.

Fonctionnement

En beta 1, le data binding est implémenté sur tout les objets qui héritent de FrameworkElement. Les propriétés de ce contrôle peuvent être liées à un objet source spécifiée lors du binding ou être liées au contexte de binding en cours. Le binding en cours est stockée par la propriété DataContext.

  • Exemple 1:

    1 <UserControl x:Class="Ucaya.SilverlightTest.Page"

    2    xmlns="http://schemas.microsoft.com/client/2007"

    3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4    xmlns:sys="clr-namespace:System;assembly=mscorlib"

    5    Width="400" Height="300">

    6     <UserControl.Resources>

    7         <sys:String x:Name="src">Binding source</sys:String>

    8     </UserControl.Resources>

    9     <StackPanel x:Name="LayoutRoot" Background="White">   

   10         <TextBlock x:Name="tb1" Text="{Binding Source={StaticResource src}}"/>

   11         <TextBlock x:Name="tb2" Text="{Binding Length, Source={StaticResource src}}"/>

   12     </StackPanel>

   13 </UserControl>

Ici, le TextBlock tb1 a sa propriété Text bindée à l'objet src qui se trouve dans les ressources. N'ayant pas spécifié de chemin dans la binding, c'est l'objet lui même qui est lié.
Le TextBlock tb2 est lié à la propriété Length de la source.

  • Exemple 2:

    1 <UserControl x:Class="Ucaya.SilverlightTest.Page"

    2    xmlns="http://schemas.microsoft.com/client/2007"

    3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4    xmlns:sys="clr-namespace:System;assembly=mscorlib"

    5    Width="400" Height="300">

    6     <UserControl.Resources>

    7         <sys:String x:Name="src">Binding source</sys:String>

    8     </UserControl.Resources>

    9     <Grid x:Name="LayoutRoot" Background="White">

   10         <ListBox x:Name="lb" ItemsSource="{Binding Source={StaticResource src}}">

   11             <ListBox.ItemTemplate>

   12                 <DataTemplate>

   13                     <TextBlock x:Name="tb" Text="{Binding }"/>

   14                 </DataTemplate>

   15             </ListBox.ItemTemplate>

   16         </ListBox>       

   17     </Grid>

   18 </UserControl>

La ListBox est bindée de la même manière que dans l'exemple 1. La string étant énumérable, la ListBox instancie un ItemTemplate par valeur de l'énumération.
Le TextBlock à sa propriété Text bindée au contexte de binding en cours fourni par la ListBox. Comme dans l'exemple 1, n'ayant pas fourni de chemin de binding, c'est directement la source du binding qui est liée.

Modes de binding

La synchronisation des valeurs peut se faire de différente manière.

OneTime

La donnée est recopiée de la source vers le contrôle une seule fois lorsque le binding est créé.

OneWay

La donnée est mise à jour dans le sens source vers contrôle à chaque fois que la valeur de la source change. Pour que le binding soit notifié du changement, l'objet source du binding doit implémenter l'interface INotifyPropertyChanged pour les propriétés et INotifyCollectionChanged pour les collections.

TwoWay

La donnée est mise à jour dans le sens source vers contrôle à chaque fois que la valeur de la source change (sous réserve de l'implémentation de INotifyPropertyChanged ou INotifyCollectionChanged) et dans le sens contrôle vers source quand la valeur du contrôle change.

Conversion des données

Il arrive que les données sources ne puissent être converties directement entre les propriétés d'un contrôle et les valeurs de la source. C'est là qu'interviennent les classes de conversion. Elles permettent par exemple de convertir un string en Uri, un string en ImageSource ou encore de formatter un double en string avec 2 nombres après la virgule. Ces convertisseurs sont des classes qui implémentent l'interface IValueConverter.

  • Exemple

Implémentation d'un convertisseur effectuant un string.Format. Affiche une valeur en pourcentage.

Xaml:

 

<UserControl x:Class="Ucaya.SilverlightTest.Page"

   xmlns="http://schemas.microsoft.com/client/2007"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:sys="clr-namespace:System;assembly=mscorlib"

   xmlns:sle="clr-namespace:SLExtensions.Data;assembly=SLExtensions"

   Width="400" Height="300">

    <UserControl.Resources>

        <sys:Double x:Key="src">0.333333</sys:Double>

        <sle:StringFormatConverter x:Key="formater"/>

        <sys:String x:Key="formatString">{0:p}</sys:String>

    </UserControl.Resources>

    <StackPanel x:Name="LayoutRoot" Background="White">   

        <TextBlock x:Name="tb1"

                  Text="{Binding Converter={StaticResource formater}, ConverterParameter={StaticResource formatString}, Source={StaticResource src}}"/>

    </StackPanel>

</UserControl>

Convertisseur:

// <copyright file="StringFormatConverter.cs" company="SLExtensions.Controls">

// Distributed under Microsoft Public License (Ms-PL)

// </copyright>

 

namespace SLExtensions.Data

{

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Documents;

    using System.Windows.Ink;

    using System.Windows.Input;

    using System.Windows.Media;

    using System.Windows.Media.Animation;

    using System.Windows.Shapes;

    using System.Windows.Data;

 

    /// <summary>

    /// Convert data to string objects as it passes through the binding engine.

    /// </summary>

    public class StringFormatConverter : IValueConverter

    {

        /// <summary>

        /// Default string format syntax

        /// </summary>

        private const string DefaultStringFormat = "{0}";

 

        #region IValueConverter Members

 

        /// <summary>

        /// Convert source data to string before passing it to the target for display in the UI.

        /// </summary>

        /// <param name="value">The source data being passed to the target.</param>

        /// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the target dependency property.</param>

        /// <param name="parameter">An optional parameter to be used in the converter logic.</param>

        /// <param name="culture">The culture of the conversion.</param>

        /// <returns>

        /// The value to be passed to the target dependency property.

        /// </returns>

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            if (targetType != typeof(string))

            {

                throw new ArgumentException(Resource.StringFormatConverterExceptionInvalidTargetType);

            }

 

            if (value == null)

            {

                return string.Empty;

            }

 

            string format = (parameter as string) ?? DefaultStringFormat;

            return string.Format(format, value);

        }

 

        /// <summary>

        /// This convertion is not supported

        /// </summary>

        /// <param name="value">The target data being passed to the source.</param>

        /// <param name="targetType">The <see cref="T:System.Type"/> of data expected by the source object.</param>

        /// <param name="parameter">An optional parameter to be used in the converter logic.</param>

        /// <param name="culture">The culture of the conversion.</param>

        /// <returns>

        /// The value to be passed to the source object.

        /// </returns>

        object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new NotSupportedException();

        }

 

        #endregion

    }

}

#    Comments [0] |
# Wednesday, April 30, 2008

Une RichTextBox en Silverlight

Michael Sync a publié sur son blog et sur CodePlex un Rich Text Editor en silverlight. Je ne l'ai pas encore testé, mais cela semble très prometteur.

rte

#    Comments [0] |

Conditions d'utilisation du serveur de tuiles VirtualEarth

Dans un billet précédent, j'ai présenté un module permettant d'afficher des cartes VirtualEarth en Silverlight. Ce module requête directement le serveur de tuile chez Microsoft sans utiliser le sdk javascript fourni par MS et du coup ne respecte pas la licence d'utilisation de VirtualEarth. Ce matin, un billet sur le blog virtual earth explique comment légaliser cet accès. Il suffit d'insérer un identifiant reçu par webservice lors de l'appel des tuiles.
Vous trouverez donc bientôt une nouvelle version de mon module tenant compte de cet identifiant.

#    Comments [0] |

iSwish sur Windows Mobile un iPhone killer ?

iSwish est un projet de FlickSoftware visant à porter une partie de l'interface iPhone sur Windows Mobile. Bien que la "carte graphique" de l'iPhone soit largement plus puissante que ce qu'on peut trouver sur les machines tournant sous Windows Mobile, le résultat est très fluide.

Source : http://www.engadget.com/2008/04/30/video-iswish-puts-the-iphone-ui-on-any-windows-mobile-phone/

#    Comments [0] |
# Wednesday, April 23, 2008

Cartographie en silverlight

layerhostJe poste sur le labs un début de contrôle de gestion de calques. Le but de ce contrôle est de pouvoir afficher des calques, zoomer, se déplacer dedans, afficher dynamiquement des données sur les nouvelles zones visibles. Ça ressemble furieusement à de la cartographie tout ça. C'est pourquoi les premiers calques que j'ai développés sont des calques Googlemap et Virtual Earth. Les premiers tests sont visibles ici : http://labs.ucaya.com/layerhost/
Ce contrôle devrait bientôt rejoindre une librairie qui sera téléchargeable sur CodePlex

#    Comments [0] |
# Wednesday, April 02, 2008

Ucaya, 1 an déjà

Cela fait 1 an aujourd'hui qu'Ucaya a démarré son activité. Nous voyons se dessiner l'avenir sereinement. Si les débuts ont consisté essentiellement en de la prestation de service, nous commençons à développer nos futurs produits en interne. Je ne vous dis pas pour l'instant ce sur quoi nous travaillons, mais je peux déjà vous dire que ça sera en silverlight. Nous avons réalisé beaucoup de tests et projets en interne et nous croyons fortement dans cette technologie.

Petit bilan de l'année écoulée.

Les projets publics

  • XamlTune Au début d'Ucaya, n'ayant pas encore de licence Microsoft, nous développions avec les outils gratuits disponibles sur le marché. Pour faire du Xaml, nous avions besoin de convertir des fichiers Illustrator et SVG en Xaml. Inkscape nous permettait de retoucher les fichiers au format vectoriel et XamlTune nous les convertissait.
    Ce projet est maintenant hébergé sur CodePlex.
  • NHib.Wizard La partie d'accès aux données de nos projets utilise essentiellement NHibernate. Le coût d'entrée à NHibernate est assez élévé. Apprentissage du fichier de mapping, maintenance du schema de la base de données, maintenance des classes métier.
    Ce petit wizard permet de convertir des diagrammes de classes UML XMI en classes métier, fichier de mapping NHibernate et de générer le script de la base de données. Un bon gain de temps pour nos projets.
    Les sources sont disponibles sur la page produit

D'autres projets existent mais ne sont pas montrable en l'état. Dès que je trouve le temps de les remettre d'aplomb je les publie sur le site.

Comme je vous disais plus haut, nous avons beaucoup travaillé avec des logiciels gratuits. Voici la liste des outils que vous pouvez trouver sur mon poste de développement.

#    Comments [2] |

Identification InfoCard (CardSpace) avec Silverlight2

cardspace Voila la conversion du sample js en Silverlight. L'appel à la fonction GetToken lance l'interface InfoCard et retourne le xml d'identification.

InfoCardSample.zip (19,32 KB)
#    Comments [0] |

Animer du HTML avec Silverlight 2

Silverlight possède un système d'animation très poussé. L'idée de ce sample est d'utiliser les storyboards pour agir sur des éléments du DOM.
Le principe est le même que pour le "game loop" d'un jeu. Tous les x millisecondes, on va agir sur les éléments à animer. On récupère la position et la taille d'un élément silverlight qui va servir de modèle et on les applique à l'élément du DOM.

Le projet :

La classe HtmlAnimation est en charge du "game loop". Elle possède une collection de HtmlAnimationPair. Les HtmlAnimationPair font la liaison entre l'élément silverlight et l'élément du DOM.

Site web de test

HtmlAnimation.zip (13,68 KB)
#    Comments [2] |
# Tuesday, March 04, 2008

Javascript benchmark

Après avoir lu sur je ne sais plus quel blog que les dernières versions de firefox 3 (pre beta4) ont encore fait des progrès, j'ai voulu m'en rendre compte moi même. Voici les résultats réalisés avec SunSpider.

Firefox 3 :
Total:                 3369.2ms +/- 2.4%

Opera 9.26.8835
Total:                  8861.2ms +/- 1.5%

IE7
Total: 41311.2ms +/- 5.5%

IE8
Total:                  8131.4ms +/- 1.0%

Safari Windows :
Total:                 8278.6ms +/- 1.0%

Firfox 2.0.0.12 :

Total:                 13912.8ms +/- 0.7%
#    Comments [0] |
# Thursday, February 21, 2008

XamlTune Live

xamltuneA live version of xamltune has been released on our web site. This tool is intensively used by our development team. Svg graphics are converted in Xaml and the result can be optimized in several ways.

XamlTune sources and binaries can be downloaded on the codeplex page.

Live version can be tested in our labs area.

#    Comments [0] |
# Friday, February 08, 2008

XamlTune on CodePlex

XamlTune is now on codeplex. Have fun.
Untitled
#    Comments [2] |
# Wednesday, February 06, 2008

VisioExportToXAML

Saveen Reddy has released his visio export plugin on codeplex. He is using my XamlTune export library.

I've corrected XamlTune to render its last sample.
Untitled

#    Comments [0] |
# Tuesday, February 05, 2008

A Second Attempt at Exporting XAML from Visio 2007

This morning while checking my daily feeds, I saw on Saveen Reddy's blog a post dealing with Visio and Xaml Export. Saveen Reddy uses svg export feature from Visio and Andrej Benedik's library for converting svg to Xaml. The result is good for common shapes but gradients are not handled properly.
Some times ago, I developped a svg to xaml converter based on SharpVector library. I mainly use this tool for working on silverlight projects. It reads svg input convert it to xaml and can manipulate xaml objects. (Replace rectangle geometries with Rectangle, set canvas as a bounding box of inner elements, remove default values,...).
After reading this post, I've just plug my convert library and here are the results :

XamlTune Export2

VisioExportToXaml-(2008-02-04)1.zip (325,29 KB)

Edit: Empty element names are now ignored. Thanks Saveen.

#    Comments [2] |
# Friday, February 01, 2008

Souris gonflable

Vous ne savez pas où ranger votre souris quand vous êtes en voyage avec votre portable ? "Jelly clic" est pour vous. C'est une souris ultra plate gonflable.
Plus d'infos ici.

1-31-08-jelly_click

#    Comments [0] |
# Thursday, January 31, 2008

Le blog de le loose

Voici un blog qui recense une belle collection de foirages.

shipment

#    Comments [0] |
# Tuesday, January 29, 2008

Convertir vos photos en 3D

Un projet de l'université de stanford permet de convertir une photo en objets en 3D. Je ne vois pas encore l'utilité de ce genre d'application, mais il faut bien occuper les étudiants ;)

http://make3d.stanford.edu/

#    Comments [0] |
# Friday, January 25, 2008

Quake 3 en .Net

quake341_thumb Il y a des gens qui doivent s'ennuyer la journée (et la nuit aussi ...). Greg Dolley a porté le code de Quake 3 sur la plateforme .Net.

Un beau challenge. Plus de 28000 erreurs de compilation à corriger...

#    Comments [0] |
# Monday, January 21, 2008

Nouveau concept de téléphone

ledcellphoneVoici un nouveau téléphone conçu pour être peu gourmand en énergie et pas trop cher. La surface du téléphone est constituée de centaines de LEDs qui s'illuminent en fonction des modes du téléphone, constituant ainsi le pavé numérique.

source : Physorg.com

#    Comments [0] |

Un peu de Pub

Dur d'être créatif dans la publicité. On a déjà vu tellement de choses... Je trouve les images qui suivent super bien réalisées !

clip_image001_2 clip_image002_2 clip_image003_2 clip_image004_2 clip_image005_2 clip_image006_2 clip_image007_2 clip_image009_2 clip_image010_2 clip_image011_2 clip_image012_2 clip_image013_2 clip_image014_2 clip_image015_2 clip_image016_2 clip_image017_2

#    Comments [1] |
# Thursday, January 17, 2008

Pas à pas dans le code du framework .Net

Il y a quelques temps maintenant, Microsoft annonçait la possibilité de faire du pas à pas dans le code du framework .Net. Cette fonctionnalité est maintenant active. Voici un article explicant comment configurer VS2008 pour télécharger les pdb sur le serveur de source Microsoft. Attention, cette fonctionnalité n'est pas disponible dans la gamme Visual Studio Express.

http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

#    Comments [0] |
# Wednesday, January 16, 2008

Illusion d'optique - Pub


Tout est dans le titre !
#    Comments [0] |
# Monday, January 14, 2008

Produit d'avenir ?

La société Cynergy Labs regarde trop Minority Report. Ils ont utilisé la wii-mote pour et une paire de gants pour controler une application qui ressemble à Surface.
Ca ouvre pas mal de possibilités pour de nouvelles applications !

Une video plus longue : http://labs.cynergysystems.com/Silverlight.html

#    Comments [1] |
# Wednesday, December 19, 2007

Tafiti sous licence MS-PL

tafiti Tafiti est un des premiers projets Silverlight apparut sur le net. C'est une interface graphique plugguée au moteur de recherche live.com.

Ce projet fait maintenant parti des Windows Live Quick Application. Ce sont des petits exemples concrets qui expliquent comment utiliser la plateforme windows live.

Tafiti_Search_Visualization

#    Comments [0] |
# Saturday, December 08, 2007

Convertion Svg vers Xaml

XamlTune Si la plateforme .Net supportant le Xaml est gratuite, il n'en est pas de même des outils pour développer pour cette plateforme. Je ne parle pas ici du code, mais de quoi faire du desin Xaml. Pour transformer du svg en xaml, il n'existe que peu de logiciels.
WPF-Graphics - Paste2Xaml is a clipboard or metafiles into xaml
et Adobe Illustrator to XAML Export à ma connaissance. Je vous propose ici un petit utilitaire libre de droits pour générer faire ce travail. Il est basé sur SharpVector pour lire le svg et génère du xaml pour wpf ou silverlight. Je mets au passage le lien vers InkScape qui permet de travailler les fichiers svg.

Mise à jour 2007/12/13: Corrections sur les dégradés radiaux. La case à cocher "Canvas as Path Bounding box" reste encore buggué.

Mise à jour 2007/12/15: Ajout de textbox de visualisation et d'édition du svg/xaml. Corrections de bugs. Ajout d'un treeview de visualisation des éléments xaml.

Télécharger

#    Comments [0] |
# Friday, December 07, 2007

Blend 2 December Preview et Blend sp1

boxShot_Blend Une nouvelle CTP de Blend 2 est disponible sur le site de Microsoft. Quelques corrections de bugs et le support des solutions VS2008 RTM.

Téléchargement : Blend 2
Blend sp1

#    Comments [0] |
# Thursday, November 29, 2007

Silverlight 2.0

La liste des fonctionnalités de Silverlight 2.0 (précédemment Silverlight 1.1) vient d' être annoncée de manière non officielle sur plusieurs blogs. Silverlight se rapproche de plus en plus de WPF. (Databinding, Contrôles, Layout, ...) Devant toutes ces fonctionnalités, Microsoft a renommé Silverlight 1.1 en Silverlight 2.0

source : http://blogs.msdn.com/chabrook/archive/2007/11/29/silverlight-announcements-2-0-expected-to-go-beta-in-q1.aspx

#    Comments [0] |

Microsoft Download Center - Silverlight

UntitledMicrosoft refait son site de téléchargement en silverlight. Il n'est pas encore en production, mais vous pouvez le tester ici.

#    Comments [0] |
# Tuesday, November 27, 2007

Facebook

Facebook est le nouveau site communautaire à la mode du moment. Après LinkedIn, viadeo, MySpace, etc... Je dois avouer qu'à moins de passer toute la journée sur ce genre de site pour raconter sa vie je n'en vois pas trop l'intéret. Mais avant de critiquer plus, je vais essayer de voir ce que ca peu apporter. Voici donc mes différents comptes si vous voulez rentrer en contact avec moi.


images

#    Comments [0] |

Silverlight 1.1 Tools Alpha pour VS2008

Je n'ai pas fais de post à ce sujet, mais vous aurez remarqué que visual studio 2008 (aka VS2008 ou orcas) est sorti récemment. Le petit addin permettant de coder en silverlight 1.1 et de debugger ce code n'était pas compatible avec la version finale. C'est maintenant chose corrigée.

source: http://weblogs.asp.net/scottgu/archive/2007/11/26/silverlight-1-1-tools-alpha-for-visual-studio-2008-available-for-download.aspx
download: microsoft

#    Comments [0] |
# Monday, November 19, 2007

Premier post venu d'ailleurs ...

alien

J'ai reçu aujourd'hui mon nouvel outil de travail. Il s'agit d'un petit laptop alienware. Première impression: le look est sympa :). En ce qui concerne les performances, je n'ai pas encore eu le temps de les tester, il faut d'abord réinstaller la tonne de logiciels qui vont biens. Voila le détail de la bête. Lors de la commande, je n'ai pas choisi une carte graphique super poussée vu que la machine n'est destinée qu'à travailler. Le portable est arrivé avec un super tapis de souris Func, un TShirt et un classeur (en cuir? ) pour y ranger les cds d'installs et la doc.

Voici un extrait des specs.

More details about my computer

Processor : Intel(R) Core(TM)2 CPU T7400 @ 2.16GHz : 5,1
Memory (RAM) 2,00 GB : 4,7
Graphics ATI Mobility Radeon X1400 : 3,3
Gaming graphics 129MB Dedicated graphics memory: 3,6
Primary hard disk : 70GB Free (105GB Total) : 5,1

Windows Vista (TM) Ultimate
System 

Manufacturer : ALIENWARE
Model : m5500i-R3
Number of processor cores : 2
64-bit capable : Yes
Primary monitor resolution : 1920x1200

Network Adapter
Realtek RTL8169/8110 Family PCI Gigabit Ethernet NIC (NDIS 6.0)
Intel(R) Wireless WiFi Link 4965AGN

PIC-0011
PIC-0012
PIC-0013
PIC-0014

PIC-0015
PIC-0016
PIC-0019

#    Comments [0] |
# Sunday, November 18, 2007

Visual Studio 2008

Abonnés MSDN, surveillez votre compte, la version 2008 de visual studio devrait sortir cette semaine !

vs2008_Count2_thumb

source : http://blogs.msdn.com/clauer/archive/2007/11/18/visual-studio-2008-serait-propos-en-t-l-chargement-la-semaine-prochaine-pour-les-abonn-s-msdn.aspx

#    Comments [0] |
# Monday, November 12, 2007

Windows Live Writer

writer_screenshotLa version finale de Windows Live Writer est sortie. C'est un bon produit pour blogguer rapidement. L'installeur suporte enfin le x64 ;) et en plus c'est gratuit...

#    Comments [0] |
# Thursday, November 08, 2007

Silverlight physics engine

physics

La gestion des effets physiques dans les jeux m'a toujours impressionné. Voici une démonstration d'un moteur qui tourne sous XNA ou silverlight 1.1. Ce moteur est hébergé sur codeplex.

Les liens :

#    Comments [0] |
# Thursday, October 25, 2007

Silverlight Tour - Nantes

PIC-0010Hier se déroulait la session de Nantes du silverlight tour.
Cette présentation avait pour but d'expliquer les fonctionnalités de la version 1.0 ainsi que de la version 1.1 du framework.

Cette présentation vise un public plutôt novice sur silverlight. Si vous avez déjà bidouillé avec silverlight, la présentation repasse sur la majorité des fonctionnalités.
Le bon points de la présentation sont les conseils sur comment bien mettre en place un projet silverlight, la présentation d'un projet réel (quicksilver) et les pièges à éviter.
A noter la démonstration du Silverlight Control Toolkit qui semble un bon point de départ pour une nouvelle application.

La démonstrations ont présenté la gamme Expression de microsoft et particulièrement deux produits. Expression Blend2 qui est l'atelier de design et d'animation de silverlight et Expression Encoder qui permet de compresser les vidéos pour être utilsables par silverlight.

Comme d'habitude, certaines personnes de l'assistance ont joué les blasés en qualifiant la version 1.0 de coup de pub ou de sans intérêt en attendant la version 1.1. Je ne suis pas de cet avis.

  • Toute la partie de rendu vectorielle va permettre d'intégrer plus facilement les travaux des infographistes.  
  • Cette première version va (comme le soulignait Christophe Lauer) permettre une démocratisation du plugin nécessaire à la visualisation du contenu silverlight.
  • La gestion de la video devient un jeu d'enfant

L'après midi fut très agréable. Je tiens à souligner la qualité des speakers de microsoft. Ils ne sont intéressant et savent maintenir leur auditoire en haleine. Nous sommes repartis avec une version d'evaluation de la gamme Expression

PIC-0008  PIC-0009

#    Comments [0] |

Windows LIve Writer sur plateforme x64

writer_screenshotSuite à mon passage de vista x32 en vista x64, j'ai été obligé de réinstaller mes applications favorites. Pas de surprises pour l'instant, tout marche bien. Ce matin, une envie de blogguer me prend soudain, vite, mon windows live writer. Zut, il n'est pas encore réinstallé. Je me rends de ce pas sur le site de microsoft et là, l'installeur de la gamme livem'empêche tout simplement de continuer en me disant que les produits live ne sont pas compatible x64. Après environ 5 secondes de recherche sur mon moteur de recherche favoris je trouve une parade. Les fichiers de l'installeur live se trouvent hébergés sur SkyDrive.

Voici les liens :

  • Le dossier
  • Windows Live Writer : Install_{1F973A7F-3FE4-4D11-A9A2-E869C2899A7D}.msi
  • Windows Live Messenger : Install_{508CE775-4BA4-4748-82DF-FE28DA9F03B0}.msi
  • Windows Live Sign-In Assistant : Install_{CB5EA99C-8A5B-49F2-9A1A-2EF78BE4DB41}.msi
  • Windows Live Mail : Install_{EDB619FD-4E71-403C-8E99-DFC9CF9DD345}.msi
  • Windows Live Toolbar : Install_{C6876FE6-A314-4628-B0D7-F3EE5E35C4B4}.msi
  • Windows Live Photo Gallery : Install_{AA436FBD-2595-479B-8DDE-E9C36F50D99D}.msi
#    Comments [0] |
# Thursday, October 18, 2007

Début de la grêve

On se sait pas quand ca se terminera...
vaches-sncf

#    Comments [0] |
# Tuesday, October 16, 2007

Réalité augmentée


Petite démonstration sympa. Il faudra que je me penche la dessus un jour, ça a l'air sympa.
#    Comments [0] |
# Sunday, October 14, 2007

Diagrammes de classes

UcaModeler

Dans la sections produits du site se trouve un test de silverlight 1.1. Le but de ce test est de réaliser un éditeur de diagrammes de classes.
Les technologies utilisées ici sont

Ce n'est encore un premier jet mais je pense qu'il est possible d'arriver à quelque chose d'utilisable assez rapidement.

Pour tester, c'est par ici

#    Comments [0] |
# Saturday, October 13, 2007

NHibernate Wizard

nhibwizard
Ce petit soft permet de générer un fichier de mapping NHibernate, le code c# des classes ainsi que le script de création de la base données.

Le fonctionnement: 

  1. Le modèle UML est créé à partir d'argoUML.
    argouml
  2. Le wizard extrait le modèle au format xmi du fichier .zargo
  3. Ce xmi est converti au format xmi 2.1 grâce au script de transformation de nUML
  4. le fichier xmi subit des transformations xslt et donne en sortir le fichier de mapping et  les classes métier en c#
  5. Les classes métier sont "mergées" avec les fichiers issus de précédentes génération afin de pas perdre le code utilisateur. Celui ci doit être placé dans des balises #region
  6. Le wizard termine par générer le script de création de la base de données. Il utilise le fichier de mapping et les classes métier pour générer une assembly temporaire et utilise les possibilités de génération de script de NHibernate (disponible dans svn)

Limitations:

  • La convertion zargo vers XMI 2.1 ne convertie pas les énumérations.
  • Je n'ai pas pu me procurer d'autres modèle XMI 2.1. La compatibilité avec d'autres sources n'est pas garantie.
  • Aucun paramétrage sur le schéma de mapping. Tout est dans le fichier xslt.

Mise à jour :

  • Corrections diverses sur les fichiers de mapping et sur les classes générées.
  • Ajout de fonction Add et Remove pour les listes
  • Si un dialect est spécifié dans le fichier .config, NHibernate génère le script de création de la base de données.
  • Si une connection NHibernate est définie dans le fichier .config, le wizard génère un script de modification de la base de données. (Ne marche que pour PostgreSql)

Téléchargement

#    Comments [0] |
# Thursday, October 11, 2007
# Saturday, October 06, 2007

Pâte à modeler

Note : Acheter de la pâte à modeler à mes enfants.

#    Comments [0] |
# Friday, October 05, 2007
# Thursday, October 04, 2007

Architecture & Strategy : Microsoft releasing the source code for the .NET Framework libraries

dotnet Grosse annonce. Microsoft publie les sources du framework .Net. Les développeurs vont pouvoir inspecter ce qui se passe à l'intérieur du framework. Il était déjà possible d'intuiter le fonctionnement du framework grâce à Reflector, on va maintenant pouvoir débugger et faire du pas à pas dans le code de Microsoft. Le code est publié sous licence Microsoft Reference License (MS-RL). Il sera possible d'inspecter le code, mais aucune modification ou recompilation ne sera autorisée.

Architecture & Strategy : Microsoft releasing the source code for the .NET Framework libraries

#    Comments [0] |
# Tuesday, September 25, 2007

Stop Motion

stopmotion 
Ils ont du passer des jours à faire ça

#    Comments [0] |
# Sunday, September 23, 2007

Peux mieux faire

Manque de litérature...

57% Geek

#    Comments [0] |

Subversion et .Net

Je suis fan de SVN et Trac. Deux produits opensource qui permettent de gérer mes sources et les bugs. Je viens de découvrir l'équivalant .Net : nTrac et DotSvn. Plus besoin d'installer un serveur apache. Le tout doit tourner sur un IIS.

#    Comments [0] |
# Saturday, September 22, 2007

Billard

Il y a des billards au cp maintenant ???

 

#    Comments [0] |

Le raytracing, base des jeux de demain

Les jeux actuels utilisent n'utilisent pas le raytrancing, trop gourmand en ressources pour pouvoir produire une animation fluide en temps réel. Les jeux actuels utilisent des algorithmes qui appliquent des effets sur les triangles composant la scène. On obtient une scène qui a un rendu proche de la réalité. Plus les alogrithmes sont complexes et plus les détails sont fins, les ombres précises, etc... Le raytracing calcule pour chaque pixel affiché à l'écran quels sont les rayons de lumière qui viennent l'éclairer. Ce type de rendu est utilisé pour la réalisation de films d'animation.

L'article original : http://www.pcper.com/article.php?aid=455&type=expert&pid=1

Un moteur de raytracing : http://www.povray.org/

#    Comments [0] |

xUnit un nouveau framework de tests unitaires

5 ans après NUnit, James Newkirk nous parle du nouveau Framework de tests unitaires xUnit. L'idée est de tirer parti des années d'utilisation des outils existants et de corriger les défauts. Je reconnais que les arguments avancés pour la création de xUnit sont justes. La question est de savoir si il sera utilisé par la communauté. 

http://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html

#    Comments [0] |
# Thursday, September 20, 2007

Daft Punk pour les sourds ?

Je mets cette video qui date un peu. A chaque fois que je la vois, elle m'impressionne. Respect ...

#    Comments [0] |
# Wednesday, September 19, 2007

Silverlight 1.1 et les resources de langue

La version 1.1 de silverlight permet d'utiliser les fichiers .resx dans le code compilé comme n'importe quel autre projet. A l'heure actuel, Orcas ne reconnait pas encore qu'il utilise un projet silverlight et génère des attributs qui n'existent pas sur la classe associée. Pour passer la compilation, il suffit donc de recréer les classes qui manquent. 

namespace System.CodeDom.Compiler
{
  
public class GeneratedCodeAttribute : Attribute     
   {     
     
public GeneratedCodeAttribute(string a, string b) { }
   }
}

namespace System.Diagnostics.CodeAnalysis
{
  
public class SuppressMessageAttribute : Attribute  
   {
     
public SuppressMessageAttribute(string a, string b) { }
   }
}
     
namespace System.ComponentModel
{
  
public class EditorBrowsableAttribute : Attribute
   {
     
public EditorBrowsableAttribute(EditorBrowsableState a) { }
   }
  
public enum EditorBrowsableState { Advanced }
}

#    Comments [0] |
# Sunday, September 16, 2007

Blend 2 Sept. Preview

boxShot_Blend

Si vous cherchez un outil pour développer des animations WPF ou Silverlight, il vous faut Blend. Cet outil fait parti de la nouvelle suite d'outil graphiques de microsoft. Les versions Preview de Blend 2 introduisent des fonctionnalités pour silverlight.

Le téléchargement est ici : Blend 2 September preview

#    Comments [0] |

Café quand tu nous tiens.

Le café est un truc assez simple. Un peu d'eau chaude sous pression, un peu de lait pour un cappucino, et c'est servi. La video suivante m'a bluffé, jamais je n'aurais pensé à faire ça avec !

#    Comments [1] |
# Saturday, September 15, 2007

Site Web

Après un long moment avec une page peu esthétique, notre site web se dote d'un nouveau look. Ce look est temporaire, le temps qu'un infographiste nous fasse une charte graphique digne de ce nom.

Les différentes sections du site ainsi que les blogs devraient se remplir au fil du temps.

#    Comments [0] |