Le SurfaceScrollViewer est une surcharge de l'élément WPF ScrollViewer. Le framework 3.5 SP1, ne nous permet pas d'accéder, en code xaml, sur les propriétés HorizontalOffset et VerticalOffset de ce composant. Or ces 2 propriétés nous indiquent la position du scroll, respectivement horizontale et verticale, du ScrollViewer. Cette restriction vient du fait que l'on ne peut pas effectuer un binding en mode TwoWay sur ces propriétés. En effet, on peut récupérer la valeur, mais on neut peut la modifier. Afin de modifier ces 2 propriétés, on doit utiliser les méthodes ScrollToHorizontalOffset et ScrollToVerticalOffset respectivement. Dans cet article je vais vous détailler comment contourner cette carence de DataBinding. On va synchronizer différents SurfaceScrollViewers entre eux, selon leur groupe d'appartenance dont le développeur pourra définir.

Pour cela on créé une classe qui dérive de DependencyObject. Car, on sait que l'objet que l'on créé n'a pas de représentation graphique. Par contre, il doit être capable de fournir une propriété de dépendance et de s'attacher à un composant graphique, en l'occurence un SurfaceScrollViewer, d'où cet héritage.

declarer_class_scrollviewer_synchronizer.bmp

Dans cette classe nous allons créé une nouvelle propriété de dépendance, en utiilisant la méthode statique RegisteryAttached. Cette méthode permet d'inscrire une propriété jointe avec le système de propriétés de son appartenance. On lui spécifie le nom de la propriété dans un string, le type de la propriété, le type du propriétaire et les métadonnées de la propriété spécifiée. Voici la déclaration de la DependencyPorperty et son encapsulation.

dependency_property_srcollviewer_synchronizer.bmp

Les méthodes d'encapsulations sont statiques, ce qui leur permettent d'être partagées avec n'importe quel autre objet. Pour en savoir un peu plus sur les DependencyProperty je vous conseille de lire mon article sur le sujet.

Maintenant que nous avons notre propriété de dépendance sur laquelle on peu effectuer un binding, il nous faut stocker des données concernant les SurfaceScrollViewers afin de pouvoir synchroniser leurs scrolls. On définit 3 dictionnaires de données. Le premier permet de stocker les objets de type SurfaceScrollViewer en tant que clé du dictionnaire. Chaque clé a une valeur assignée de type string. Cette valeur correspond au groupe de synchronisation des scrolls dans lequel le SurfaceScrollViewer appartient. Le groupe correspond à la valeur de la propriété de dépendance ScrollGroupProperty que l'on aurra encré dans les propriétés, en code xaml, des SurfaceScrollViewers que l'on désire synchroniser.

dictionnaire1_scrollviewer_synchronizer.bmp

Les 2 dictionnaires suivant permettent de stocker la position du scroll horizontal et du scroll vertical respectivement. Ilsprennent comme clé un string qui est la valeur stockée dans le premier dictionnaire, c'est-à-dire le groupe de synchronisation.

dictionnaires_scrolls_scrollViewer_Synchronizer.bmp

On revient sur la méthode spécifiée dans les métadonnées de la propriété de dépendance ScrollGroupProperty. Elle est déclenchée suite à la modification de la propriété. Cette méthode va nous permettre de réagir à 2 choses :

  • On désabonne la méthode ScrollViewer_ScrollChanged de l'event ScrollChanged et on supprime le SurfaceScrollViewer du premier dictionnaire.
  • On réabonne la méthode ScrollViewer_ScrollChanged à l'event ScrollChanged, après avoir placé les scrolls selon les caractéristiques du groupe et l'insertion de la nouvelle KeyValuePair dans le dictionnaire.

OnScrollGroupChanged_ScrollViewer_synchronizer.bmp

La méthode ScrollViewer_ScrollChanged que l'on abonne à l'event ScrollChanged de chaque SurfaceScrollViewer ayant un groupe de définit, nous permet de positionner le scroll de tous les autres SurfaceScrollViewer du même goupe à la position de l'appelant. Merci aux références ^). C'est dans cette méthode que l'on pourra utiliser les méthodes ScrollToHorizontalOffset et ScrollToVerticalOffset.

ScrollViewer_ScrollChanged_ScrollViewer_Synchronizer.bmp

Pour utiliser cette nouvelle classe en xaml, on doit définir au préalable le namespace dans un xmlns. Voici une fois fait, comment uriliser la synchronisation.

SynchronizerScroll_SurfaceScrollViewer_xaml.bmp