Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

Actualités

Shift

View Georges Legros' profile on LinkedIn


DependencyChecker: Comment faire pour ignorer certaine rêgles
Je viens de me battre avec un wizard Microsoft qui contenait un DependencyChecker.

Ce dernier, comme vous l'aurez certainement compris, passe en revue un certain nombre de rêgles afin de s'assurer que la solution installée peut tourner sans problême.
C'est donc une sorte de sécurité.

Mais dans mon cas, cette sécurité m'a joué un mauvais tour...

Après le lancement du Wizard, je me retrouve avec le message suivant:
Your current OS is not supported by Fabrikam Shipping sample. This sample uses Windows Identitiy Foundation that requieres Windows Vista SP2, Windows Server 2008 SP2, Windows 7 or Windows Server 2008 R2.

Constat: mon OS est ... Windows 7 Tongue Tied Tongue Tied

Bon, on ne se décourage pas et on regarde un peu plus loin.

Il se trouve que dans les fichier d'installation, je tombe sur un super fichier de config: Dependencies.xml

Celui-ci, en plus d'autre choses, contient le bloc suivant

  <dependencies>
    <os type="Vista;Server" buildNumber="2600;6001;6002;7000;7100;7600">
             [...]
    </os>
  </dependencies>

Une recheche rapide me rend comme résultat que 7600 c'est le build number de Windows 7... Etrange....
Il semble en effet que le SP1 de 7 fasse passer le build number a 7601, ce qui à en croire le registre, est le cas de mon PC.
Vous trouverez l'info de votre pc ici: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

On relance le tool et miracle, on passe a l'étape suivante.
Et la une nouvelle erreur... Une autre dépendance: Visual Studio 2008

Ayant Visual Studio 2010, je pense pouvoir affirmer que ce que je veux installer va fonctionner.

Le bloc incriminé est le suivant:

        <dependency
                    value=".\scripts\Dependencies\Check\CheckVS2008.ps1"
                    enabled="true"
                    optional="false"
                    title="Microsoft Visual Studio 2008"
                    explanation="This sample requires Microsoft Visual Studio 2008"
                    scriptName=""
                    downloadUrl="http://www.microsoft.com/visualstudio/" />

Dans ce cas-ci, plusieurs façon de contourner:
  1. faire passer le enabled a false (a vérifier)
  2. faire passer le optional a true : ceci va générer un warning dans le wizard mais permettra de passer a l'étape suivant
  3. Modifier le script (CheckVS2008.ps1) pour lui faire retourner true ou lui faire chercher VS2010
  4. [PREFERED OPTION] effacer le bloc, pourquoi s'embêter...

Au passage, le script est le suivant:

function SearchUninstall($SearchFor)
{
$uninstall = "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
$uninstallObjects = ls -path $uninstall ;
$found = $FALSE;

foreach($uninstallEntry in  $uninstallObjects)
{
   $entryProperty = Get-ItemProperty -path registry::$uninstallEntry
   if($entryProperty.DisplayName -like $searchFor)
    {      
       $found = $TRUE;
       break;
    }
}

$found;
}

SearchUninstall -SearchFor 'Microsoft Visual Studio 2008*';

 

Ca n'a pas l'air bien sorcier a modifier mais fait étrange, il va chercher dans \Uninstall ...

Etrange parce que Visual Studio a sa propre entrée dans la registry. Ca ressemble donc a une sorte de hack...

Enfin, toujours est-il que mon problème est résolu et que, je l'espère, si vous aussi vous avez ce probleme, ce post pourra vous aider.

A bientôt!

Posté le dimanche 11 mars 2012 18:09 par DjoDjo | 0 commentaire(s)

Classé sous : , ,

Navigation Silverlight : Quand les évènements nous échappent…

Salut à tous,

 

Ca fesait longtemps que je n’étais pas passé par ici.

Cependant je reviens pour vous parler d’un petit soucis de la navigation Silverlight.

Je ne suis pas certain qu’il convienne de parler de Bug mais toujours est-il qu’il pourrais être intéressant de repasser par la…

 

Mise en situation

J’utilise l’événement Navigated de la Frame afin d’être nofitié d’une navigation (tiens donc…)

Dans l’eventArg (de type NavigationEventArgs) se trouve la propriété Content.

Celle-ci représente la page vers laquelle vous avez navigué.

Le but étant d’exécuter des actions spécifiques au niveau de la Frame à ce moment (modification des menu, …)

 

Problème

Si vous êtes sur la page Home et que vous lancez une navigation vers cette même page Home (click sur un menu ou HyperlinkButton, header du site) la valeur de la propriété Content est NULL…

 

Ce n’est évidement pas un problème bien méchant mais j’avoue ne pas comprendre pourquoi ne pas simplement repasser la page.

Tout ca pour dire que même si “ca n’a pas de sens…” il est important de faire des vérification sur la valeur des paramètres. Ca peut toujours éviter des comportements non attendu.

 

A bientôt,

DjoDjo

Posté le lundi 1 novembre 2010 14:17 par DjoDjo | 3 commentaire(s)

Classé sous : ,

[Silverlight] Un petit contrôle d’expand/collapse

J’ai du cette semaine trouver une solution pour “Animer la transition entre l’état Visible et Collapsed” d’un contrôle.

L’idée est de faire quelque chose permettant d’avoir une transition smooth.

Pour avoir une flexibilité maximum, j’ai choisi d’étendre un ContentControl. Cela me permettra d’utiliser mon contrôle comme un container facilement réutilisable.

Le style est plutôt simple :

<Style TargetType="local:VisibilityAnimator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:VisibilityAnimator">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Rectangle x:Name="animator" />
<ContentPresenter x:Name="presenter" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
 

Notez tout de même la présence du rectangle “animator” sur lequel je reviendrai plus tard.

Au niveau du code behind, il faut noter la présence de la DependencyProperty Visibility qui est évidemment celle qui nous utiliserons afin de lancer les animations. Il est évidemment impératif d’utiliser le mot clé “new” afin de spécifier que nous voulons faire un override de la propriété (déjà existante dans UIElement).

Dans le Handler de la DependencyProperty, il nous suffira de faire un check sur le e.NewValue afin d’appeller les méthode Expand ou Collapse.

Pour le Collapse, les chose sont assez simple. Nous allons procéder en 4 étapes.

  1. Donner au rectangle “animator” (cf le code XAML) la taille actuelle du contrôle.
  2. Rendre “presenter” transparent. Ici, pas d’animation, après de nombreux test, le temps du fade out est plus gênant qu’autre chose. Mais je garde quand même l’étape pour 2 petites raisons, si vous voulez utiliser ce contrôle et que vous désirez une animation de FadeOut avant celle de Collapse, c’est ici que ca se passe. Lors de l’Expand, j’utilise une animation pour effectuer le FadeIn. j’ai donc besoin que l’opacité soit à 0.
  3. Rendre “presenter” invisible. j’insiste sur la nuance, ici nous utilisons bien la propriété Visibility.
  4. Enfin, une petite animation pour la taille de “animator”.

Au niveau du code cela donne:

private void Collapse()
{
//Step 1 : Set animator size
animator.Width = ActualWidth;
animator.Height = ActualHeight;
//Step 2 : Fade out Children
UpdatePresenterOpacity(0);
//Step 3 : Make Children invisible
UpdatePresenterVisibility(Visibility.Collapsed);
//Step 4 : Make Rect small
GetAnimatorSizeStoryboard(0, 0).Begin();
}
 

Ici nous remarquons tout d’abord les 2 méthode UpdatePresenterXXX() qui ne font rien d’autre que changer la propriété ciblée avec la valeur en paramètre. L’utilité réside dans le fait qu’avant de réellement modifier la valeur, je fait un test afin de vérifier que “presenter” n’est pas NULL.

Vous n’aurez évidemment pas manqué la méthode GetAnimatorStoryboard dont voici le code:

private Storyboard GetAnimatorSizeStoryboard(double width, double height)
{
Storyboard sizeStoryboard = new Storyboard();
sizeStoryboard.Children.Add(GetAnimation(animator, width, ANIMATION_TIME, "Width"));
sizeStoryboard.Children.Add(GetAnimation(animator, height, ANIMATION_TIME, "Height"));
return sizeStoryboard;
}
 

Utilisant GetAnimation :

private static Timeline GetAnimation(DependencyObject obj, double value, double duration, string property)
{
var newAnim = new DoubleAnimationUsingKeyFrames { BeginTime = TimeSpan.FromSeconds(0) };
Storyboard.SetTarget(newAnim, obj);
Storyboard.SetTargetProperty(newAnim, new PropertyPath(property));
newAnim.KeyFrames.Add(new SplineDoubleKeyFrame
{
Value = value,
KeyTime = TimeSpan.FromSeconds(duration),
KeySpline = new KeySpline
{
ControlPoint1 = new Point(.188, .000),
ControlPoint2 = new Point(.0, 1)
}
});
return newAnim;
}

Enfin, nous pouvons nous attaquer à la méthode Expand, un tout petit peu plus compliquée. Cette fois-ci j’ai relevé 5 étapes clé :

  1. Donner au rectangle “animator” la taille actuelle du contrôle. Cela devrait donner 0…
  2. Récupérer la taille finale (nous verrons comment après…)
  3. Animer la taille du rectangle pour retrouver la taille finale
  4. Rendre “presenter” visible
  5. Enfin, une petite animation pour le FadeIn de “presenter”

ce qui donne en code:

private void Expand()
{
//Step 1 : Set animator size
animator.Width = ActualWidth;
animator.Height = ActualHeight;
 
//Step 2 : Get final size
UpdatePresenterVisibility(Visibility.Visible);
UpdateLayout();
double finalHeight = ActualHeight;
double finalWidth = ActualWidth;
UpdatePresenterVisibility(Visibility.Collapsed);
UpdateLayout();
 
//Step 3 : Make rect big
Storyboard expandStoryboard = GetAnimatorSizeStoryboard(finalWidth, finalHeight);
expandStoryboard.Completed += delegate
{
//Step 4 : Make children visible
UpdatePresenterVisibility(Visibility.Visible);
//Step 5 : Fade in Children
GetChildrenFadeStoryboard(1).Begin();
};
expandStoryboard.Begin();
}

Nous conviendrons tous que la Step 2 n’est pas parfaite mais je n’ai pas trouvé de meilleure façon de faire… Je suis donc ouvert à toute amélioration…

J’arrive donc a la fin de cet article… Il ne me reste plus qu’à espérer que vous avez apprécier votre lecture et que ce contrôle vous sera utile ;-)

A bientôt,

DjoDjo

Posté le mercredi 7 avril 2010 09:52 par DjoDjo | 0 commentaire(s)


Attachment(s): VisibilityAnimator.zip

[Silverlight] Bug avec Firefox 3.6

Il y a quelques jours, la version 3.6 du navigateur Firefox est sortie.

Le problème c’est que si vous avez une application Silverlight qui occupe tout l’espace du browser, une fois que vous donnez le focus au plugin, Firefox ajoute automatiquement des ScrollBars.

En plus d’être esthétiquement dérangeant, elles ne servent à rien car il n’y a qu’un pixel qui déborde et c’est une sorte de Focus Visual ajouté par le browser lui-même.

Le fautif est au fait Visual Studio. Lorsqu’il génère le site ASP.Net et plus précisément la page aspx d’hébergement, il ajoute le style suivant :

<style type="text/css">
html, body
{
height: 100%;
overflow: auto;
background: #97ACBA;
}
body
{
padding: 0;
margin: 0;
}
#silverlightControlHost
{
height: 100%;
text-align: center;
}
</style>

Les plus attentifs auront déjà compris, modifier le overflow: auto; en overflow: hidden; et votre problème est réglé !!

Je ne sais pas si c’est la façon la plus propre de faire mais ca a au mois 1 avantage : ca marche…

A bientôt,

DjoDjo

Posté le jeudi 4 février 2010 10:55 par DjoDjo | 3 commentaire(s)

[.Net] Gestion d’un framework

Cet article est valable pour tous les types de Framework mais l’exemple que m’occupe concerne plus particulièrement Silverlight.

Mise en place du décor:

Dans mon projet actuel, je suis impliqué dans un Framework ayant pour but de fournir des contrôles Silverlight aux autres projets en cours dans la boite.

La semaine dernière avec mon responsable, nous avons ouvert un débat: “Quelle est la meilleure manière de gérer les namespaces ?”

La première approche que nous avons suivie consistait en ces 2 principes :

  1. Chaque contrôle ou groupe de contrôles liés (par exemple TabControl et TabItem) ont un répertoire dédié.
  2. Les namespaces collent à la hiérarchie physique interne du projet

Nous avions donc des namespaces qui ressemblent à “CompanyName.ProjectName.AssemblyName.FolderName”

Ce qui a pour résultat dans le cas des TabControl et TabItem custom de donner les noms complets suivants:

  • CompanyName.ProjectName.AssemblyName.TabControl.CustomTabControl
  • CompanyName.ProjectName.AssemblyName.TabControl.CustomTabItem

Description du problème :

Le Framework a pas mal évolué depuis ses débuts et nous avons maintenant plusieurs dizaines de contrôles. Cela implique que nous avons également plusieurs dizaines de namespaces.

Pour certains contrôles nous avons également créé des librairies dédiées. Ainsi nous avons 2 type de DataGrid différentes et chacune a son assembly.

Solution possible :

En regardant comment d’autres gèrent cela, nous nous sommes attardé sur la solution type Microsoft.

En effet, l’exemple de Microsoft nous montre qu’il n’est pas du tout nécessaire de créer une collection de namespace. Leur approche tout à fait différente. Par exemple, l’assembly System.Windows.Controls (http://msdn.microsoft.com/en-us/library/system.windows.controls.aspx) contient tous les contrôles de la librairie sans “sous-namespace”.

Plus interpellant, d’autres librairies rajoutent parfois du contenu directement dans System.Windows.Controls…

Il y a également certains contrôles tels que la DataGrid qui arrive dans System.Windows.Controls.Data.

Le choix effectué :

L’approche Microsoft est plutôt tentante car elle nous permettrait de limiter les namespace devenant très encombrants. Elle a toutefois un désavantage conséquent : un seul folder pour contenir des dizaines de classes ça fait rapidement désordre.

Nous avons donc décidé de casser la règle prise au début qui disait que “Les namespaces collent à la hiérarchie physique interne du projet”.

Nous avons donc réussi à éliminer nos namespaces inutiles mais également à garder un certain ordre permettant une gestion et une maintenance facile.

Et vous ?

Pour être tout à fait honnête, le but de ce post était double.

Il me permet de partager avec vous le process qui nous suivons mais également de vous demander de quelle manière vous feriez.

Qui sait, nous n’avons pas encore commencé le refactoring, nous trouverons peut-être de très bonnes idées dans vos approches personnelles.

Au plaisir de vous lire…

A bientôt,

DjoDjo

Posté le dimanche 31 janvier 2010 12:21 par DjoDjo | 5 commentaire(s)

Classé sous : ,

[Silverlight] Bug dans les Pixel Effects

Hier et ce après des heures de recherches nous (Maarten De Wilde et moi-même) avons trouvé un bug plutôt gênant dans la gestion des Pixel Shader Effect de Silverlight...

En effet, lors de son utilisation avec une TextBox (oui vous avez bien lu...) les performances se dégradent rapidement.

Exemple de référence :

Pour ce qui suit, et pour voir clairement le problème, nous alons utiliser le enableRedrawRegion dans les paramètres de l'object HTML

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

Prenons comme base une Grid de 500x500 avec une TextBox au millieu.

Lorsqu'on lance l'application, au moment ou l'on donne le Focus à la TextBox, on peut voir que le "Caret" clignotte en couleur (grâce au enableRedrawRegion) et cela est tout à fait normal.

Maintenant commencent les problèmes...

Appliquons maintenant une DropShadowEffect à la Grid et relancons l'exemple.

On peu alors voir que lorsque la TextBox prend le Focus, TOUTE la Grid est clignotte....

Pire encore, lorque la TextBox perd le Focus, ca continue de clignotter...

Preuve en est que si on ouvre le Task Manager le CPU Usage du browser monte significativement...

Et maintenant ?

Le problème a été remonté chez Microsoft et je ne peux qu'espérer que celui-ci sera fixé dans la prochaine version de Silverlight...

Néanmoins pour ceux qui veulent continuer à utiliser une DropShadow sans impacter les performances (ou les impacter moins...) vous pouvez toujours utiliser la technique multicouche... Il suffit pour cela d'avoir deux object l'un en dessous de l'autre, celui du dessous contiendrait la DropShadow. Dans notre exemple de référence, on ajouterais un Rectangle blanc dans la Grid sous la TextBox.

A bientôt !!

DjoDjo

Posté le mardi 5 janvier 2010 09:23 par DjoDjo | 0 commentaire(s)

Classé sous :

[Prism] Vidéo en ligne

Comme je l’avais annoncé dans un post précédent, la vidéo du Live Meeting sur Prism est maintenant en ligne.

Si vous l’avez manqué ou si vous voulez la revoir, voici l’adresse : http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=1485#

Bon film ;-)

DjoDjo

Posté le mardi 24 novembre 2009 10:09 par DjoDjo | 0 commentaire(s)

Classé sous : , , ,

[Silverlight] Behaviors et Triggers

Introduction

Comme promis dans un précédent article, je reviens pour vous parler des Behaviors et des triggers de Silverlight 3.

Ceux qui connaissent WPF le confirmeront, les Triggers sont à la fois extrêmement pratique et puissant. C’est d’ailleurs l’une des choses qu’un développeur WPF regrette le plus lorsqu’il commence à développer en Silverlight.

La troisième mouture de Silverlight arrive donc à point nommé.image

Ou se cachent-ils ?

Arrêtez tout de suite de chercher dans les librairies par défaut de Silverlight, vous ne trouverez rien…

En effet, ces librairies ne font pas partie du Framework mais proviennent du SDK de … Microsoft Expression Blend 3.

Vous trouverez donc les DLL voulue à cet endroit :
C:\Program Files\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\Silverlight
[Si vous avez un OS 64, il se peut que ce soit dans « Program Files (x86) » ]

Si vous n’avez pas de licence pour Blend, pas de panique… vous pouvez télécharger une version d’évaluation ici (http://expression.microsoft.com/en-us/cc136523.aspx).

De quoi ai-je besoin ?

Je vais ici parler principalement de 3 classes à savoir Behavior, TriggerAction and TargetedTriggerAction.

Behaviorimage

Pour les allergiques à l’anglais, ce mot signifie « comportement ». En d’autres termes, il s’agit de définir ce qu’un composant va faire lorsqu’il sera face à une situation particulière…

On aura accès à l’élément associé grâce à la propriété AssociatedObject de type T.

public class FillBehavior : Behavior<Shape>
{
private List<Color> colors = new List<Color> { Colors.Red, Colors.Magenta, Colors.Cyan, Colors.Green, Colors.Purple };
private int index;
protected override void OnAttached()
{
base.OnAttached();
 
AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
}
 
protected override void OnDetaching()
{
base.OnDetaching();
 
AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
}
 
private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
index++;
if (index >= colors.Count)
index = 0;
AssociatedObject.Fill = new SolidColorBrush(colors[index]);
}
}
 

Comme on peut le voir ici, nous surchargeons simplement la méthode OnAttached et OnDetaching. C’est Le Behavior qui se lie à l’évènement MouseLeftButtonDown, le contrôle auquel on attachera le behavior n’aura donc rien à faire.

Voici comment lier le Behavior à un élément en XAML :

<Rectangle x:Name="shape">
<Interactivity:Interaction.Behaviors>
<Behaviors:FillBehavior />
</Interactivity:Interaction.Behaviors>
      </Rectangle>
 

Ou en C# :

FillBehavior behavior = new FillBehavior();
behavior.Attach(shape);

 

TriggerAction<T> image

Ici on va gagner en flexibilité… La classe que nous écrirons ne sera plus responsable dans le choix de l’évènement auquel réagir.

L’utilisateur pourra donc décider de quand exécuter l’action.

Nous aurons toujours accès à l’AssociatedObject mais également  à l’eventArgs correspondant à l’évènement utilisé.

Ici l’action ne sera pas exécutée dans un Event Handler mais dans la méthode Invoke qu’il faudra évidemment surcharger :

protected override void Invoke(object parameter)
{
index++;
if (index >= colors.Count)
index = 0;
AssociatedObject.Fill = new SolidColorBrush(colors[index]);
}
 

Si le principe est similaire, il faut ici voir une sorte d’event handler réutilisable et que l’on peut attacher à des évènements de natures différentes.

En ce qui concerne la méthode utilisée pour attacher le Trigger, c’est similaire à celle utilisée pour le Behavior:

<Rectangle x:Name="shape">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="MouseLeftButtonUp">
<TriggerAction:FillTriggerAction/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Rectangle>

TargetedTriggerAction<T> image

Ici on prend les même et on recommence… ou presque.

Il s’agit cette fois de s’attacher à un event provenant d’un contrôle mais à exécuter l’action sur un autre.

On pourra donc récupérer le click (par exemple) sur un bouton et exécuter une action sur un Rectangle à un autre endroit.

Concernant le code, il n’y a pas de différence majeure à noter sauf que cette fois-ci on n’effectue une action sur la cible, Target.

protected override void Invoke(object parameter)
{
index++;
if (index >= colors.Count)
index = 0;
Target.Fill = new SolidColorBrush(colors[index]);
}
 

Attacher le trigger sera également un peu différent puisqu’on devra le faire à partir de la source et non de la cible:

<Button Content="Click here !!">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click">
<TargetedTriggerAction:FillTargetedTriggerAction TargetName="shape"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>

Pour aller plus loin…

… vous pouvez aussi utiliser les 2 attributs suivants:

DefaultTrigger

Utilisé sur un TriggerAction<T>, celui-ci vous permet de spécifier en fonction de l’AssociatedObject, l’évènement par défaut utilisé.

Attention à ne pas se méprendre, cela ne permet pas au développeur de rajouter le trigger sans spécifier l’évènement.

Cette information sera utilisée pas Blend lorsque vous utiliserez le trigger depuis l’éditeur.

[DefaultTrigger(typeof(Rectangle), typeof(EventTrigger), "MouseLeftButtonUp")]

Notez aussi que vous pouvez en utiliser plusieurs sur un seul trigger. Vous pourrez alors définir des event par défaut différent pour un Button et pour un Rectangle.

TypeConstraint

Dans le cas d’un TargetedTrigger<T>, le T est le type de l’objet Target. Le type de l’objet cible lui n’est pas défini.

Si vous souhaitez le définir, vous devez le faire via le TypeConstraint comme ceci :

[TypeConstraint(typeof(Button))]

Attention, si le type utilisé dans le Xaml n’est pas celui de la contrainte, il n’y aura pas d’erreur de Build. Vous aurez par contre un plantage au runtime.

En conclusion,

Voici donc 3 classes vraiment très pratique que je ne peux que vous recommander.

Je vous conseille également de regarder dans Microsoft Expression Blend 3 avant de créer votre propre Trigger ou Behavior car il en existe déjà quelques uns très pratique.

 

A très bientôt,

 

DjoDjo

Posté le dimanche 15 novembre 2009 13:43 par DjoDjo | 0 commentaire(s)

Classé sous :

[PRISM] Un peu d’auto pub n’a jamais fait de mal…

Je profite de mon blog pour annoncer que je serais speaker pour un Live Meeting le 29 octobre.

Sujet : Utiliser des modules pour construire des applications WPF et Silverlight (PRISM).
Description : Lorsqu’on a à sa disposition deux technologies d’interface utilisateur (UI) comme WPF et Silverlight écrites dans des langages similaires (XAML) mais qui tournent avec des ensembles différents en fonction des circonstances, quoi de plus logique que de vouloir n’écrire qu’une seule interface utilisateur pour pouvoir ensuite choisir la meilleure des options, ou les deux, moyennant un minimum d’effort. Cette session explique comment tirer au mieux parti de vos applications XAML et quelles décisions prendre, en matière d’architecture, pour que ces options soient disponibles dès le début du cycle de développement.

Je dois avouer que j’ai la pression… c’est en effet le premier Webcast que je fais, je vais donc vers l’inconnu !!

Si vous êtes intéressé, les inscriptions c’est par ici : http://msevents.microsoft.com/cui/WebCastEventDetails.aspx?EventID=1032424479&culture=fr-BE

Comme vous le remarquerez, le nom de l’orateur inscrit est Gill Cleeren (www.snowball.be), il s’agit ici d’une petite erreur qui sera très vite corrigée. Pour info, Gill Cleeren fait une présentation sur le même sujet en Neerlandais au début du mois (Pour la vidéo c’est ici).

Au plaisir de vous y retrouver !

DjoDjo

Posté le mardi 27 octobre 2009 20:21 par DjoDjo | 4 commentaire(s)

[Silverlight/WPF]Un contrôle très pratique,

Vous vous êtes déjà dit que créer un UserControl juste pour afficher un bloc UI était un peu dommage ? Vous rêvez de pouvoir utiliser du Xaml de la même façon qu’un png ? Alors vous aller adorer le XamlLoader !

Principe :

Le XamlLoader est un contrôle permettant de charger dynamiquement un fichier contenant du Xaml.

L’intérêt réside dans le fait que lorsqu’on désire avoir des icônes en mode vectoriel (comprendre en Xaml), la plupart du temps, on créera un contrôle avec un fichier de code behind vide… ou qui ne contiendrait que la « logique » destinée aux évènements souris.

Cela n’est maintenant plus nécessaire, le fichier Xaml à lui seul suffit. En effet, la description simple en Xaml du contenu de l’icône peut être placée dans un fichier au sein du projet et chargée dynamiquement.

Conditions :

Pour cela il faut respecter certaines conventions :

  • Le fichier doit être une « Page » du projet : pour ce faire, il suffit de faire un click droit sur le fichier et changer la « Build Action » de celui-ci en Page.
    properties
  • Ce fichier est au fait un fichier XML. A ce titre (et contrairement aux fichiers XAML normaux) il nécessite une entête XML
<?xml version="1.0" encoding="utf-8"?>
  • L’élément racine doit impérativement importer les namespaces nécessaires à l’interprétation des différents tags XAML
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Ici nous n’avons que le strict minium mais on peut bien sûr importer ce que l’on veut…

  • Il est aussi nécessaire de donner une taille fixe à l’élément racine. On pourrait comparer cela à ce que l’on fait avec une image de type Bitmap ; la taille est fixe.

Utilisation :

L’utilisation du XamlLoader est relativement simple, il suffit de le mettre dans le corps de votre Xaml et de lui donner le chemin d’accès au fichier à charger. Attention toutefois à lui donner ce chemin de façon absolue (/Sample;component/XamlImages/icon_upload.xaml). En effet, si vous donner le chemin du fichier relativement à partir du contrôle en cours d’édition, lorsque le XamlLoader essaiera de le charger, depuis (bien souvent une toute autre position dans la hiérarchie de fichier de votre application. Cela permet aussi d’avoir les fichiers Xaml dans un autre projet, et donc de partager facilement les icônes.

Le XamlLoader offre aussi les possibilités suivantes :

  • Une propriété « Stretch » empruntée à la ViewBox du Toolkit Silverlight qui permet de spécifier comment adapter la taille du contenu par rapport à la taille du container.
    En pratique, cela permet de spécifier le Stretch mode à :
    • None : peu importe la taille du XamlLoader, la taille de l’icône chargée sera celle spécifiée dans le fichier Xaml.
    • Fill : Ici on va adapter la taille du contenu à celle du contrôle, sans se préoccuper du ratio.
    • Uniform : On s’arrange pour prendre la taille la plus grande possible tout en respectant l’aspect ratio. On ne sortira donc jamais des limites du contrôle.
    • UniformToFill : Similaire à Uniform mais en remplissant complètement le XamlLoader. On dépassera donc toujours (sauf en cas d’une taille identique) soit en largeur soit en hauteur les limites.
  • Une propriété IsEnabled. Celle-ci fait essentiellement 2 choses :
    • Elle applique un GreyscaleEffect (trouvé sur le net et qui n’est autre qu’un pixel shader effect) à l’élément en lui-même permettant de griser l’icône.
    • Elle parcourt tous les enfants du contrôle afin de trouver tous les éléments de type Control et de mettre à jour les propriétés IsEnabled.

Comment ca marche ?

Pour faire charger et générer le Xaml, rien de bien sorcier, Silverlight nous offre cette fonctionnalité out of the box avec le XamlReader

Voici donc comment l’utiliser :

        public static object LoadFromXaml(Uri uri)
{
StreamResourceInfo streamInfo = Application.GetResourceStream(uri);
if ((streamInfo != null) && (streamInfo.Stream != null))
{
using (StreamReader reader = new StreamReader(streamInfo.Stream))
{
return XamlReader.Load(reader.ReadToEnd());
}
}
return null;
}

Pour ce qui est des la gestion de la taille, on utilise des Transformation pour s’adapter au Stretch et à la taille choisie.

        private void CheckSize()
{
double currentWidth = ActualWidth;
if (!double.IsNaN(Width))
if (Width > ActualWidth)
currentWidth = Width;
double currentHeight = ActualHeight;
if (!double.IsNaN(Height))
if (Height > ActualHeight)
currentHeight = Height;

if (loadedElement != null && loadedElement.Width > 0 && loadedElement.Height > 0 && currentWidth > 0 &&
currentHeight > 0)
{
double scaleX = (currentWidth/loadedElement.Width);
double scaleY = (currentHeight/loadedElement.Height);

double centerX = loadedElement.Width/2;
double centerY = loadedElement.Height/2;

loadedElement.SetValue(LeftProperty, (currentWidth/2) - (loadedElement.Width/2));
loadedElement.SetValue(TopProperty, (currentHeight / 2) - (loadedElement.Height / 2));

switch (Stretch)
{
case Stretch.None:

break;
case Stretch.Fill:
loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = scaleX,
ScaleY = scaleY,
CenterX = centerX,
CenterY = centerY
};
break;
case Stretch.Uniform:
double smallest = scaleY;
if (scaleX < scaleY)
smallest = scaleX;

loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = smallest,
ScaleY = smallest,
CenterX = centerX,
CenterY = centerY
};

break;
case Stretch.UniformToFill:
double biggest = scaleY;
if (scaleX > scaleY)
biggest = scaleX;

loadedElement.RenderTransform = new ScaleTransform
{
ScaleX = biggest,
ScaleY = biggest,
CenterX = centerX,
CenterY = centerY
};
break;
}
}

}

Et mes events alors ?

Comme je le disais au début de cet article, on utilise généralement un UserControl pour bénéficier du support des event et ainsi permettre de modifier l’état du contrôle. Avec Silverlight 3 on peut maintenant tirer parti des librairies Interactivity (Microsoft.Expression.Interactivity et System.Windows.Interaction) fournie dans le SDK de Blend 3.

Cela nous permet entre autre de faire ce qui suit.

  1. Créer les storyboard dont on a besoin :
            <Storyboard x:Key="FlipIn" >
    <DoubleAnimation Duration="0:0:0.5" To="45" Storyboard.TargetName="projection" Storyboard.TargetProperty="RotationX" />
    </Storyboard>
    <Storyboard x:Key="FlipOut" >
    <DoubleAnimation Duration="0:0:0.5" To="0" Storyboard.TargetName="projection" Storyboard.TargetProperty="RotationX" />
    </Storyboard>
  2. Ensuite des triggers pour démarrer les storyboards:
        <Interactivity:Interaction.Triggers>
    <Interactivity:EventTrigger EventName="MouseEnter">
    <Media:ControlStoryboardAction Storyboard="{StaticResource FlipIn}" ControlStoryboardOption="Play" />
    </Interactivity:EventTrigger>
    <Interactivity:EventTrigger EventName="MouseLeave">
    <Media:ControlStoryboardAction Storyboard="{StaticResource FlipOut}" ControlStoryboardOption="Play" />
    </Interactivity:EventTrigger>
    </Interactivity:Interaction.Triggers>
Je prépare un petit post sur les Triggers et Behaviors pour les jours à venir car ce que je fais ici est assez simple mais ces derniers sont bien plus puissants qu’il n’y parrait.

Résultat:

image image
Cette belle télévision provient de la : http://advertboy.wordpress.com/blendcandy/

Conclusion :

Personnellement j’use et j’abuse de ce contrôle dans beaucoup de situations.

Ce n’est pas encore parfait et de multiples améliorations pourrait y être amenées mais déjà tellement pratique.

J’espère qu’il vous simplifiera la vie tout autant qu’à moi !

Enfin, je vous prépare égallement un post tirant parti des possibilités de ce contrôle…

A bientôt !

DjoDjo

Technorati Tags: ,

PS : Je vous mets les sources rapidement !! Dès que je trouve comment faire…

EDIT : Les sources sont maintenant attachées au post ;-)


Posté le lundi 19 octobre 2009 21:14 par DjoDjo | 0 commentaire(s)

Classé sous : ,

Et un de plus !

Salut à tous,

Je me présente, je m’appelle Georges Legros (DjoDjo), j’ai 23 ans et je bosse actuellement pour Shift

Je travaille depuis quelques temps avec les technologies Microsoft et plus particulièrement avec WPF et Silverlight.

Ce blog sera composé de retour d’expériences, de descriptions de contrôles réutilisables et d’analyses de technos et Frameworks.

Bref, j’espère qu’il vous sera non seulement utile mais également agréable à lire !

A très bientôt!

DjoDjo

PS : Merci à Nix pour l’ouverture ultra rapide de ce blog!


EDIT : Ma situation professionnelle ayant changé, je me permet de modifier les informations écrites ici....

Posté le jeudi 15 octobre 2009 19:32 par DjoDjo | 1 commentaire(s)



Les 10 derniers blogs postés

- Office 365: Script PowerShell pour assigner des droits Full Control à un groupe défini par Blog Technique de Romelard Fabrice le 04-30-2017, 09:22

- SharePoint 20XX: Script PowerShell pour exporter en CSV toutes les listes d’une ferme pour auditer le contenu avant migration par Blog Technique de Romelard Fabrice le 03-28-2017, 17:53

- Les pièges de l’installation de Visual Studio 2017 par Blog de Jérémy Jeanson le 03-24-2017, 13:05

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22