Browsed by
Month: April 2011

Un contrôle bubbleText

Un contrôle bubbleText

Suite à une demande de David Poulin qui souhaitait avoir un contrôle semblable aux bulles de l’application SMS de windows phone 7, j’ai pris 5 minutes pour lui en développer un.

Les possibilités

  • définir la position de la pointe de la bulle : en haut à gauche ou en bas à droite
  • changer le background : par défaut la couleur ‘accent’
  • changer le foreground pour le texte
  • modifier le contenu pour afficher ce que l’on souhaite : une image, un bouton, etc…

 

Comment l’utiliser

Ajoutez une référence à la dll Huyn.BubbleText.

Ajoutez xmlns:huyn=’clr-namespace:Huyn.BubbleText;assembly=Huyn.BubbleText’ dans votre page.

Ajoutez dans votre page le contrôle :

<huyn:BubbleText Margin="0,5" Content="Normal Bubble"/>

Exemple d’utilisation

<huyn:BubbleText Margin="0,5" CornerPosition="BottomRight"  Content="Normal Bubble with bottom corner"/>
<huyn:BubbleText x:Name="DatedBubble" Margin="0,5" Content="Dated Bubble"/>
<huyn:BubbleText  Background="Yellow"  Foreground="Red" Content="Colored bubble"  Margin="0,5"/>
<huyn:BubbleText Margin="0,5">
<Image Source="steve-ballmer.jpg"/>
</huyn:BubbleText>

 

Télécharger Huyn.BubbleText

Préparer sa certification Windows Phone 7 : partie 1

Préparer sa certification Windows Phone 7 : partie 1

Depuis le 19 avril, nous avons la possibilité de passer la certification 71-599 en version béta .

Je l’ai pour ma part passé le 21. Afin de vous aider à préparer votre certification, je vais compiler sur plusieurs articles les différents points indispensables à maîtriser pour la passer dans de bonnes conditions.

Pour chaque partie je vais lister les différents points à maîtriser à titre d’exemple, toutefois des questions peuvent être abordées sur des points non listés.

Nous allons détailler dans cet article les différents points qui seront à étudier pour passer la première partie de cet examen : concevoir des stratégies d’accès aux données. Nous allons donc aborder les problématiques de réseau, de données persistantes et de communication.

Cette première partie  représente 19% de l’examen, soit 8 – 9 questions (sur une base de 47 questions)

Concevoir des stratégies d’accès aux données

Envoyer et recevoir des données.

Concevoir une stratégie de stockage de données.

Gestion de la bande passante et détection de connexion réseau.

 

Repasser l'écran du HTC Mozart 7 en 32bits

Repasser l'écran du HTC Mozart 7 en 32bits

HTC Mozart avant et après nodo (comparé à un Omnia 7)
Avant de commencer, cette manipulation est uniquement réservée aux personnes ayant un compte développeur.

Si comme moi, la mise à jour Nodo a détérioré significativement la qualité de l’affichage des dégradés, voici une solution pour repasser votre écran comme avant.

La raison

A priori, ce n’est pas la mise à jour du système qui est en cause, mais le firmware HTC qui était livré avec. En effet ce dernier remet vos paramètres d’affichage en 16 bits au lieu de 24/32 bits, ce qui à pour conséquence d’afficher des aplats de couleurs dans les dégradés (les possesseurs de Omnia 7 connaisse déjà ce soucis).

La solution

Tout comme son grand frère ‘Windows’, le téléphone possède une base de registres. C’est dans cette dernière qu’est stocké les informations sur les applications, les réglages et notamment celui de la profondeur de couleurs de l’écran.

Nous allons donc éditer cette base pour changer la profondeur d’affichage de l’écran.

Prérequis

Pour modifier cette base de registre, il nous faut un éditeur. Je conseillerais “Registry editor” par Julien Schapman, qui fonctionne plutôt bien et est facile d’utilisation. L’application est téléchargeable à l’adresse suivante : téléchargez.

Pour l’installez, passez par l’outils ‘Application Deployment’ fourni avec le SDK.

Modification du registre

Nous allons modifier 3 entrées de registres.

Pour cela, lancez ‘Registry editor’, et allez dans le répertoire suivant :
HKEY_LOCAL_MACHINEDriversDisplayPrimary

Dans ce répertoire, modifiez les valeurs suivantes :

BPP : 16 -> 32
PanelBPP : 16 -> 24
PrimBPP : 16 -> 32

(pensez à bien cliquez sur OK après avoir modifié vos valeurs)

Puis redémarrez immédiatement votre téléphone.

Et voila, vous retrouverez de beaux dégradés maintenant !

Calculer l'acronyme d'une suite de mot

Calculer l'acronyme d'une suite de mot

Pour l’application TVShow, je souhaitais calculer l’acronyme d’un nom d’une série un peu long, comme par exemple  :

  • How I Met Your Mother ==> HIMYM
  • Saturday Night Live ==> SNL

var accro=
  TVShow.Title.Split(new char[] { ' ' }).Select(s=>s[0].ToString()).Aggregate((a,b)=>a+b).ToUpper();

On découpe la phrase par rapport aux espaces, on récupére la première lettre de chaque mot, on les combine et enfin on convertit le résultat en majuscules.

Si vous n’êtes pas familier avec la fonction Aggregate, c’est une fonction d’accumulation sur une séquence. Techniquement, elle prend un premier item et essaie de le combiner avec un second (le résultat doit être du même type que les items) et ce jusqu’à la fin de l’énumération.

C’est donc pour cela que j’ai converti le caractère s[0] en String, afin d’avoir des items de type String que je pourrais accumuler après.

LimitedObservableCollection : Code complet

LimitedObservableCollection : Code complet


[csharp]
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;

namespace Huyn
{

public class LimitedObservableCollection: INotifyCollectionChanged, IEnumerable
{
private int _limit;
private ObservableCollection _observable;

public LimitedObservableCollection(ObservableCollectiono, int limit)
{
_observable = o;
_limit = limit;
o.CollectionChanged += new NotifyCollectionChangedEventHandler(o_CollectionChanged);
}

void o_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{

case NotifyCollectionChangedAction.Replace:
if (e.NewStartingIndex >= 0 && e.NewStartingIndex < _limit)
{
OnCollectionChanged(e);
}
break;
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex >= 0 && e.NewStartingIndex < _limit)
{
OnCollectionChanged(e);
if (_observable.Count > _limit)
{
NotifyCollectionChangedEventArgs eRemove =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _observable[_limit], _limit);
OnCollectionChanged(eRemove);
}
}
break;
case NotifyCollectionChangedAction.Reset:
OnCollectionChanged(e);
break;
case NotifyCollectionChangedAction.Remove:
{

if (e.OldStartingIndex >= 0 && e.OldStartingIndex < _limit)
{
OnCollectionChanged(e);
if (_observable.Count >= _limit)
{
NotifyCollectionChangedEventArgs eAdd =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _observable[_limit - 1], _limit - 1);
OnCollectionChanged(eAdd);
}
}
break;
}
}
}

public IEnumeratorGetEnumerator()
{
return _observable.Take(_limit).GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _observable.Take(_limit).GetEnumerator();
}

protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
try
{
CollectionChanged(this, e);
}
catch (System.NotSupportedException)
{
NotifyCollectionChangedEventArgs alternativeEventArgs =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(alternativeEventArgs);
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
}

 

[/csharp]

LimitedObservableCollection : partie 2

LimitedObservableCollection : partie 2

 

Suite à la partie 1 de la création de la classe LimitedObservableCollection, nous allons dans cet article implémenter les 4 cas types d’évènements vu précédemment.

 

Nous allons compléter la méthode


Le remplacement d’un item

 

 

Nous allons dans un premier temps vérifier que l’index de notre item est entre zéro et limit et si c’est le cas, on fera suivre l’événement

 

case NotifyCollectionChangedAction.Replace:
if (e.NewStartingIndex >= 0 && e.NewStartingIndex < _limit)
{
OnCollectionChanged(e);
}
break;

L’ajout d’un item


Comme précédemment, nous allons vérifier que l’index de notre élément est dans notre périmètre (entre 0 et limit) et si c’est le cas, alors on vérifie que l’on ne dépasse pas la limite après l’ajout de cet élément, et si c’est le cas, nous enlevons l’élément en trop

case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex >= 0 && e.NewStartingIndex < _limit) 	{ 		OnCollectionChanged(e); 		if (_observable.Count > _limit)
{
NotifyCollectionChangedEventArgs eRemove=
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _observable[_limit], _limit);
OnCollectionChanged(eRemove);
}
}
break;

La remise à zéro

Ce cas est le plus simple à gérer, il suffit simplement de faire suivre la notification sans aucun traitement

case NotifyCollectionChangedAction.Reset:
OnCollectionChanged(e);
break;

La suppression d’un élément


Inversement au cas de l’ajout, il faudra faire attention à ‘ajouter’ un élément (s’il existe) si on supprime un élément dans notre périmètre afin de garder le bon nombre d’élèment.

case NotifyCollectionChangedAction.Remove:
{

if (e.OldStartingIndex >= 0 && e.OldStartingIndex < _limit) 		{ 			OnCollectionChanged(e); 			if (_observable.Count >= _limit)
{
NotifyCollectionChangedEventArgs eAdd =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _observable[_limit - 1], _limit - 1);
OnCollectionChanged(eAdd);
}
}
break;
}

Et voilà, nous avons maintenant une ObservableCollection disposant d’une limite maximum d’élément énumérable.

Code source entier

LimitedObservableCollection : partie 1

LimitedObservableCollection : partie 1

Problématique

Lorsque l’on souhaite limiter le nombre d’Item dans une listbox, on écrit :

dans le viewmodel

public get IEnumerable Last10Messages{
return Messages.Take(10);
}

et dans le xaml


<ListBox ItemSource="{Binding Messages}" />

<ListBox ItemSource="{Binding Last10Messages}" />

Le code fonctionne, toutefois, si ‘Messages’ était une ObservableCollection, on perd ici tous ses avantages. En effet, si vous ajoutez un nouvel item à messages, il sera automatiquement ajouté dans la première listbox mais pas dans seconde.

En effet, la fonction ObservableCollection::Take va retourner une IEnumerable et non une ObservableCollection, cela signifie que pour mettre à jour la table ListLimited, il faudra lancer un OnPropertyChanged de ‘Last10Messsages’, mais là, toute la table se remettra à jour et non juste une suppression/ajout des modification (on perd donc considérablement en performance).

Solution : LimitedObservableCollection

Nous allons donc créer une nouvelle classe, hébergeant un ObservableCollection, mais qui avoir pour fonction de filtrer les notifications et de limiter l’énumération sur la source.

Fonctionnement

 

Création

La solution serait donc de continuer à limiter le nombre d’item mais d’exposer une ObservableCollection.

Commençons donc par créer notre classe

public class LimitedObservableCollection
{

}

Dans le constructeur, deux paramètres seront obligatoires, une ObservableCollection correspondant à notre source et un int correspondant au nombre d’items que l’on souhaite au maximum.

private ObservableCollection _observable;
public int _limit;

public LimitedObservableCollection(ObservableCollection source,int limit)
{
_observable = source;
_limit = limit;
}

IEnumerable

Afin d’accéder aux données de notre collection, il faut implémenter l’interface IEnumerable :

public IEnumerator GetEnumerator()
{
return _observable.Take(_limit).GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _observable.Take(_limit).GetEnumerator();
}

Au niveau fonctionnel, on a l’équivalent de notre cas d’étude précédent, on récupère bien une liste de X élements, mais nous ne récupérons pas d’évènement lors de sa modification.

Récupération des évènements de modification de notre source

ObservableCollection implémente l’interface INotifyCollectionChanged.

Cette dernière capte :

  • le remplacement d’un item par un autre
  • l’ajout d’un item
  • la suppression d’un item
  • la remise à zéro de la collection

On va gérer ces 4 types d’évènements, mais avant enregistrons nous :

dans le constructeur :

source.CollectionChanged += new NotifyCollectionChangedEventHandler(o_CollectionChanged);

Notre fonction :

void o_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{

case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Add:
break;
case NotifyCollectionChangedAction.Reset:
break;
case NotifyCollectionChangedAction.Remove:
break;
}
}

INotifyCollectionChanged

On vient d’utiliser l’interface INotifyCollectionChanged de notre ObservableCollection, on va maintenant implémenter nous même l’interface INotifyCollectionChanged afin de pouvoir lancer nos évènements.

Ce que l’on va faire est plutôt simple, on va récupèrer les évènements de notre source, les analyser et envoyer nos propres évènements.

Il faut donc ajouter l’évènement CollectionChanged à notre classe et on implémentera une fonction OnCollectionChanged qui nous aidera à lancer cet évènement avec une gestion des erreurs.

public event NotifyCollectionChangedEventHandler  CollectionChanged;

protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
try
{
CollectionChanged(this, e);
}
catch (System.NotSupportedException)
{
NotifyCollectionChangedEventArgs alternativeEventArgs =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(alternativeEventArgs);
}
}

La suite : LimitedObservableCollection : partie 2

Revue des blogs #1

Revue des blogs #1

Chaque semaine, je vais essayer de vous faire une sélection d’articles de blogs intéressants. On parlera beaucoup de .Net et de la XAML family (Silverlight, WPF et WP7), mais pas que ! Pour cette première semaine, comment créer ses microsoft tags, une web TV, l’avenir de silverlight, F# et la communauté des développeurs windows phone 7 qui commence à bien grandir !

Read More Read More

Si Grégoire codait en C# avec Linq, voilà ce que ça donnerait

Si Grégoire codait en C# avec Linq, voilà ce que ça donnerait


A force d’enseigner Linq, je voulais changer un peu, voila donc une façon originale de l’apprendre  via les paroles de la chanson “Toi+Moi” façon C# 😀 (et il a raison, la chanson est bien niaise)

 

 

 

 


void Launch()
{
var group1 = GetGroup((p) => p.InsoucianceAsync());
Action1(group1);
var group2 = GetGroup((p) => p.LuckyDay = DateTime.Now.Date);
Action2(group2);
group1 = GetGroup((p) => p.InsoucianceAsync());
Action3(group1);
group2 = GetGroup((p) => p.LuckyDay = DateTime.Now.Date);
Action4(group2);
group1 = GetGroup((p) => p.InsoucianceAsync());
group2 = GetGroup((p) => p.LuckyDay = DateTime.Now.Date);
var group3 = GetGroup((p) => { while (true) { Danse.Add(p); } });
}

private IEnumerable GetGroup(Action callback = null)
{
var firstGroup = (from person in People
where person.Id == YourId || person.Id == MyId
|| (person.Id != MyId && person.Id != YourId)
|| person.DoWant()
select person
).Distinct();
var secondGroup = (from person in People
where person.Id == HimId || person.Id == HerId
|| !person.Friends.Any()
select person
).Distinct();
var group = firstGroup.Union(secondGroup);
Danse.Concat(group);
if (callback != null)
group.AsParallel().ForAll(p => callback(p));
return group;
}

private void Action1(IEnumerable group)
{
if (group.Count() == 2 || group.Count() == 1000)
{
Actions.ForEach(
action => { action.CanHappen = action.Possible = true; });
var newPosY = Reve.Position.Y - delta;
var newPosX = Greve.Position.X + delta;
group.AsParallel().ForAll(person =>
{
person.Position = new Geocoordinate() { Y = newPosY, X = newPosX };
});
}
}

private void Action2(IEnumerable group)
{
if (!RetrieveDesire() || RetrieveStrength() || RetrieveBravery())
return;
var me = (from person in People where person.Id == MyId select person).First();
var withoutme = group.Except(new List() { me });
withoutme.AsParallel().ForAll(p => p.WaitOne());
Cold.SetAsMiracle();
Fear.SetAsMiracle();
Misfortune.Dispose();
me.WaitOne();
withoutme.AsParallel().ForAll(p => p.Release());
}

private void Action3(IEnumerable group)
{
var me = (from person in People where person.Id == MyId select person).First();
var mysong = Songs.First(p => p.Owner == me);
if (!(mysong.IsNaive && mysong.Style == SongStyle.Stupid && mysong.IsHarmless))
throw new Exception("can't occur");

var exWorld = World.Clone();
World.Give(mysong);
if (World.Equals(exWorld))
RoundDancing.GiveInvitation(group);
}

private void Action4(IEnumerable group2)
{
Hope.TakeAll();
Ardor.TakeAll();
var me = (from person in People where person.Id == MyId select person).First();

Hope.Take(me.Arms);
Hope.Take(me.Heart);
Hope.Take(me.Shoulders);
Hope.Take(me.Back);
Ardor.Take(me.Arms);
Ardor.Take(me.Heart);
Ardor.Take(me.Shoulders);
Ardor.Take(me.Back);
var you = (from person in People where person.Id == YourId select person).First();

var innerObject = me.Look(you.Eyes);
return innerObject.Exists(inobj => inobj is Star) && !me.IsSubmitted() && !you.IsSubmitted() && me.IsHappy() && you.IsHappy();
}

Problème d'animation d'un panorama au démarrage

Problème d'animation d'un panorama au démarrage

Habitué à l’animation de démarrage du panorama au lancement d’une page sur Windows Phone 7, j’ai été surpris ce matin de constater que mon dernier prototype affichait directement le panorama sans animation.

J’ai donc cherché quel était la différence entre ce projet et mes autres, en reprenant la même page, mais en l’incluant dans un autre projet, l’animation se faisait bien. Au fur et à mesure, je remarque que si j’ajoutais une BitmapImage dans les ressources de l’application, cela semblait ‘activer’ cette animation. Evidemment ceci est un effet de bord et nullement la solution à notre question, le problème est donc ailleurs.

Si vous souhaitez le  reproduire, rien de plus simple. Créez un projet de type “Windows Phone Panorama Application” avec Blend ou Visual Studio et lancez le : pas d’animations, ajoutez une ressource image bidon dans les ressources de App.xaml

<BitmapImage x:Key="Bidon" UriSource="PanoramaBackground.png"/>

L’animation s’affiche bien…

En fait, le problème est plutôt simple.

Au lancement de l’initialisation de l’application (dans la méthode App::InitializePhoneApplication), on créé une nouvelle PhoneApplicationFrame et on s’enregistre sur l’événement Navigated.

RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;

Quand on reçoit l’évènement Navigated, on affecte le RootVisual au PhoneApplicationFrame que l’on vient de créer, l’écran n’affiche donc plus la splashscreen, mais notre PhoneApplicationFrame.

Revenons donc à notre problème, en réalité l’animation d’ouverture du panorama a bien eu lieu, mais elle s’est déroulée alors que le RootVisual n’était pas encore affecté à notre Frame, donc visuellement, on ne voit que l’état final, après animation. Le fait de rajouter une BitmapImage dans les ressources de l’application, rallonge légèrement le temps d’initialisation de notre page et donc, l’animation à eu lieu après l’affectation du RootVisual.

Solutions

On affiche plus tôt notre page

La première solution est donc de travailler avec l’événement Navigating et non Navigated, afin d’affecter le RootVisual à ce niveau là

private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigating += CompleteInitializePhoneApplication;
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
phoneApplicationInitialized = true;
}
private void CompleteInitializePhoneApplication(object sender, NavigatingCancelEventArgs e)
{
if (RootVisual != RootFrame)
RootVisual = RootFrame;
RootFrame.Navigating -= CompleteInitializePhoneApplication;
}

Précharger vos images pour retarder le lancement

Comme on vient de le voir, ajouter des ressources dans l’application retarde légèrement l’affichage.

Prenez une de vos images (comme l’image de  fond du panorama) et mettez là dans les ressources de votre application. Remplacez ensuite la propriété Background par une référence à votre ressource, et voilà !

 

Merci à Samuel Blanchard qui m’a aidé à résoudre ce mystère