Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

[Silverlight] TextBox scrollable / Auto scroll to caret

Je viens d’avoir la nécessité d’une chose inexistante (temporairement j’espère) dans Silverlight.

J’ai eu besoin d’un TextBox scrollable capable de scroller automatiquement lors de l’ajout de texte au TextBox.
Evidement il faut que cette fonctionnalité s’auto-désactive lorsque l’utilisateur change la position de la ScrollBar et se réactive lorsqu’il replace la ScrollBar à sa position maximale.

Exemple d’utilisation

On peut avoir l’utilité de ce type de control pour des logs, sur un client ou un serveur…

L’existant

Le TextBox de Silverlight permet l’utilisation des scroll bars en utilisant les propriétés suivantes dans le XAML :
- ScrollViewer.VerticalScrollBarVisibility
- ScrollViewer.HorizontalScrollBarVisibility

Cependant, on n’a strictement aucun moyen de gérer les événements sur ces mêmes scroll bars, ni même de connaitre leur valeur.
Le seul control qui donne accès a la gestion concrète des événements est le ScrollBar, mais la c’est le drame. Pour l’utiliser il faut tout recréer de zéro pour l’associer a un control.

L’alternative

J’ai donc crée un UserControl qui contient un ScrollViewer (scrollViewer) qui contient lui même un TextBox (txtRaw).

XAML:

<UserControl x:Class="Project.Controls.CustomTextBox"
    xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="Auto" Height="Auto" >
    <Grid x:Name="LayoutRoot" Background="White">
        <ScrollViewer x:Name="scrollViewer" Padding="0"
                      HorizontalScrollBarVisibility="Disabled">
            <TextBox Height="Auto" Width="Auto" x:Name="txtRaw" />
        </ScrollViewer>
    </Grid>
</UserControl>

Le fonctionnement est assez simple.

La propriété AutoScrollToCaret permet à l’utilisateur de spécifier s’il veut que la fonctionnalité d’auto scroll soit active ou non. Si c’est le cas, on va “écouter” les événements de mouvement de la souris sur le ScrollViewer mais aussi celui de changement de texte du TextBox.

Lorsqu’un de ces événements nous parvient, on vérifie différentes informations concernant la ScrollBar :
- Sa position par rapport à la dernière position connue. Si elle a changée et que l’OriginalSource est de type Rectangle, alors cela signifie que c’est l’utilisateur qui a déplacé la position de la ScrollBar. On appelle donc une méthode qui va déterminer si le Control doit continuer d’auto scroller ou non en fonction de la position de la ScrollBar (si la position est au max alors on scrollera sinon non)
- La position maximum. Si elle a changée (suite a une augmentation ou une diminution du contenu du TextBox) ET que l’auto scroll est actif alors on envoie la ScrollBar a sa position maximale.

La variable _hasToCaret est à true lorsque la propriété AutoScrollToCaret est à true et que l’utilisateur n’a pas déplacé la position de la ScrollBar. On se base sur l’état de ce booléen afin de savoir si on doit auto scroller ou non.

Code:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes;

using Project.Common.Delegates; // Namespace comprenant une simple definition pour StringDelegate

namespace Project.Controls
{
   public partial class CustomTextBox : UserControl
   {
      #region Variables
      private double _lastVerticalOffset = 0;
      private double _lastScrollableHeight = 0;

      private bool _hasToCaret = false;
      private bool _autoScrollToCaret = false;
      #endregion

      #region Properties
      public bool AutoScrollToCaret
      {
         get { return _autoScrollToCaret; }
         set
         {
            _autoScrollToCaret = value;
            if (value == true) // On active nos listeners
            {
               _hasToCaret = true;
               scrollViewer.MouseMove += new MouseEventHandler(scrollViewer_MouseMove);
               txtRaw.TextChanged += new TextChangedEventHandler(txtRaw_TextChanged);
               ScrollToCaret();
            }
            else // On désactive nos listeners
            {
               _hasToCaret = false;
               scrollViewer.MouseMove -= new MouseEventHandler(scrollViewer_MouseMove);
               txtRaw.TextChanged -= new TextChangedEventHandler(txtRaw_TextChanged);
            }
         }
      }
      #endregion

      #region Constructor
      public CustomTextBox()
      {
         InitializeComponent();
      }
      #endregion

      #region UI Events
      private void scrollViewer_MouseMove(object sender, MouseEventArgs e)
      { 
         CheckScrollBar((e == null ? null : e.OriginalSource));
      }

      private void txtRaw_TextChanged(object sender, TextChangedEventArgs e)
      {
         scrollViewer.UpdateLayout();//Obligatoire sinon la taille de la scroll bar n’est pas rafraichie avant de passer dans la méthode [j’y ai passé un petit moment avant de comprendre ca !!!]
         CheckScrollBar(null);
      }
      #endregion

      #region Scroll Management
      private void CheckScrollBar(object origin)
      {
         double scrollableHeight = scrollViewer.ScrollableHeight;
         double verticalOffset = scrollViewer.VerticalOffset;

         if (verticalOffset != _lastVerticalOffset ||   // Si la position a changée ou 
             scrollableHeight != _lastScrollableHeight) // si la position max a changée 
         {
            if (origin != null && origin is Rectangle) // Si l’origine est un Rectangle, alors l’utilisateur a déplacé la ScrollBar
               ScrollChangedByMouseMove();
            else if (_hasToCaret == true// Si ce n’est pas le cas et que l’on doit auto-scroller… YES WE CAN
               ScrollToCaret();

            _lastVerticalOffset = verticalOffset;
            _lastScrollableHeight = scrollableHeight;
         }
      }

      private void ScrollChangedByMouseMove()
      {
         if (AutoScrollToCaret == true)
         {
            if (scrollViewer.VerticalOffset != scrollViewer.ScrollableHeight) // Si la position de la ScrollBar n’est pas au max
            {
               _hasToCaret = false; // on désactive l’auto-scroll
            }
            else // sinon
            {
               _hasToCaret = true; // on l’active 
            }
         }
      }

      private void ScrollToCaret()
      {
         scrollViewer.ScrollToVerticalOffset(scrollViewer.ScrollableHeight);
      }
      #endregion

      #region Add Text
      private void addText(string str)
      {
         txtRaw.Text += str;
      }

      internal void AddText(string str)
      {
         txtRaw.Dispatcher.BeginInvoke(new StringDelegate(addText), str);
      }
      #endregion
   }
}

Cette source parait toute conne maintenant qu’elle fonctionne, mais j’ai bien tourné en rond avant de m’en sortir !

Voila, si ca peut dépanner une ou deux personnes, ce sera déjà pas mal.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Publié lundi 5 janvier 2009 00:00 par NeuroCypher

Commentaires

Pas de commentaires
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01

- Office 365: Script PowerShell pour supprimer des comptes utilisateur de collections de sites SharePoint Online par Blog Technique de Romelard Fabrice le 11-19-2018, 16:47

- Retrouvez-moi aux Microsoft experiences18 ! par Blog de Jérémy Jeanson le 11-06-2018, 22:25

- Office 365: Script PowerShell pour extraire les Teams associés à un utilisateur spécifié par Blog Technique de Romelard Fabrice le 11-06-2018, 13:37