Nous venons de voir la création du service WCF. Il convient donc maintenant de développer la partie cliente. Celle-ci est composée d'une interface WPF qui affichera les différentes images récupérées depuis notre service WCF. Afin de proposer un affichage un peu plus sympathique que le contrôle
Image de base disponible avec WPF, nous allons utiliser le contrôle
TransitionPresenter proposé par
Kevin Moore dans son
Bag'O'Trick (celui-ci peut-être télécharger sur le blog de Kevin :
Kevin@Work). Ce contrôle
TransitionPresenter est très pratique car il permet d'afficher une liste d'objets avec des effets de transitions entre chaque objet. Son utilisation passe par la déclaration d'un grand nombre de
Transitions, dans les ressources de l'application. Ensuite, il ne reste plus qu'à utiliser le contrôle dans notre application et à utiliser un
DataTemplate :
<lib:TransitionPresenter x:Name="tp" Margin="10">
<lib:TransitionPresenter.Resources>
<Converters:ByteArrayToImageConverter x:Key="ByteArrayToImageConverter"/>
<DataTemplate DataType="{x:Type BusinessObjects:CustomImage}">
<Image Width="300"
Height="300"
Source="{Binding Path=Img, Converter={StaticResource ByteArrayToImageConverter}, Mode=Default}"
/>
</DataTemplate>
</lib:TransitionPresenter.Resources>
</lib:TransitionPresenter>
Le
DataTemplate est utilisé pour donner une représentation graphique aux objets de type CustomImage, étant donné que ce sont ces objets qui seront affichés dans l'application.
Vous avez sans doute remarqué que le
DataTemplate fait appel, lors du binding, à un convertisseur. En effet, si vous vous souvenez de ce que nous avons dit un peu plus haut, le type
Image n'est pas sérialisable. C'est pourquoi nous avons utilisé un tableau de byte. Hors pour afficher cette image, nous devons convertir ce tableau de byte en image, d'où l'utilisation du convertisseur, dont vous pouvez voir le code juste après :
public class ByteArrayToImageConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImage ImgSource = null;
byte[] array = value as byte[];
if (array != null)
{
ImgSource = new BitmapImage();
ImgSource.BeginInit();
ImgSource.StreamSource = new MemoryStream(array);
ImgSource.EndInit();
}
return ImgSource;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
Comme tout convertisseur WPF, nous avons juste utilisé une classe sur laquelle nous avons implémenté l'interface
IValueConverter, afin de pouvoir écrire les méthodes
Convert et
ConvertBack. Le but de ces méthodes est, tout simplement, de prendre un type en entrée (ici, il s'agit du tableau de byte) et de le convertir en un autre type, nécessaire pour l'objet sur lequel le binding est appliqué.
Une fois la partie IHM terminée, il ne reste plus qu'à écrire un peu de code, nécessaire pour changer de transitions, appeler les méthodes du service WCF, etc.
Nous allons commencer par mettre en place le système permettant de changer de transitions et d'éléments dans le
TransitionPresenter. Pour cela, nous allons utiliser un
DispatcherTimer, qui n'est rien d'autre qu'un timer mais qui va nous permettre d'accéder à notre interface graphique depuis un autre Thread (et donc d'éviter les problèmes d'appels/d'échanges inter-thread) :
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, interval);
timer.IsEnabled = true;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
Comme vous pouvez le voir, à chaque intervalle identifié par la variable
interval, la méthode
timer_Tick sera appelée. Voici le contenu de cette méthode :
/// <summary>
/// Method called when the Tick event of the timer occurs.
/// </summary>
private void timer_Tick(object sender, EventArgs e)
{
Transition[] transitions = (Transition[])FindResource("Transitions");
int nextValTransition;
int nextValData;
do
{
nextValTransition = rmdTransition.Next(1, transitions.Length);
}
while (nextValTransition == previousTransition);
previousTransition = nextValTransition;
this.tp.Transition = transitions[nextValTransition];
do
{
nextValData = rdmImg.Next(0, GlobalImageList.Count);
}
while (nextValData == previousValue);
previousValue = nextValData;
this.tp.Content = GlobalImageList[nextValData];
}
Vous pouvez en juger par vous-même : il n'y a là rien de compliqué. On change la transition de l'objet
TransitionPresenter et on change également l'élément à afficher dans notre objet (
GlobalImageList est une liste de
CustomImage qui est chargée au démarrage de l'application).
Toute la partie concernant l'interface utilisateur est maintenant terminée : il ne nous reste plus qu'à nous connecter à notre service WCF, depuis l'application cliente.