Wilfried Woivré & .Net

Customiser une ListBox en WPF

NOVE29

Il est très courant en WPF que l’on doivent redéfinir le rendu des composants de base afin d’avoir une interface plus ergonomique pour l’utilisateur.

Prenons par exemple le cas d’une ListBox de personne, qui ressemblerait à peu près à ça :

image

Pour ce code XAML :

<ListBox ItemsSource="{Binding}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} {1}">
                        <Binding Path="FirstName" />
                        <Binding Path="LastName" />
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Maintenant, on va supposer que le client veut que tous les Items soit placé, non pas l’un en dessous de l’autre, mais disperser au niveau de la fenêtre selon différents critères. Il voudrait donc que l’interface ressemble à quelque chose de ce genre :

image

Donc là on voit clairement que selon les besoins des spécifications, les items de la liste ne sont ni vraiment ordonnées, mais plus vraisemblablement placé au hasard dans l’application. Cependant on a tout de même bien affaire à une liste d’élément.

Nous avons donc plusieurs choix qui s’offre à nous, le premier est de créer un Canvas, ou l’on ajoute les éléments 1 à 1 en code behind. Ce choix peut être pas trop mal, mais va de ce fait nécessité un peu de code, et la maintenance sur cet ajout de composant peut rapidement devenir compliqué, surtout si vous voulez “binder” les différents champs.

Notre deuxième choix serait donc de customiser la ListBox, ce qui permettrait uniquement grâce au binding de placer nos Items, sans nous embêter, et de plus nous pourrons rendre ça plus facilement dynamique. Donc, pour cela, on va commencer par faire des tests, on va changer la propriété ItemsPanelTemplate, pour y mettre un Canvas.

        <ListBox ItemsSource="{Binding}">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>

Puis après, nous allons ajouter deux propriétés à notre objet Personne afin d’y spécifier les coordonnées X et Y de l’item dans la ListBox, notons qu’on pourrait ajouter ces propriétés d’une autre manière, mais là ce n’est qu’une démonstration….

Ceci fait, nous allons maintenant lier les positions aux propriétés Canvas.Top et Canvas.Left de notre TextBlock. Alors déjà premier petit problème, l’intellisense ne nous fournit pas ces propriétés dans notre DataTemplate, on peut donc se dire que cela provient d’un bug de l’Intellisense, après tout Visual Studio 2008 et le XAML n’apporte pas beaucoup d’aide là ou il le faudrait. Donc on va tout de même le taper et voir si ça compile.

                <DataTemplate>
                    <TextBlock Canvas.Top="{Binding Pos.Y}" Canvas.Left="{Binding Pos.X}">
                        <TextBlock.Text>

 

Et là c’est magique, Visual Studio compile correctement le projet, ne signale aucune erreur, n’annonce pas de problème de binding dans la sortie. Mais par contre l’application quand à elle ne marche pas comme on le souhaite, car on obtient ceci :

image

On voit ici très bien qu’il ne prend pas en compte nos coordonnées, on se dit maintenant que Visual Studio avait peut être raison de ne pas nous proposer les propriétés Canvas.Left et Canvas.Top sur notre TextBlock.

Après un peu de réflexion, on se dit qu’il faudrait essayer de définir un style à notre Item, et là encore, la ListBox, est bien équipé pour ça, avec sa propriété ItemContainerStyle.

            <ListBox.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding Pos.X}" />
                    <Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
                </Style>
            </ListBox.ItemContainerStyle>

 

On enlève maintenant nos propriétés de la TextBlock , vu que visiblement elles ne fonctionnent pas, et puis on relance notre application. On obtient donc ainsi notre joli fenêtre :

image

Alors, effectivement comme ça on peut ne pas trop voir l’utilité de repositionner chacun des Items, en fait c’est parce que pour mon projet Imagine Cup, je suis en train de réaliser une ListBox ou les items sont disposés sous forme de cercle. Mais je ne vous dévoile pas tout !

D’ailleurs j’en profite pour passer une annonce, si vous connaissez un étudiant sur Paris qui est intéressé par les technologies comme le XAML, ainsi que l’ergonomie et le design d’application, notre équipe recherche quelqu’un, vous pouvez donc m’envoyer un mail à wilfried.woivre[at]gmail.com.

Remonter

Silverlight 3.0 et les .Net RIA Services

AOÛT12

Dans la continuité d’un de mes articles sur Silverlight et les différents moyens d’accéder aux données soit via l’utilisation du JSon, ou par des services Web (WCF …). Il en manquait inévitablement un sur les .Net RIA Services. Alors les .Net RIA Services en deux mots, qu’est-ce que c’est, c’est une nouvelle mouture d’ADO.Net pour Silverlight. En effet, lors de la conception d’application Silverlight, on avait vu que cela péchait pour récupérer les données, vu que Silverlight est une technologie cliente. Il fallait donc, comme le montre le schéma ci-dessous  pour accéder aux données contenues dans notre base depuis notre interface, passer par le code métier et ensuite, via un service arrivé à notre base de données pour effectuer la requête. C’est donc ce que l’on appelle une architecture n-tiers, facile à implémenter et facilement maintenable. image Cependant ce découpage est bien souvent pas respecté pour beaucoup de projets, il n’est donc pas rare de voir les différentes parties clientes mélangées, ainsi que le côté serveur pour des questions de rapidité de développement. On a donc généralement une architecture de ce type pour finir. image Cette facilité de développement, j’avoue peut être intéressante dans l’instant lorsque l’on n’a absolument pas l’habitude de bien séparer les diverses couches d’une application, ou que l’on ne veut pas implémenter de Design Pattern (comme le MVVM) dans son projet en plus. Néanmoins, la reprise de tel projet par la suite est d’autant plus dur … Alors maintenant, voyons comment nous structurons nos données avec .Net RIA Services image Bon maintenant que nous avons vu comment est construit une application Silverlight utilisant les .Net RIA Services, on va pouvoir en faire une. Alors pour base de données, je vais prendre celle de NorthWind non modifié, vous pouvez la trouver à ce lien si vous ne l’avez pas. Alors pour commencer nous allons créer une nouvelle application Silverlight de type Business Application. Ce type d’application est ajouté après avoir installer les .Net RIA Services. Maintenant que notre projet est créé, nous allons accéder à notre base, donc dans notre projet ASP.Net, nous allons créer cet accès avec Entity Framework,  avec lequel nous allons récupérer tous les employés de la base Northwind. Donc jusque là, rien de bien anormal par rapport à un autre projet Silverlight que vous auriez pu faire avant. Maintenant, toujours dans le projet ASP.Net, nous allons créer un nouvel item qui s’appelle “Domain Service Class”, on peut le trouver dans la partie Web, comme on peut le voir ci dessous : image Maintenant, nous avons un deuxième écran qui nous demande quels éléments seront disponibles du côté client. Pour la démonstration, nous allons donc choisir les “Employees”, et la possibilité d’éditer ceux-ci. image Cette classe nous génère donc entre autre ce code ci :
    // Implements application logic using the NorthwindEntities context.
    // TODO: Add your application logic to these methods or in additional methods.
    [EnableClientAccess()]
    public class NorthwindDomainService : LinqToEntitiesDomainService<NorthwindEntities>
    {

        // TODO: Consider
        // 1. Adding parameters to this method and constraining returned results, and/or
        // 2. Adding query methods taking different parameters.
        public IQueryable<Employees> GetEmployees()
        {
            return this.Context.Employees;
        }

        public void InsertEmployees(Employees employees)
        {
            this.Context.AddToEmployees(employees);
        }

        public void UpdateEmployees(Employees currentEmployees)
        {
            this.Context.AttachAsModified(currentEmployees, this.ChangeSet.GetOriginal(currentEmployees));
        }

        public void DeleteEmployees(Employees employees)
        {
            if ((employees.EntityState == EntityState.Detached))
            {
                this.Context.Attach(employees);
            }
            this.Context.DeleteObject(employees);
        }
    }
}
Comme on peut le voir, cette action nous permet de générer tous ce qu’il faut pour lire et modifier des employés de la base, bien entendu rien ne vous empêche d’en rajouter, pour des questions de performances, comme par exemple, une partie qui ne retourne seulement 10 employés de la base au lieu de toute la base. L’autre partie qui est généré sont les metadatas nécessaire pour le bon fonctionnement de la communication avec les .Net RIA Services. Donc maintenant qu’on a crée notre partie accès aux données, ainsi que notre code métier, et le service pour accéder à nos données, nous n’avons plus qu’à gérer le côté client en Silverlight. Alors commençons par la partie XAML, pour notre exemple, nous allons afficher les noms et les prénoms des employés de la base de données :
                <ListBox ItemsSource="{Binding}" Margin="0,50,0,0" Height="300">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal" >
                                <TextBlock Text="{Binding FirstName}"
                           Width="250" />
                                <TextBlock Text="{Binding LastName}" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
Nous allons maintenant charger les données depuis la base grâce aux RIA Services :
private void Page_Loaded(object sender, RoutedEventArgs e)
{
    var context = new NorthwindDomainContext();
    DataContext = context.Employees;
    context.Load(context.GetEmployeesQuery());
}
Nous chargeons les données de façon assez simple, grâce à une requête Linq, ou à la méthode context.GetEmployeesQuery() qui renvoie ceci :
public EntityQuery<Employees> GetEmployeesQuery()
{
    return base.CreateQuery<Employees>("GetEmployees", null, false, true);
}
Soit toutes les données de la table Employees. Bien entendu, toutes ces opérations avec .Net RIA Services sont asynchrone afin que l’application reste fluide pour l’utilisateur. Alors pour conclure sur cet article, et sur cette technologie, les .Net RIA Services sont très puissant pour toutes les applications Business, néanmoins la faille que je vois par rapport à un service classique en WCF, c’est que les .Net RIA Services sont orientés uniquement pour Silverlight, alors qu’un service WCF permet de partager l’accès aux services entre divers types d’application. Voilà bien entendu je vous fournis le code source de l’application, il faudra bien entendu changer la chaine de connexion à la base de données. image Ressouces : Un très bon lien sur .Net RIA Services que je vous conseille : http://blogs.msdn.com/brada/archive/tags/RIAServices/default.aspx
Remonter