nRoute – Commands - Passage de paramètres

6 minutes read

Pré-requis

Afin de mieux appréhender le contenu de cet article, il est conseillé d’avoir lu l’article précédent concernant l’utilisation et la création de commandes.

Cet article vous fera découvrir différentes manières de passer des paramètres à vos commandes avec nRoute.

Le contexte

Nous souhaitons développer une application simple qui affiche une liste d’utilisateur et qui, lorsqu’un élément de cette liste est sélectionné, permet d’en afficher les détails à droite de la liste.

Cette application ressemblera à la capture ci-dessous :

nRoute-Command-Parameters

Le modèle de données

Notre modèle de donnée dispose de deux classes représentant les utilisateurs.

La première est UserLight et ne contient que les informations minimale de notre utilisateur (Id et Login). C’est de cette classe dont nous nous servirons pour peupler la liste à gauche.

La seconde est User qui est la classe contenant toutes les informations de notre utilisateur.

Voici les définitions de ces deux classes :

public class UserLight
{
    public long Id { get; set; }
    public string Login { get; set; }
}

public class User
{
    public long Id { get; set; }
    public string Login { get; set; }
    public string Email { get; set; }
    public DateTime CreationDate { get; set; }
}

L’accès aux données

Afin de faciliter l’accès à nos données, nous avons créé un dépôt des utilisateurs. Ce dépôt est un dépôt de test qui contient dix utilisateurs et qui nous renvoie nos résultats via une callback tel que le ferait une implémentation qui ferait un appel à un service web. Il contient deux méthodes, une nous renvoyant la liste de tout les utilisateurs, et une nous renvoyant les détails d’un utilisateur donné.

Voici le code du dépôt :

public class UserRepository
{
    private static readonly List<User> _users;

    static UserRepository()
    {
        _users = new List<User>();

        for (int i = 0; i < 10; ++i)
        {
            _users.Add(new User
            {
                Id = i,
                Login = "login_" + i,
                Email = "email" + i + "@company.com",
                CreationDate = DateTime.Now.AddDays(-i)
            });
        }
    }

    public void GetAll(Action<IEnumerable<UserLight>> callback)
    {
        callback(_users.Select(u => new UserLight { Id = u.Id, Login = u.Login }));
    }

    public void GetById(long userId, Action<User> callback)
    {
        callback(_users.FirstOrDefault(u => u.Id == userId));
    }
}

Le view model et la vue

Maintenant nous allons créer notre view model. Celui-ci contient deux propriétés. La première est Users qui est la liste de tout les utilisateurs. La seconde est CurrentUser qui représentera l’utilisateur sélectionné dans la liste. Le view model chargera dans son constructeur, la liste de tout les utilisateurs du dépôt afin de les afficher dans la liste à gauche.

Voici le code du view model :

[MapViewModel(typeof(MainPage))]
public class MainPageViewModel : ViewModelBase
{
    private readonly UserRepository _userRepository;

    private ObservableCollection<UserLight> _users;
    private User _currentUser;

    public MainPageViewModel()
    {
        _userRepository = new UserRepository();

        _userRepository.GetAll(users => Users = new ObservableCollection<UserLight>(users));
    }

    public ObservableCollection<UserLight> Users
    {
        get
        {
            return _users;
        }

        set
        {
            if (_users != value)
            {
                _users = value;
                NotifyPropertyChanged(() => Users);
            }
        }
    }

    public User CurrentUser
    {
        get
        {
            return _currentUser;
        }

        set
        {
            if (_currentUser != value)
            {
                _currentUser = value;
                NotifyPropertyChanged(() => CurrentUser);
            }
        }
    }
}

Et bien entendu voici le code XAML de notre MainPage :

<UserControl
    x:Class="nRoute_Command_Parameters.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:n="http://nRoute/schemas/2010/xaml">

    <i:Interaction.Behaviors>
        <n:BridgeViewModelBehavior />
    </i:Interaction.Behaviors>

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ListBox x:Name="listBox"
                 Grid.Column="0"
                 Grid.RowSpan="4"
                 ItemsSource="{Binding Users}"
                 DisplayMemberPath="Login"
                 Margin="20"
                 SelectedValuePath="Id">
        </ListBox>

        <TextBlock Grid.Column="1" Grid.Row="0" Text="Id : " />
        <TextBlock Grid.Column="1" Grid.Row="1" Text="Login : " />
        <TextBlock Grid.Column="1" Grid.Row="2" Text="Email : " />
        <TextBlock Grid.Column="1" Grid.Row="3" Text="Date : " />

        <TextBox Grid.Column="2" Grid.Row="0" Text="{Binding CurrentUser.Id}" IsReadOnly="True" />
        <TextBox Grid.Column="2" Grid.Row="1" Text="{Binding CurrentUser.Login}" />
        <TextBox Grid.Column="2" Grid.Row="2" Text="{Binding CurrentUser.Email}" />
        <TextBox Grid.Column="2" Grid.Row="3" Text="{Binding CurrentUser.CreationDate}" IsReadOnly="True" />
    </Grid>
</UserControl>

Déclaration et utilisation d’une commande paramétrée

Tout ceci est très bien mais lorsque l’on clique sur un utilisateur chargé dans notre liste rien de ne s’affiche à droite, ce qui est normal car nous n’avons rien codé.

Afin de remédier à celà nous allons créer une commande qui sera invoquée lorsque la sélection de notre listbox changera.

Nous allons donc modifier notre view model pour lui ajouter le code suivant :

private ICommand _showDetailCommand;

public ICommand ShowDetailCommand
{
    get
    {
        return _showDetailCommand;
    }
}

private void ShowDetail(long id)
{
    _userRepository.GetById(id, u => CurrentUser = u);
}

N’oublions pas d’initialiser cette commande dans notre constructeur.

_showDetailCommand = new ActionCommand<long>(ShowDetail);

Vous remarquerez que contrairement à l’article précédent, ici nous utilisons la version générique de ActionCommand. Ici nous indiquons que cette commande prend un paramètre de type long et qu’elle doit utiliser la méthode ShowDetail. Cette dernière utilisera notre dépôt pour récupérer les détails de l’utilisateur sélectionné et de l’affecter à la propriété CurrentUser du view model.

Et voilà ! La boucle est bouclée ! Oui mais enfait non. Nous n’avons toujours pas lié notre commande dans le xaml. Il nous faut donc remplacer le code de notre listbox par le suivant :

<ListBox x:Name="listBox"
    Grid.Column="0"
    Grid.RowSpan="4"
    ItemsSource="{Binding Users}"
    DisplayMemberPath="Login"
    Margin="20"
    SelectedValuePath="Id">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <n:ExecuteCommandAction
                Command="{Binding ShowDetailCommand}"
                Parameter="{Binding ElementName=listBox, Path=SelectedValue}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

Maintenant si nous analysons le XAML ci-dessus nous voyons que la valeur sélectionnée par notre listbox correspond à la propriété Id de notre utilisateur (SelectedValuePath=”Id”).

Nous déclarons un trigger qui se déclenche lorsque la sélection de notre lisbox est changé (EventName=”SelectionChanged”).

Lorsque ce trigger est déclenché, nous executons la commande ShowDetailCommand de notre view model (Command=”{Binding ShowDetailCommand}”) en lui passant la valeur sélectionnée par notre listbox qui est ici l’Id de l’utilisateur (Parameter=”{Binding ElementName=listBox, Path=SelectedValue}”).

Cette fois-ci c’est vraiment fini. Si nous executons notre application vous pourrez constater que tout se passe comme prévu.

Vous savez maintenant comment passer un paramètre à une commande en XAML.

Variantes et paramètres multiples.

Pour la suite je vais vous proposer une petite variante qui va vous permettre d’apprendre à passer plusieurs paramètres à vos commande.

nRoute fournit deux couples de classes permettant de passer plusieurs paramètres :

  • n:Parameter / n:ParametersCollection
  • n:DependencyParameter / n:DependencyParametersCollection

Le groupe Parameter n’accepte que des valeurs statiques alors que le groupe DependencyParameter permet le binding ce qui est très souvent le fonctionnement recherché.

Modifions donc notre XAML pour passer un paramètre en utilisant une instance de DependencyParameter.

<n:ExecuteCommandAction Command="{Binding ShowDetailCommand}">
    <n:ExecuteCommandAction.Parameter>
        <n:DependencyParameter Value="{Binding ElementName=listBox, Path=SelectedValue}" />
    </n:ExecuteCommandAction.Parameter>
</n:ExecuteCommandAction>

Maintenant l’initialisation de notre commande dans le ViewModel :

_showDetailCommand = new ActionCommand<DependencyParameter>(
    p =>
    {
        var id = System.Convert.ToInt64(p.Value);
        ShowDetail(id);
    });

En lancant l’application vous constaterez que le fonctionnement est identique à précédement.

Si nous décidions, à titre d’exemple, de passer le login de l’identifiant et le login de l’utilisateur en paramètre à notre commande nous pourrions procéder de la manière suivante.

Modification du XAML :

<n:ExecuteCommandAction Command="{Binding ShowDetailCommand}">
    <n:ExecuteCommandAction.Parameter>
        <n:DependencyParametersCollection>
            <n:DependencyParameter Key="Id" Value="{Binding ElementName=listBox, Path=SelectedValue}" />
            <n:DependencyParameter Key="Login" Value="{Binding ElementName=listBox, Path=SelectedItem.Login}" />
        </n:DependencyParametersCollection>
    </n:ExecuteCommandAction.Parameter>
</n:ExecuteCommandAction>

Modification de l’initialisation de notre commande dans le ViewModel :

_showDetailCommand = new ActionCommand<DependencyParametersCollection>(
    p =>
    {
        var id = System.Convert.ToInt64(p["Id"]);
        var login = p["Login"].ToString();
        System.Windows.MessageBox.Show(login);
        ShowDetail(id);
    });

Je pense que vous avez compris le principe maintenant ;-)

Vous pourrez retrouver le code de cet article sur mon skydrive.

Updated:

Leave a Comment