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

WPF : Utiliser des commandes dans un DataTemplate

NOVE21

Après une très longue période sans réutiliser de technologies tel que WPF ou Silverlight, on s’aperçoit que l’on perd très vite la main. C’est donc durant un projet éclair que je me suis décidé de refaire un petit projet en utilisant le MVVM.

C’est donc confiant que je commence mon code, que je me décide à le lancer au bout d’une heure, histoire de voir ce que ça donne en dehors du Designer WPF. Mon interface ressemble donc à ce que je veux, cependant je m’aperçois que certaines de mes commandes ne marchaient pas, je passe donc 10 bonnes minutes pour trouver ce qu’il se passe et enfin corriger.

On va donc voir dans cet article un moyen de contourner la déclaration des commandes dans un DataTemplate, sans pour autant perdre en fonctionnalité.

 

Pour cette démonstration je vais utiliser comme base le template de MVVM qui vient de codeplex (WPF Model-View-ViewModel Toolkit 0.1) que je vais modifier de tel sorte que ViewModelBase dérive de DependencyObject, et implémente deux méthodes abstraites qui sont OnViewReady(), et OnViewDispose(). On obtient donc ceci :

/// <summary>
/// Provides common functionality for ViewModel classes
/// </summary>
public abstract class ViewModelBase : DependencyObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public abstract void OnViewReady();
    public abstract void OnViewDispose();

}

Note les modifications apportées ici ne sont pas nécessaire pour la démonstration, mais grâce à cela vous pouvez utiliser les DependencyProperty dans votre ViewModel, et propager les évènements Loaded et Unloaded de vos vues, vers vos ViewModel. Voilà pour l’astuce du jour !

Voici donc  la commande que nous allons utiliser :

#region GestionCommandes
private DelegateCommand<Models.Person> editPersonCommand;

public ICommand EditPersonCommand
{
    get
    {
        if (editPersonCommand == null)
            editPersonCommand = new DelegateCommand<Models.Person>(EditPerson);
        return editPersonCommand;
    }
}

private void EditPerson(Models.Person person)
{
    MessageBox.Show("Ici on éditera la personne", "", MessageBoxButton.OK);
}
#endregion 

Bon maintenant, affichons nos données dans la vue au pas à pas. Après un binding initial on obtient quelque chose du style

<Window x:Class="WpfModelViewApplication3.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:WpfModelViewApplication3.Commands"
    Title="Main Window" Height="400" Width="800">

    <Grid>
        <ListBox ItemsSource="{Binding People}" />
    </Grid>
</Window>

Avec pour interface générée :

image

Bon ceci, n’étant pas très lisible pour un client, on va donc créer un DataTemplate. Or pour des raisons de lecture, nous allons ajouter ce DataTemplate en ressources de la fenêtre.

Donc confiant, j’écris ceci :

<Window.Resources>
    <DataTemplate DataType="{x:Type localModel:Person}">
        <StackPanel Orientation="Horizontal">
            <Label Content="{Binding FirstName}" />
            <Button Content="Modifier" Margin="5 0" Command="{Binding EditPersonCommand}" CommandParameter="{Binding}" />
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<Grid>
    <ListBox ItemsSource="{Binding People}" />
</Grid>

J’ai donc mon interface qui change en conséquence :

image

Le problème se situe en fait lorsque je clique sur le bouton Modifier, en effet rien ne se produit. J’ai donc regardé si lors de la construction de la fenêtre il allait bien me chercher ma commande, mais il ignora mon point d’arrêt dans Visual Studio.

Alors l’astuce facile à réaliser avec ce template est d’utiliser leur CommandReference, de tel sorte :

<Window.Resources>
    <c:CommandReference x:Key="EditPerson" Command="{Binding EditPersonCommand}" />


    <DataTemplate DataType="{x:Type localModel:Person}">
        <StackPanel Orientation="Horizontal">
            <Label Content="{Binding FirstName}" />
            <Button Content="Modifier" Margin="5 0" Command="{StaticResource EditPerson}" CommandParameter="{Binding}" />
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<Grid>
    <ListBox ItemsSource="{Binding People}" />
</Grid>

Là, notre code fonctionne comme on le souhaitait, et l’on récupère bien entendu le bon membre lors du déclenchement de la commande. En fait, ce qui a changé, nous avons déclaré la command, dans l’objet CommandReference. Notre bouton peut donc appeler la commande via le nom qu’on lui a donné. En effet, il passe par une ressource statique qui existe, et non une donnée dynamique comme il était question dans le DataTemplate.

Vous pouvez retrouvez la CommandReference dans le MVVM Toolkit, ce qui nous prouve encore une fois qu’avec des Toolkit bien pensé on optimise fortement notre temps de développement.

En espérant que cet article vous aide dans de futurs développement !

Remonter

Des nouvelles de la PDC 2009 : Silverlight 4 Beta

NOVE21

Et voilà, comme je le suppose, vous avez tous du apprendre que Silverlight 4 Beta est disponible pour nous les développeurs ! Et sinon vous n’étiez pas au courant, voilà qui est fait. Alors je ne vais pas vous lister toutes les nouveautés de Silverlight 4, vu que d’autres l’ont déjà fait ! Mais voici un ensemble de liens ou vous pourrez voir tous les avantages de cette nouvelle version.

 

Le blog de Tim Heueur bien entendu : http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx Et la série d’article de Mike Taulty (qui soit dit en passant à réussi à faire perdre la tête à mon Netvibes)
Silverlight 4: Beta Announced at PDC
Silverlight 4 Rough Notes: Trusted Applications
Silverlight 4 Rough Notes: Camera and Microphone Support
Silverlight 4 Rough Notes: Printing
Silverlight 4 Rough Notes: HTML Hosting in the WebBrowser Control
Silverlight 4 Rough Notes: Notification Windows
Silverlight 4 Rough Notes: RichTextArea
Silverlight 4 Rough Notes: Networking
Silverlight 4 Rough Notes: FlowDirection
Silverlight 4 Rough Notes: Clipboard Access
Silverlight 4 Rough Notes: Silverlight as a Drop Target
Silverlight 4 Rough Notes: Right Mouse Button Support
Silverlight 4 Rough Notes: Binding
Silverlight 4 Rough Notes: Binding and IDataErrorInfo
Silverlight 4 Rough Notes: Binding with INotifyDataErrorInfo
Silverlight 4 Rough Notes: A Word for Visual Studio 2010
Silverlight 4 Rough Notes: Styles
Silverlight 4 Rough Notes: Commanding
Silverlight 4 Rough Notes: Customising the Window
Silverlight 4 Rough Notes: Animating Items Into/Out Of ItemsControls
Silverlight 4 Rough Notes: HTML interop & dynamic language features
Silverlight 4 Rough Notes: Data Grid Enhancements
Silverlight 4 Rough Notes: Taking Control of Navigation
Silverlight 4 Rough Notes: SelectedValue
Silverlight 4 Rough Notes: Mousewheel Support in Controls
Silverlight 4 Rough Notes: TextBlock with Trimming
Silverlight 4 Rough Notes: CompositeTransform
Silverlight 4 Rough Notes: Viewbox
Silverlight 4 Rough Notes: Managed Extensibility Framework

 

Et voilà de quoi occuper longuement votre weekend, et le mien aussi d’ailleurs.
Remonter

Silverlight 3 : le contrôle DataForm

AOÛT22

Dans cet article, je vais vous présenter les fonctionnalités du DataForm, vous pouvez trouver ce composant dans le Silverlight Toolkit présent sur codeplex Alors commençons par notre entité que nous allons afficher :
public class Personne
 {
     public string Nom { get; set; }
     public string Prenom { get; set; }
     public int Age { get; set; }
     public string Mail { get; set; }
 }
Donc jusque là une entité classique que l’on peut retrouver dans beaucoup d’application (surtout celle de démonstrations ….) Et un code XAML de ce type :
  <UserControl.Resources>
      <local:Personne Age="23"
                      Nom="Woivré"
                      Prenom="Wilfried"
                      Mail="wilfried.woivre@gmail.com"
                      x:Key="MaPersonne" />
  </UserControl.Resources>
<Grid x:Name="LayoutRoot" Width="400" Height="400">
  <dataFormToolkit:DataForm CurrentItem="{StaticResource MaPersonne}"></dataFormToolkit:DataForm>
</Grid>
On obtient donc rien qu’avec cela une fenêtre éditable de mon entité personne, comme on peut le voir sur l’image ci dessous image Maintenant, c’est bien beau, mais actuellement, notre DataForm a lié l’intégralité de notre entité, qui est donc modifiable par l’utilisateur, on peut donc se service des attributs Editable et Display pour personnaliser notre DataForm. ATTENTION, dans les anciennes versions de Silverlight Toolkit, ces attributs étaient présent sous la nom de Bindable.
[Display(AutoGenerateField=false)]
public string Nom { get; set; }
[Editable(false)]
public string Prenom { get; set; }
public int Age { get; set; }
[Display(Name="E mail")]
public string Mail { get; set; }
L’élément Display contient divers arguments pour personnalisez comme vous le souhaiter votre DataForm. image On obtient donc le résultat suivant à l’affichage image Maintenant, pour savoir lorsque notre entité change ou est en train de changer, on peut bien entendu s’abonner aux différents évènements du DataForm pour exécuter nos différentes opérations. Cependant on peut aussi utiliser l’interface IEditableObject sur notre entité comme ceci :
public class Personne : IEditableObject
{
    #region Properties

    [Display(AutoGenerateField=false)]
    public string Nom { get; set; }
    [Editable(false)]
    public string Prenom { get; set; }
    public int Age { get; set; }
    [Display(Name="E mail")]
    public string Mail { get; set; }

    #endregion

    #region IEditableObject Members

    public void BeginEdit()
    {
    }

    public void CancelEdit()
    {
    }

    public void EndEdit()
    {
    }

    #endregion
On peut donc gérer notre entité directement ainsi pour gérer nos opérations sur cette entité en question. Maintenant voyons comment se comporte le DataForm avec une liste d’objet que l’on peut déclarer de la sorte :
public ObservableCollection<Personne> MesPersonnes { get; set; }
public MainPage()
{
    InitializeComponent();
    MesPersonnes = new ObservableCollection<Personne>()
    {
        new Personne(){
            Nom = "Woivré",
            Prenom = "Wilfried",
            Age = 23,
            Mail ="wilfried.woivre@gmail.com"
        },
        new Personne(){
            Nom = "Pera",
            Prenom = "Alexis",
            Age = 22
        },
        new Personne(){
            Nom = "Payet",
            Prenom = "Patrick",
            Age = 26
        }
    };

    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = this;
}
<Grid x:Name="LayoutRoot" Width="400" Height="400">
  <dataFormToolkit:DataForm ItemsSource="{Binding MesPersonnes}"></dataFormToolkit:DataForm>
</Grid>
Notre DataForm va donc nous créer une liste d’objet dans laquelle on pourra naviguer grâce aux différents boutons situés sur le header du composant comme on peut le voir. image Donc comme on peut le voir, on peut bien entendu naviguer, mais aussi rajouter des éléments ou en supprimer. Alors voilà un petit tour sur les possibilités du DataForm, je vous rappelle que ce contrôle est dans le Silverlight Toolkit ( de la version de  juillet, dans ce cas ), il est donc susceptible d’évoluer encore un peu ! Ressource : Une petite vidéo de Mike Taulty sur le DataForm : http://silverlight.net/learn/learnvideo.aspx?video=187317 dans une version précédente celle de juillet, donc il y a quelques changements à effectuer si vous voulez reproduire la démonstration !
Remonter

MVVM Template en WPF et Silverlight

JUIL31

Bonjour à tous,

Alors je publie cet article vu le nombre de personne qui visitent mon blog pour les données sur le MVVM, j’espère avant tout qu’elles vous sont précieuses !

En fait durant mes différentes recherches sur le MVVM, principalement sur des projets de template pour réaliser des démonstrations, ou pour créer des projets plus rapidement avant de décider à implémenter mon propre projet de Template qui sera peut être publié un jour (si vous êtes sages !)

 

Vous pouvez donc retrouver un très bon Template sur le MVVM sur le site de codeplex à l’adresse : http://wpf.codeplex.com

 

Maintenant pour Silverlight et WPF, vous avez celui sur le site de Galasoft : http://www.galasoft.ch/mvvm/install/publish.html 

Alors rapidement avant que je ne vois des gens choqués sur mon blog, ces deux templates référence une dll de Galasoft, qui gère le MVVM, néanmoins si vous n’avez pas besoin de changer le comportement de son Toolkit, il suffit amplement pour des projets d’entreprise et de démonstration entre autre. Cependant voici un aperçu de sa dll : http://www.galasoft.ch/mvvm/getstarted/

 

Et voilà, en espérant que cela vous sera utile pour des développements futurs !

Remonter