web-dev-qa-db-fra.com

Zone de texte multiligne avec défilement vertical automatique

Il y a beaucoup de questions similaires sur Internet, sur SO inclus, mais les solutions proposées ne fonctionnent pas dans mon cas. Scénario: il y a une zone de texte de journal dans xaml

 <TextBox Name="Status"
          Margin="5"
          Grid.Column="1"
          Grid.Row="5"
          HorizontalAlignment="Left"
          VerticalAlignment="Top"
          Width="600"
          Height="310"/>

Il existe des méthodes dans le code-behind qui fonctionnent et ajoutent des messages multilignes (c'est peut-être le problème?) Dans cette zone de texte:

private static void DoSomeThings(TextBox textBox)
{
   // do work
   textBox.AppendText("Work finished\r\n"); // better way than Text += according to msdn
   // do more
   textBox.AppendText("One more message\r\n");
   ...
}

private static void DoSomething2(TextBox textBox)
{
   // same as first method
}

Vous devez faire défiler vers le bas de la zone de texte une fois toutes les actions effectuées. Testé ScrollToEnd (), ScrollToLine, encapsulant la zone de texte dans ScrollViewer, Selection et Caret, contournant ScrollToEnd à TextChanged. Rien de tout cela fonctionne, après les lignes d'exécution qui dépassent la hauteur de la zone de texte doivent encore être défilées manuellement. Désolé pour la question en double, je suppose que je manque quelques problèmes mineurs qui peuvent être résolus rapidement par quelqu'un qui a une nouvelle vision du problème. Merci d'avance.

41
Jaded

Selon cette question: TextBox.ScrollToEnd ne fonctionne pas lorsque le TextBox est dans un onglet non actif

Vous devez concentrer la zone de texte, mettre à jour la position du curseur, puis faire défiler jusqu'à la fin:

Status.Focus();
Status.CaretIndex = Status.Text.Length;
Status.ScrollToEnd();

[~ # ~] modifier [~ # ~]

Exemple de TextBox:

<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" 
         AcceptsReturn="True" Name="textBox"/>
81
Adrian Fâciu

Si vous en faites un simple contrôle personnalisé, vous n'avez pas besoin de code pour effectuer le défilement.

public class ScrollingTextBox : TextBox {

    protected override void OnInitialized (EventArgs e) {
        base.OnInitialized(e);
        VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
    }

    protected override void OnTextChanged (TextChangedEventArgs e) {
        base.OnTextChanged(e);
        CaretIndex = Text.Length;
        ScrollToEnd();
    }

}

Si vous utilisez WPF, il serait bien préférable d'utiliser la liaison plutôt que de passer la zone de texte dans le code derrière.

17
Lee Willis

Si vous n'aimez pas trop le code, voici une propriété attachée qui fera l'affaire:

namespace YourProject.YourAttachedProperties
{

    public class TextBoxAttachedProperties
    {

        public static bool GetAutoScrollToEnd(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoScrollToEndProperty);
        }

        public static void SetAutoScrollToEnd(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoScrollToEndProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoScrollToEnd.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoScrollToEndProperty =
        DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(TextBoxAttachedProperties), new PropertyMetadata(false, AutoScrollToEndPropertyChanged));

        private static void AutoScrollToEndPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if(d is TextBox textbox && e.NewValue is bool mustAutoScroll)
            {
                textbox.TextChanged += (s, ee)=> AutoScrollToEnd(s, ee, textbox);
            }
        }

        private static void AutoScrollToEnd(object sender, TextChangedEventArgs e, TextBox textbox)
        {
            textbox.ScrollToEnd();
        }
    }
}

Et puis dans votre xaml, faites simplement:

<TextBox
    AcceptsReturn="True"
    myAttachedProperties:TextBoxAttachedProperties.AutoScrollToEnd="True"/>

N'oubliez pas d'ajouter en haut de votre fichier xaml

xmlns:myAttachedProperties="clr-namespace:YourProject.YourAttachedProperties"

Et voila

4
yan yankelevich

Merci! J'ai ajouté ceci pour me rappeler le focus d'origine:

var oldFocusedElement = FocusManager.GetFocusedElement(this);

this.textBox.Focus();
this.textBox.CaretIndex = this.textBox.Text.Length;
this.textBox.ScrollToEnd();

FocusManager.SetFocusedElement(this, oldFocusedElement);
2
Darko D.