Ca fait quelques temps que je bosse sur différents projets Windows 8 (aussi bien WinJS que Xaml / DirectX), et je suis tombé sur un bug un peu ennuyant sur ItemsControl (et comme j’ai un Workaround, ça me fait un sujet sur lequel bloguer utilement !).

Bref, le problème se pose lorsqu’on veut utiliser un ItemContainerStyleSelector sur un ItemsControl Windows 8 Xaml (ListBox, ListView, GridView…). En effet, celui-ci est tout simplement ignoré lorsque les ItemContainers (ListBoxItem, GridViewItem…) sont matérialisés. Pour pallier à ce problème, l’idée est de créer un contrôle dérivant du contrôle à debuguer, et de surcharger la méthode PrepareContainerForItemOverride pour affecter le style de l’ItemContainer en utilisant le StyleSelector.

En C++ ca donne ca :

   1: protected:
   2: virtual void  PrepareContainerForItemOverride(Windows::UI::Xaml::DependencyObject^ element, 
   3:                                               Platform::Object^ item) override
   4: {
   5:     Windows::UI::Xaml::Controls
   6:         ::IItemsControlOverrides
   7:         ::PrepareContainerForItemOverride(element, item);
   8:     auto container = dynamic_cast<Windows::UI::Xaml::Controls::Control^>(element);
   9:     if(ItemContainerStyleSelector && container)
  10:         container->Style = ItemContainerStyleSelector->SelectStyle(item, container);
  11:  
  12: };

(en C# on remplace l’appel à IITemsControlOverrides ::PrepareContainerForItemOverride par base. PrepareContainerForItemOverride).

Comme je suis sympa, je vous ai fait une librairie C++ contenant des fix pour tous les ItemsControl du framework. Vous pouvez la récupérer ici.

 

GridView MultiSize

 

Cela me permet d’enchainer sur une astuce pour créer des GridView MultiSize avec une source de données dynamique. Pour pouvoir faire ça, il faut d’abord utiliser le StyleSelectingGridView. Ensuite, on va modifier l’ItemsPanel utilisé par un VariableSizeWrapGrid(soit celui du GridView, soit celui du GroupStyle dans le cas d’une source de données groupée) :

 

   1: <fixes:StyleSelectingGridView 
   2:                      ItemsSource="{Binding DataSource}" 
   3:                      ItemContainerStyleSelector="{StaticResource MySelector}" >
   4:     <GridView.ItemsPanel>
   5:         <ItemsPanelTemplate>
   6:             <VirtualizingStackPanel Orientation="Horizontal"  />
   7:         </ItemsPanelTemplate>
   8:     </GridView.ItemsPanel>
   9:     <GridView.GroupStyle>
  10:         <GroupStyle>
  11:             <GroupStyle.Panel>
  12:                 <ItemsPanelTemplate>
  13:                     <VariableSizedWrapGrid ItemWidth="150" ItemHeight="90"/>
  14:                 </ItemsPanelTemplate>
  15:             </GroupStyle.Panel>
  16:         </GroupStyle>
  17:     </GridView.GroupStyle>
  18:  
  19:  
  20:  
  21: </fixes:StyleSelectingGridView>

 

L’ItemWidth / ItemHeight du VariableSizedWrapGrid correspondent à la taille des rectangles utilisés pour gérer le layout des éléments multisize. Ensuite, dans les styles renvoyés par le StyleSelector, il faut spécifier le nombre de colonnes / lignes sur lequel doit s’étendre le container :

 

   1: <Style TargetType="GridViewItem">
   2:     <Setter Property="HorizontalContentAlignment" Value="Stretch" />
   3:     <Setter Property="VerticalContentAlignment" Value="Stretch" />
   4:     <Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="3" />
   5:     <Setter Property="VariableSizedWrapGrid.RowSpan" Value="4" />
   6: </Style>

 

Le StyleSelector renverra donc des styles avec des ColumnSpan / RowSpan différents en fonction de la nature de vos données. Le même système existe pour sélectionner des DataTemplate (sauf que dans ce cas-là, il n’y a pas de bug).

Au final on pourra du coup obtenir des layouts assez sympa du style :

 

sc


Si vous êtes abonnes a ce flux, je suppose que vous connaissez déjà C# async (avec les mots-clef async et await). Ce que vous ne savez par contre peut-etre pas, c’est qu’il est tout a fait possible de creer vos propres trucs awaitable.

 

En effet, comme cela était déjà le cas avec la syntaxe Linq a l’époque de C# 3 (mots clefs where, select etc.), le mot clef “await” ne fait que déclencher un traitement de precompilation. En gros, await attend un truc, sans trop savoir ce qu’il attend. Si j’ecris la syntaxe :

 

Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
int value = await DoSomethingVeryComplicated();
Console.WriteLine(value);

 

Apres l’etape de precompilation, le compilateur voit quelque chose du genre :

 

TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
var awaiter = DoSomethingVeryComplicated().GetAwaiter();
awaiter.OnCompleted(() =>
   {
      int value = awaiter.GetResult();
      Console.WriteLine(value);
      tcs.SetResult(null);
   });
return tcs.Task;

 

(je dis bien du genre, parce que je suis pas le compilateur donc je ne vois pas vraiment ce qu’il voit)

 

Un des elements qui semble completement anodin, mais qui fait la toute surpuissance du machin, c’est le “var” a la 3e ligne. En effet, un peu comme le mot clef Where cherche une methode ayant la signature Where(Func<T,bool> predicate) ce qui permet de lui faire faire un peu tout et n’importe quoi, await cherche une methode GetAwaiter() qui renvoit quelque chose ayant une methode OnCompleted(Action completion) et une methode GetResult(). Et tant que ces methodes sont presentes, ca marche !

 

Par exemple, je peux ecrire cette classe :

 

    public class MaFakeTaskJustePourEmbeterLeCompilo
    {
        private int _result;
        private bool _completed;
        private List<Action> _completions = new List<Action>();
 
        public MaFakeTaskJustePourEmbeterLeCompilo()
        {
            // truc asynchrone parce que je le veux bien
            Task.Factory.StartNew(() =>
                                      {
                                          Thread.Sleep(3000);
                                          _result = 42;
                                          NotifyCompleted();
                                      });
        }
 
 
        private void NotifyCompleted()
        {
            _completed = true;
            lock (_completions)
            {
                foreach (var c in _completions)
                    c();
                _completions.Clear();
            }
        }
 
        public void OnCompleted(Action c)
        {
            // ce code ne gere pas le SynchronisationContext courrant pour plus de lisibilite.
            // ne pas utiliser tel quel dans une appli WP7 !
            lock (_completions)
            {
                if (_completed)
                {
                    c();
                }
                else
                    _completions.Add(c);
            }
        }
        public int GetResult()
        {
            return _result;
        }
 
        public bool IsCompleted
        {
            get { return _completed; }
        }
 
        public MaFakeTaskJustePourEmbeterLeCompilo GetAwaiter()
        {
            return this;
        }
    }

 

et l’utiliser ensuite avec async :

 

Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
var value = await new MaFakeTaskJustePourEmbeterLeCompilo();
Console.WriteLine(value);

 

Maintenant, vous savez comment faire attendre un truc rigolo au mot clef await. Youhou!

 

(plus serieusement, ca peut permettre d’utiliser la syntax-sugarite de async avec des trucs pas prevus pour, et ca, c’est cool)


Depuis la sortie de la beta de Silverlight 5, David Catuhe a lancé un projet Codeplex Babylon Toolkit avec pour but de simplifier la vie aux développeurs d’applications utilisant les APIs 3d de Silverlight 5.

 

Depuis un mois, j’ai le plaisir de l’épauler dans ce projet passionnant (ouais, bizarrement, simuler de la physique sur le GPU pour faire des effets de fumée derrière un Viper en 3D, ca me passionne un peu plus que de faire des applications business avec toujours les même boutons, textbox, data grid etc.). La version 1.0.4 apporte un bon gros lot de nouveautés : Particle System, pas mal de nouveaux effets, des optims et bug fixes d’un peu partout, un generateur de primitives, du skinning, des samples de la mort,… Ça a occupé pas mal de nos soirées, mais c’est pas grave, parce que ca poutre !

 

L’autre projet dont je voulais parler est SL3DMI (Silverlight 3D Model Importer), lui aussi sur Codeplex (et dévelopé par moi-même), qui permet d’exporter des modèles depuis une application Xna Windows (et ainsi bénéficier du ContentPipeline d’Xna) dans un format optimisé pour le rendu real-time, compact et très rapide à charger sur la carte graphique, et de le réimporter dans une application Silverlight 5. Depuis la version 1.0.4, une certaine synergie s’est installée entre les 2 projets, et SL3DMI permet maintenant de créer des Modèles Babylon Toolkit à partir des même fichiers optimisés.

Pour un apercu de ce que peut donner ces 2 projets ensemble, voici une liste de liens (qui nécessitent Silverlight 5):

Un tank qui bouge

Un Lézard avec du Bump Mapping

Un bonhomme qui bouge (skinning)

Un Viper de Battlestar Galactica avec des particules qui bougent


... en Francais, et avec du qui pique dedans !

Si vous avez installé le runtime developpeur de Silverlight 5, enjoy la démo ! (attention le xap est un peu gros)

Ou pour une version plein écran : http://www.simonferquel.net/blog/Dwarf5/DwarfSL5TestPage.html

(je précise que le modèle 3D utilisé est tiré des sample XNA... merci monsieur Microsoft pour ces modèles de qualité !)

 


Ca fait un moment que je n'ai pas bloggé, pour cause de plein de taff (et aussi de flemme, et un peu de WoW en début d'année), et pour cette fois, je voulais montrer un truc marrant en ASP.Net dont j'ai eu besoin chez un client.

 

Le contexte est simple: on a une appli ASP.Net, développée en MVC 2 qui a un soucis qui ne se produit que lorsqu'elle est déployée en mode WebFarm (plusieurs serveurs Web, répartisseur de charge sans affinité de session). Le détail du problème nous importe peu (il s'agissait d'un soucis au niveau de l'invalidation d'un cache partagé), ce qui nous intéresse, c'est la mise en place de l'environement de debugging.

 

Les 2 solutions qui viennent le plus vite a l'esprit sont:

 

  • La mise en place de machines virtuelles, du remote debugging et l'installation d'un repartisseur de charge type IIS ARR. Cette solution est assez lourde en terme d'install / config (les joies du parametrage du Remote Debugger...)
  • La creation de 2 sites locaux qui ecoutent sur un port différent et configuration d'un IIS ARR pour distribuer la charge sur ces 2 sites. Cela nécessite de connaitre un minimum ARR, et attacher le debugger aux différents sites manuellement après chaque build, c'est pas super fun

 

La 3e solution que je vous propose aujourd'hui est celle qui est la plus agréable pour le développeur : Utiliser l'environement de simulation de Windows Azure (qui repose sur IIS Web Core), et paramétrer un WebRole contenant votre appli Web en mode multi-instance:

 

  • Dans notre solution, on commence par ajouter un projet Azure.
  • Quand on nous demande les roles à ajouter, on dit “rien”,
  • puis dans le SolutionExplorer, sur le dossier “Roles” du projet Windows Azure, on fait Clic droit => Add => Web Role Project in solution
  • On selectionne notre projet Web, et on fait Ok

 

Ensuite on configure le Role comme ceci (clic droit => Properties sur le role qui est apparu dans le projet Windows Azure):

Selectionnez le projet Windows Azure comme projet de demarrage, et vous voila avec 4 instances d’applications ASP.Net sous IIS Web Core avec un NLB logiciel devant !

 

Attention tout de même, l’algorithme de distribution des requêtes semble un peu spécial (basé sur le temps plutôt que sur un nombre de requêtes traitées par serveur) et fonctionne mieux avec 2 instances.

 

A savoir également, par défaut le NLB écoute sur le premier port libre à partir de 80. Si Vous voulez qu’il écoute sur le 80 plutôt que le 81 (comportement par défaut si vous avez installé IIS), coupez l’environement de simulation Azure, stoppez le service IIS, et relancez le debug.

 

Enjoy!


J’ai beau être en vacances au bord de la piscine, quand Microsoft sort une beta du SDK Windows Phone 7, je ne peux pas passer à coté…

J’ai donc pris le temps de travailler un peu sur SL Flow Layouts Library pour inclure le support de cette nouvelle plateforme (j’en ai aussi profité pour y inclure une première version pas encore tout optimisée d’un contrôle Pivot de mon cru) et corriger quelques légers bugs.

Attention par contre, sous WP7, la virtualisation est une fonctionnalité opt-in (car les cas d’utilisation dans lesquels on utilisera un layout type cover-flow ou la virtualisation a un impact négatif sont loin d’être rare).

Comme d’habitude, ça se passe sur CodePlex… Enjoy !


Ça fait un moment que je n’ai pas poste de trucs intéressants sur ce blog. Il faut dire qu’entre le Remix 2010, les divers projets avec Access it et Microsoft, mon temps libre se réduit a peau de chagrin en ce moment…


… surtout qu’il se fait phagocyter par un projet perso que j’entretiens avec ma douce depuis quelques mois : http://www.kaikilia.com. Il s’agit d’une galerie de ses créations photographiques faite entièrement en Silverlight 4 et RIA services, avec un soupçon d’ASP.Net MVC 2 pour SEO (oui, d’ici quelque temps, tout le contenu public du site sera visible via Google et Bing). Je ferai surement des petites vidéos présentant le back-office qui lui aussi est Full Silverlight, avec des outils de cropping, d’édition d’Exif et autres joyeuseries (la grosse complexité du projet réside d’ailleurs dans le backoffice).


N’hésitez pas à commenter le travail de ma belle (qui fait ça en amateur, je précise quand-même), ou à me contacter si vous voulez quelques détails techniques sur SEO, Silverlight, Ria services ou autre !


.Net 4.0 et Silverlight 4 sont sortis en RTM la semaine dernière, et j’ai donc mis à jour le projet et j’en ai profité pour apporter quelques nouveautés au niveau stylisation, layouts, et biensur, une petite dose de bugfixes.

Ca se passe toujours sur codeplex, et n’hésitez pas à faire du Feedback !


Plus tôt dans la journée, j’ai posté à propos d’une technique de PlaneProjection pour WPF à base de Shader Effect. Cette technique avait pour but principal de porter la Silverlight Flow Layouts Library pour WPF. C’est désormais chose faite : le projet CodePlex est mis à jour (binaires + sources), et le projet fait maintenant du Multi-Targeting Silverlight 4.0 RC / WPF 4.0 RC.

 

N’hésitez pas à apporter tout feedback aussi bien sur la librairie en elle-même, que sur la PlaneProjection pour WPF ou les features que vous aimeriez voir.

 

Autre chose, si vous développez vos propres algorithmes de layout, n’hésitez pas à me contacter via ce blog (ou via le site codeplex), pour venir agrémenter la future bibliothèque d’algorithmes qui va bientôt venir…


Depuis la version 3, Silverlight propose une fonctionnalité permettant de donner un effet de perspective à nos UIElements de manière très simple et très efficace : les Plane Projections. On profite ainsi d’un affichage en perspective 3D très simple à mettre en place et très performant.

 

Le problème

 

WPF ne fournit pas de Plane Projections, et nous oblige à passer par de la « vraie » 3D dès lors que l’on veut afficher quelque chose en perspective. Cela pose 2 problèmes importants :

  • la complexité requise lorsque l’on veut simplement afficher une surface plane en perspective
  • les performances qui chutent dramatiquement quand on multiplie le nombre de Viewport2DVisual3D

 

En effet, avec WPF, appliquer un effet de perspective à un élément requiert les étapes suivantes :

  • Créer un Viewport3D
  • Lui associer une PerspectiveCamera
  • Lui ajouter un Viewport2DVisual3D
  • Lui affecter un modèle (avec les coordonnées de textures et les normales)
  • Lui affecter la transformation3D correcte

 

Cela fait beaucoup d’étapes et certaines ne sont pas forcément simple à comprendre lorsque l’on travaille essentiellement dans un monde 2D.

 

Au niveau des performances, outre les nombreux objets nécessaires et couteux mis en œuvre, il faut savoir que pour chaque transition entre une surface 2D et un Viewport3D, WPF produit une Render Target Direct3D intermédiaire avec des conséquences très importantes pour les performances. en effet chaque RenderTarget consomme une quantité importante de mémoire vidéo, d’autant que sous Vista / 7, l’anti-aliasing 4x est appliqué à chaque RenderTarget participant à WPF 3D, et le switch de RenderTarget est quelque chose de très couteux. Il suffit de créer une scène avec une 15aine de Viewport2DVisual3D pour se rendre compte de l’impact.

 

Solution envisagée

 

Pour pallier à ces 2 problèmes (et aussi pour pouvoir porter la Flow Layout Library pour WPF), j’ai décidé de tenter une approche différente, et très proche de ce qui semble avoir été fait pour Silverlight. Il s’agit de générer l’effet de perspective à partir d’un ShaderEffect combiné à un TranslateTransform pour reproduire l’effet d’une PlaneProjection. Comment cela est-il possible ? Simplement avec un petit peu de mathématiques (au passage, pour les poilus, le livre Mathematics for 3D Game Programming & Computer Graphics, c’est top).

 

On commence par produire côté C# une matrice de transformation 3D (composée de la matrice de transformation par rapport à la position d’origine, de la matrice de transformation par rapport au point de vue, et de la matrice de projection qui donne l’effet de perspective)  et d’en déduire l’équation du plan généré (je vous passe la formule mathématique, ça parle de multiplication matricielles, de produits scalaires et autres joyeusetés). Cela n’a besoin d’être fait que lorsque la transformation demandée change.

 

 

Une fois qu’on obtient cette équation (composée de la normale au plan et de la distance minimale entre le point {0,0,0} et le plan), on la passe au Pixel Shader avec la matrice inverse de celle utilisée pour transformer le plan. Dans le PixelShader, pour chaque pixel à rendre, on va calculer l’intersection entre le rayon partant de ce point et se projetant dans l’axe Z, et le plan (définit par l’équation produite à l’étape précédante). On multiplie alors cette coordonnée par la matrice de transformation inverse pour savoir quelle est la position au sein du plan (et donc obtenir la coordonnée du Pixel de la texture d’origine à renvoyer).

 

On ajoute à ça quelques petites optimisations du genre clipping de la zone de rendu et utilisation d’un TranslateTransform pour éviter d’avoir à appliquer cet effet sur une surface trop grande (dans le cas où la PlaneProjection produit une translation importante) et la création d’un GeneralTransform utilisé pour le HitTesting,  et on se retrouve avec une solution beaucoup plus simple à utiliser (au final, on obtient la même chose que la PlaneProjection de Silverlight) et beaucoup plus performante (pas de RenderTarget intermédiaire, consommation de mémoire vidéo réduite…) que le Viewport2DVisual3D. Bien sûr l’inconvénient est que l’on ne peut pas mapper nos surfaces 2D sur des objets 3D autres que des plans avec cette technique, mais dans de nombreux cas, c’est exactement ce à quoi l’on veut se limiter !
(note, je ne ferai pas de listing de code dans ce post, car c’est long et pas vraiment très expressif, mais vous pourrez le télécharger à la fin de l’article).

 

Le résultat

 

Au final on va pouvoir créer ce genre d’effet :

Avec très peu d’efforts :

 

<Border Background="Transparent"  x:Name="border">

   <Media:PlaneProjection.PlaneProjection>

      <Media:PlaneProjection/>

   </Media:PlaneProjection.PlaneProjection>


</Border>
   <StackPanel Grid.Column="1">

      <TextBlock Text="GlobalOffsetX" />

      <Slider x:Name="offsetX" Minimum="-200" Maximum="200"

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).GlobalOffsetX}" />

      <TextBlock Text="GlobalOffsetY" />

      <Slider x:Name="offsetY" Minimum="-200" Maximum="200"

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).GlobalOffsetY}" />

      <TextBlock Text="GlobalOffsetZ" />

      <Slider x:Name="offsetZ" Minimum="-200" Maximum="200" 

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).GlobalOffsetZ}" />

      <TextBlock Text="RotationX" />

      <Slider x:Name="rotationX" Minimum="-180" Maximum="180"

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).RotationX}" />

      <TextBlock Text="RotationY" />

      <Slider x:Name="rotationY" Minimum="-180" Maximum="180"

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).RotationY}" />

      <TextBlock Text="RotationZ" />

      <Slider x:Name="rotationZ" Minimum="-180" Maximum="180"

              Value="{Binding ElementName=border, Mode=TwoWay, Path=(Media:PlaneProjection.PlaneProjection).RotationZ}" />

</StackPanel>

 

Ça se présente juste sous la forme d’une AttachedProperty, et au niveau utilisation, c’est la seule différence avec la PlaneProjection de Silverlight.

Bien sûr, on va aussi pouvoir faire des choses plus complexes… comme le portage de Silverlight Flow Layout Library par exemple qui va venir avec la sortie de Silverlight 4 RC :

Les sources :

 

Vous pourrez retrouver les sources dans le projet CodePlex http://slflow.codeplex.com lors de la prochaine release. En attendant je les mets a dispo ici meme.