Archive

Archive for juin 2011

Renvoyer une nouvelle instance d’une ressource à chaque appel

WPF nous offre plusieurs manières de définir un objet en tant ressource dans notre application, en utilisant :

  • notre fichier App.xaml,
  • un dictionnaire de ressource
  • un style
  • la propriété Resources de notre fenêtre, contrôles intégrés à WPF et contrôles personnalisés (User Control et Custom Control)

La liste ci-dessus n’est pas exhaustive.
Dans notre exemple nous allons définir notre ressource au niveau de notre fenêtre comme suit :

<Window x:Class="WpfApplicationSharedResource.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <ObjectDataProvider x:Key="Random"
                            MethodName="Next"
                            ObjectType="{x:Type sys:Random}">
            <ObjectDataProvider.MethodParameters>
                <sys:Int32>100</sys:Int32>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
    <UniformGrid Rows="1" Columns="2">
        <TextBlock Text="{Binding Path=., Source={StaticResource ResourceKey=Random}}" Background="AliceBlue"
                   FontSize="20"
                   VerticalAlignment="Stretch"
                   HorizontalAlignment="Stretch"
                   TextAlignment="Center" />
        <TextBlock Text="{Binding Path=., Source={StaticResource ResourceKey=Random}}" Background="Cyan"
                   FontSize="20"
                   VerticalAlignment="Stretch"
                   HorizontalAlignment="Stretch"
                   TextAlignment="Center"/>
    </UniformGrid>
</Window>

Dans le code XAML ci-dessus notre ressource utilise un objet de type ObjectDataProvider qui nous permet d’instancier la classe Random et d’appeler sa méthode Next en lui fournissant un entier comme paramètre. Ensuite nous définissons deux objets de type TextBlock qui récupèrent chacun la valeur renvoyer par notre ressource et l’affiche à l’écran.

On sait que la méthode Next de la classe Random est censée renvoyée une valeur aléatoire à chaque appel donc à l’exécution de notre exemple on peut s’attendre à ce que nos deux TextBlock aient des valeurs différentes. Mais si vous l’exécutez, vous verrez bien que ce n’est pas le cas. A chaque exécution les deux TextBlock afficheront la même valeur comme dans l’image ci-dessous :

Comment cela fonctionne ?
Les objets définis en ressource sont évalués lorsque nous essayons d’y accéder pour la première fois. A l’issue de ce premier accès le résultat est stocké en mémoire. Lorsque nous essayons d’y accéder à nouveau, ce sera le résultat stocké en mémoire qui nous sera retourné.
Donc dans notre exemple, l’accès à la ressource par notre premier TextBlock entraîne l’instanciation de la classe Random et l’appel de la méthode Next qui nous renvoie une valeur et c’est cette dernière qui est stockée en mémoire. Notre deuxième TextBlock essayant d’y accéder n’entraînera pas la réévaluation de notre ressource mais recevra tout simplement la valeur stockée en mémoire.

Comment faire pour que notre ressource nous renvoie une nouvelle instance à chaque nouvel accès ?
Pour que notre ressource nous renvoie une nouvelle instance à chaque accès, il va falloir utiliser l’attribut x:Shared et mettre sa valeur à false. Cet attribut permet d’avoir une instance pour chaque demande au lieu de partager la même instance pour toutes les demandes. Inutile de vous dire qu’en mettant la valeur de cet attribut à true cela entraînera le même comportement que l’exemple définit précédemment.

Dans l’exemple précédent, si nous modifions la définition de notre ressource en y ajoutant cet attribut et mettre sa valeur à false , nous verrons que les deux TextBlock auront des valeurs différentes.

N.B : L’attribut Shared n’est pas supporté en Silverlight.

Catégories :Silverlight, WPF Étiquettes : , ,

Faire du MutliBinding sans utiliser la classe MutliValueConverter, c’est possible !

J’ai longtemps cru qu’il était impossible de faire du MultiBinding sans disposer au préalable d’un converter c’est à dire d’une instance d’une classe dérivant de la classe de base MultiValueConverter.
Il n’y a qu’à voir cette discussion où je mettais à défi les membres du forum pour me sortir un exemple de cas d’utilisation où l’instance d’un MutliValueConverter ne serait pas indispensable.
Le fait que j’ai lancé ce défi était dû à deux choses :

  1. La première est liée au fonctionnement même du MultiBinding. Ce dernier recevant plusieurs bindings en paramètre alors il doit savoir comment les afficher donc on avait besoin d’une classe dérivant de la classe de base MutliValueConverter pour combiner ces différents bindings et fournir un résultat final.
  2. Quand on oublie de spécifier un converter dans le MutliBinding, nous remarquons le message « Impossible de définir MutliBinding parce que MutliValueConverter doit être spécifié » nous est renvoyé pas Visual Studio.

Bon ! Après des recherches sur le net, j’en suis arrivé à la conclusion suivante : il est bien possible de faire du MultiBinding sans utiliser une classe dérivant de la classe MutliValueConverter. Il suffit d’utiliser la propriété StringFormat de l’instancce de notre MultiBinding..

Comme on le dit souvent du code vaut mieux qu’un long discours alors voici un exemple de cas d’utilisation très simple :

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
 
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <ComboBox x:Name="cb1" Grid.Column="0" Margin="2">
            <ListBoxItem Content="Item 0 - 1" />
            <ListBoxItem Content="Item 0 - 2" />
            <ListBoxItem Content="Item 0 - 3" />
            <ListBoxItem Content="Item 0 - 4" />
            <ListBoxItem Content="Item 0 - 5" />
        </ComboBox>
 
        <ComboBox x:Name="cb2" Grid.Column="1" Margin="2">
            <ListBoxItem Content="Item 1 - 1" />
            <ListBoxItem Content="Item 1 - 2" />
            <ListBoxItem Content="Item 1 - 3" />
            <ListBoxItem Content="Item 1 - 4" />
            <ListBoxItem Content="Item 1 - 5" />
        </ComboBox>
 
        <TextBlock Grid.ColumnSpan="2" Grid.Row="1">
            <TextBlock.Text>
                <MultiBinding StringFormat="cbOne : {0} ### cbTwo : {1}" FallbackValue="Deux items doivent être sélectionnés">
                    <Binding Path="SelectedItem.Content" ElementName="cb1" />
                    <Binding Path="SelectedItem.Content" ElementName="cb2" />
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </Grid>

La propriété StringFormat du MultiBinding fonctionne comme la méthode statique Format de la classe String. Cette propriéte reçoit en paramètre le format à utiliser et les paramètres qui lui sont passés sont les valeurs des différents bindings

Avec le code XAML ci-dessus, vous verrez que Visual Studio devient muet et nous n’affiche plus le message d’erreur précédent. Je pense d’ailleurs que ce message devait plutôt être « Impossible de définir MutliBinding parce que MutliValueConverter ou StringFormat doit être spécifié ».

Catégories :WPF Étiquettes : , ,
%d blogueurs aiment cette page :