Archives de Catégorie: User Control

AutoCompleteBox pour Windows Phone 7

EDIT du 16/11 : Une AutoCompleteBox est maintenant disponible dans le toolkit Silverlight pour WP7 : http://silverlight.codeplex.com/releases/view/55034

Les solutions existantes

Intégrées à WP7

Il n’existe pas à proprement parlé de contrôle de type autocompletebox (comme on peut le retrouver dans le Silverlight Toolkit) pour WP7.
Bien sûr, il existe le classique T9 accessible sur n’importe quelle textbox grâce à l’input scope :

T9
<TextBox InputScope= »Text »/>

Mais il ne nous permet pas d’afficher une liste personnelle d’éléments que l’on pourrait récupérer ensuite.

Dans les toolkits de Silverlight

WP7 est basé sur Silverlight 3, nous avons donc la possibilité d’utiliser l’AutoCompleteBox du toolkit (http://silverlight.codeplex.com/). Il suffit d’ajouter une référence à System.Windows.Controls.Input disponible dans le dossier C:\Program Files (x86)\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.Input.dll.

On obtient ce type de contrôle :
Illustration AutoCompleteBox Toolkit

Vous aurez besoin d’utiliser la propriété ItemsSource pour spécifier la source de données (comme une ListBox normale) et ValueMemberPath pour spécifier la propriété à filtrer (ex : vous avez une collection de personnes ayant une propriété « nom », ValueMemberPath= »nom » vous permettra d’afficher une liste des noms commençant par ce que vous entrez).

Le template nécessitera quelques modifications selon les goûts de chacun. Personnellement, j’avais rapproché le menu déroulant de la textbox et ajouté des marges latérales. Mais plusieurs problèmes subsistent :

– Contrairement au T9, le menu déroulant est vertical et passe sous le clavier quand le contrôle se retrouve plus bas dans la page.

– Si on veut faire défiler la liste déroulante, le menu se referme automatiquement.

– Par contre, lors de la sélection d’un élément, le menu ne se ferme pas et nécessite donc un second clic peu ergonomique.

L’AutoCompleteBox du toolkit n’est donc pas encore adapté à l’environnement WP7.

Création d’une AutoCompleteBox adaptée

Après quelques recherches, je suis tombés sur une solution intéressante qui m’a poussée à créer ma propre AutoCompleteBox(1). L’auteur utilisait trois textbox, une pour entrer la donnée et les deux autres pour afficher des propositions.
Le système restait simple pour un affichage de texte. Mais j’avais besoin d’une solution générique, ainsi que de pouvoir récupérer l’objet sélectionné dans n’importe quelle ObservableCollection.
Je me suis donc lancé dans la création de l’User Control qui fait l’objet de cet article. Vous trouverez les sources en lien(2).

De quoi avons-nous besoin :

– De pouvoir binder une source de données (donc créer une DependencyProperty ItemsSource)
– Comme sur l’autre AutoCompleteBox, de pouvoir utiliser un filtre pour savoir quelle propriété comparer (ce sera une DependencyProperty nommée PropertyFilter)
– De récupérer l’élément sélectionné (au travers du classique SelectedItem)
– Et ne pas oublier de récupérer le texte entré dans le cas où l’utilisateur ne trouve pas son élément dans la collection (DepencyProperty Text)

Comment cela va fonctionner :

Nous allons nous baser sur l’AutoCompleteBox du toolkit et faire un contrôle composé d’une textbox au-dessus d’une border contenant une listbox.

ex: Structure du contrôle
Quand l’utilisateur entre du texte, la textbox obtient le focus et son événement GotFocus est appelé. Nous en profitons pour afficher le menu déroulant. De même, lorsqu’elle perd le focus, l’événement LostFocus nous permet de faire disparaître le menu déroulant.

Et le dernier événement de la textbox que nous allons utiliser va nous permettre de parcourir les éléments de notre liste. Grâce à TextChanged, nous pouvons récupérer le texte entré et le comparer aux éléments (potentiellement filtrés grâce à PropertyFilter !) de notre collection…

Problème ! L’ItemsSource doit être un IEnumerable pour correspondre à tous types de Collections. Et nous ne pouvons donc pas connaître le type de la collection et encore moins le type des éléments. Pour palier à cela nous allons utiliser la réflexion sur notre collection.

Nous allons utiliser une ObservableCollection nommée LB_ItemsSource pour stocker les éléments désirés et les afficher dans la ListBox. A chaque fois que le texte sera modifié on réalisera un clear() sur cette ObservableCollection pour supprimer les données non-pertinentes.
On créé un objet id et un int itemNumber pour pouvoir parcourir la liste. Le premier sera incrémenté jusqu’au nombre maximum d’item (représenté par itemNumber).

On récupère ensuite la collection d’items au sein de notre IEnumerable en parcourant ses DefaultMembers.
On peut ainsi rechercher nos éléments puis notre propriété et la comparer à la valeur entrée. Tous les éléments correspondants à notre texte sont ajoutés à LB_ItemsSource (liée à la ListBox déroulée).

private void
textBox1_TextChanged(object sender, TextChangedEventArgs e)

{

Text = textBox1.Text;

object[] id = newobject[1];

intitemNumber = (int)ItemsSource.GetType().GetProperty(« Count »).GetValue(ItemsSource, null);

LB_ItemsSource.Clear();

PropertyInfo items = null;

MemberInfo[] defaultMembers = ItemsSource.GetType().GetDefaultMembers();

for (int i = 0; i < defaultMembers.Length;
i++)

if (defaultMembers[i].MemberType == MemberTypes.Property)

items
= (PropertyInfo)defaultMembers[i];

if (SelectedItem!=null &&
!SelectedItem.GetType().GetProperty(PropertyFilter).GetValue(SelectedItem, null).ToString().Equals(textBox1.Text))

SelectedItem = null;

for(int i = 0;i< itemNumber;i++)

{

id[0]
= (object)i;

object item = items.GetValue(ItemsSource, id);

object value = PropertyFilter
!= null ? item.GetType().GetProperty(PropertyFilter).GetValue(item, null) :
item;

if(!String.IsNullOrEmpty(textBox1.Text))

{

foreach (string name invalue.ToString().ToLower().Split(‘ ‘))

if (name.StartsWith(textBox1.Text.ToLower()))

{

LB_ItemsSource.Add(item);

break;

}


}

}

}

 

C’est mieux, mais toujours pas adapté à un usage tactile !

En effet, de cette manière nous obtenons une AutoCompleteBox similaire à celle du toolkit sans les inconvénients d’ouverture / fermeture du menu déroulant mais l’affichage reste petit et passe sous le clavier selon son positionnement.

Qu’à cela ne tienne, inspirons-nous du système de type T9.
Transformons la ListBox en liste horizontale en modifiant l’orientation de son StackPanel. Supprimons le scroll vertical et activons celui horizontal.
Modifions l’affichage d’un élément de manière à grossir la police et à ajouter des ellipse en guise de séparateur.

Et voici une AutoCompleteBox fonctionnelle pour WP7 !

Illustration finale AutoCompleteBox

<UserControl.Resources>

<ItemsPanelTemplate x:Key= »ItemsPanelTemplate »>

<StackPanel
Orientation
= »Horizontal »/>

</ItemsPanelTemplate>

<DataTemplate x:Key= »GeneratedItem »>

<StackPanel Orientation= »Horizontal » Margin= »0″>

<Ellipse
Fill
= »Black »
Height
= »17″
Stroke
= »Black »
Width
= »17″
Margin
= »10,0« VerticalAlignment= »Center »/>

<TextBlock Text= »{Binding} »
Style
= »{StaticResource
TextBlockStyle2
} »
Margin
= »0″
Foreground
= »{StaticResourcePhoneBackgroundBrush} »FontSize= »{StaticResourcePhoneFontSizeExtraLarge} »VerticalAlignment= »Center » Height= »Auto »/>

</StackPanel>

</DataTemplate>

</UserControl.Resources>

 

<StackPanel x:Name= »UC_Layoot »>

<TextBox
Name
= »textBox1″VerticalAlignment= »Top » Width= »Auto »GotFocus= »textBox1_GotFocus »TextChanged= »textBox1_TextChanged »LostFocus= »textBox1_LostFocus » Style= »{StaticResourceProjects_TextboxClient} »
/>

<Border x:Name= »DisplayBorder »BorderBrush= »Black »BorderThickness= »1,0,1,1″ Margin= »12,-12,12,4″ Visibility= »Collapsed »>

<StackPanel>

<ListBox x:Name= »ListItems »SelectionChanged= »ListItems_SelectionChanged« 
Background
= »#FFAAAAAA »ItemsPanel= »{StaticResourceItemsPanelTemplate} »ScrollViewer.HorizontalScrollBarVisibility= »Auto »ScrollViewer.VerticalScrollBarVisibility= »Disabled »ItemTemplate= »{StaticResourceGeneratedItem} »
/>

</StackPanel>

</Border>

</StackPanel>

 

 

Par JCVASSELON

Référence :

(1) : Sujet sur le forum MSDN : http://social.msdn.microsoft.com/Forums/en-US/windowsphone7series/thread/66ba525b-9d59-4a75-8495-1e1a1f6081fc

(2) Source du projet : http://www.megaupload.com/?d=XY4IX467