Многострочное текстовое поле с автоматической вертикальной прокруткой

В Интернете есть много похожих вопросов, включая SO, но предлагаемые решения не работают в моем случае. Сценарий: в xaml есть текстовое поле журнала

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

В коде программной части есть методы, которые выполняют некоторую работу и добавляют несколько многострочных (может быть, в этом проблема?) Сообщений в это текстовое поле:

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
}

После выполнения всех действий необходимо прокрутить текстовое поле вниз. Пробовал ScrollToEnd (), ScrollToLine, перенос текстового поля в ScrollViewer, обходные пути Selection и Caret, прикрепляя ScrollToEnd к TextChanged. Ничего из этого не работает, после того, как строки выполнения, которые превышают высоту текстового поля, все равно необходимо прокрутить вручную. Извините за повторяющийся вопрос, я полагаю, мне не хватает некоторых незначительных проблем, которые могут быть быстро решены кем-то, у кого есть свежее видение проблемы. Заранее спасибо.


person Jaded    schedule 20.01.2012    source источник
comment
Когда вы говорите, что вам нужно прокрутить до [нижней] нижней части [] текстовых полей, вы действительно имеете в виду прокрутку, поскольку последний добавленный текст полностью виден? Или вы хотите, чтобы курсор находился в самом конце текстового поля?   -  person Daniel Hilgarth    schedule 20.01.2012
comment
Первый. Чтобы проиллюстрировать сделанный снимок экрана - я получаю слева, справа - то, что мне нужно (прокрутка вручную): i.piccy.info/i7/0c105234c75b7031df050587f72771b4/1-5-3848/   -  person Jaded    schedule 20.01.2012


Ответы (4)


Согласно этому вопросу: TextBox .ScrollToEnd не работает, если TextBox находится на неактивной вкладке

Вам нужно сфокусировать текстовое поле, обновить положение курсора и затем прокрутить до конца:

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

ИЗМЕНИТЬ

Пример TextBox:

<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" 
         AcceptsReturn="True" Name="textBox"/>
person Adrian Fâciu    schedule 20.01.2012
comment
Пробовал. Каретка действительно стоит после последней строки в текстовом поле, но прокрутки к ней не происходит, я все еще вижу верхние строки после запуска приложения (см. Левую часть изображения в комментарии выше). - person Jaded; 20.01.2012
comment
Я добавил пример текстового поля, которое использую, и прокрутка работает. Возможно, вам потребуется добавить свойство TextWrapping. - person Adrian Fâciu; 20.01.2012
comment
Похоже, проблема заключается в том, что текстовое поле не имеет возможности обновиться / аннулировать себя, когда операции выполняются после загрузки приложения (в конструкторе страницы). Когда я помещаю логику в обработчик кнопок и выполняю ее вручную, прокрутка действительно работает как шарм. edit Loaded + = (sender, args) = ›TestOperations () в ctor страницы помогло. Спасибо за помощь. Единственное, что я хотел бы знать, какой метод я мог бы выполнить вручную, чтобы добиться того же эффекта, оставив код в конструкторе страницы. - person Jaded; 20.01.2012
comment
Вы выполняете свой код до или после вызова InitializeComponents? - person Daniel Hilgarth; 20.01.2012
comment
Я добавил код в конструктор страницы после метода InitializeComponents, и у меня он по-прежнему работает нормально. - person Adrian Fâciu; 20.01.2012
comment
Нет, код был после InitializeComponents, например: InitializeComponent (); ShowsNavigationUI = false; const bool debugMode = true; если (debugMode) {TestOperations (); } // требование приложения - person Jaded; 20.01.2012

Если вы превратите его в простой настраиваемый элемент управления, вам не понадобится никакого кода для прокрутки.

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();
    }

}

Если вы используете WPF, было бы гораздо лучше использовать привязку, а не передавать текстовое поле в коде позади.

person Lee Willis    schedule 13.02.2014
comment
+1 Намного проще, чем другие сообщения на ту же тему. Я даже оставил только OnTextChanged (...) {base.OnTextChanged (e); ScrollToEnd ();}, потому что для моего простого приложения этого было достаточно. - person NGI; 11.09.2015

Если вам не очень нравится код, вот AttachedProperty, который поможет:

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 && mustAutoScroll)
            {
                textbox.TextChanged += (s, ee)=> AutoScrollToEnd(s, ee, textbox);
            }
        }

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

А затем в вашем xaml просто сделайте:

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

Только не забудьте добавить вверху вашего xaml файла

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

И вуаля

person yan yankelevich    schedule 03.08.2018
comment
Классный раствор. Лучший ответ на этот вопрос. Спасибо! - person Frank im Wald; 24.05.2019
comment
Пятно на. Работает как шарм. - person ardmark; 30.11.2020

Спасибо! Я добавил это, чтобы запомнить исходный фокус:

var oldFocusedElement = FocusManager.GetFocusedElement(this);

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

FocusManager.SetFocusedElement(this, oldFocusedElement);
person Darko D.    schedule 20.11.2013