Автоматическая вертикальная полоса прокрутки в WPF TextBlock?

336

У меня есть TextBlockв WPF. Я пишу много строк, намного превышающих его вертикальную высоту. Я ожидал, что вертикальная полоса прокрутки появится автоматически, когда это произойдет, но это не так. Я попытался найти свойство полосы прокрутки на панели свойств, но не смог найти.

Как сделать так, чтобы вертикальная полоса прокрутки создавалась автоматически, TextBlockкогда ее содержимое превышает ее высоту?

Пояснение: я бы предпочел сделать это от дизайнера, а не напрямую писать в XAML.

Баб Йогу
источник
1
Перечитав этот вопрос, я заметил, что вы упоминаете TextBlockдважды и TextBoxодин раз.
Дрю Ноакс

Ответы:

555

Оберните это в просмотрщик прокрутки:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

ПРИМЕЧАНИЕ. Этот ответ применяется к TextBlock(текстовому элементу только для чтения), как было задано в исходном вопросе.

Если вы хотите показать полосы прокрутки в TextBox(редактируемый текстовый элемент), тогда используйте ScrollViewerприкрепленные свойства:

<TextBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
         ScrollViewer.VerticalScrollBarVisibility="Auto" />

Допустимые значения для этих двух свойств являются Disabled, Auto, Hiddenи Visible.

Дрю Ноакс
источник
2
Как мне это сделать от дизайнера?
Баб Йогу
16
Извините, я не уверен, я не использую дизайнер WPF. Я думаю, что если вы добавите XAML напрямую, дизайнер обновится сам.
Дрю Ноакс
5
@conqenator TextBox.ScrollToEnd ();
Petey B
2
@ Грег, вопрос TextBlockне в этом TextBox.
Дрю Ноакс
7
Иногда MaxHeight на Scrollviewer необходим, чтобы заставить скролл появляться, если вмещающий элемент не обеспечивает какую-либо высоту.
HackerBaloo
106

можете использовать следующее сейчас:

<TextBox Name="myTextBox" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True">SOME TEXT
</TextBox>
Vince
источник
19
@jjnguy, я интерпретировал первоначальный вопрос как о TextBlockне TextBox(как в названии и вводной линии), но во втором пункте упоминается TextBox. Чтобы быть ясным, этот ответ, безусловно, лучший подход для текстовых блоков , и мой лучший из всех, что я знаю для текстовых блоков :)
Дрю Ноакс
@ Дрю, ах, имеет смысл. Спасибо за разъяснения.
jjnguy
2
Работал лучше для меня тоже. По крайней мере для TextBox, при использовании ScrollViewer вокруг него, как в принятом ответе, границы TextBox исчезают, потому что прокручивается весь элемент управления, а не только его содержимое.
заправлено
20

Что-то лучше было бы:

<Grid Width="Your-specified-value" >
    <ScrollViewer>
         <TextBlock Width="Auto" TextWrapping="Wrap" />
    </ScrollViewer>
</Grid>

Это гарантирует, что текст в вашем текстовом блоке не переполняется и не перекрывает элементы ниже текстового блока, как это может быть в случае, если вы не используете сетку. Это случилось со мной, когда я попробовал другие решения, хотя текстовый блок уже был в сетке с другими элементами. Имейте в виду, что ширина текстового блока должна быть Авто, и вы должны указать желаемое значение в элементе Grid. Я сделал это в своем коде, и это прекрасно работает. НТН.

varagrawal
источник
7
<ScrollViewer Height="239" VerticalScrollBarVisibility="Auto">
    <TextBox AcceptsReturn="True" TextWrapping="Wrap" LineHeight="10" />
</ScrollViewer>

Это способ использовать прокручиваемый TextBox в XAML и использовать его как текстовую область.

Джон
источник
1
Вопрос связан с TextBlockнет TextBox.
Афзаал Ахмад Зеешан
Не совсем правильный ответ, но я нашел VerticalScrollBarVisibility как полезную подсказку, поэтому +1
Малахия
4

Этот ответ описывает решение с использованием MVVM.

Это решение отлично подходит, если вы хотите добавить окно регистрации в окно, которое автоматически прокручивается вниз при каждом добавлении нового сообщения регистрации.

После добавления этих прикрепленных свойств их можно повторно использовать где угодно, что делает их очень модульным и многократно используемым программным обеспечением.

Добавьте этот XAML:

<TextBox IsReadOnly="True"   
         Foreground="Gainsboro"                           
         FontSize="13" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True"
         attachedBehaviors:TextBoxApppendBehaviors.AppendText="{Binding LogBoxViewModel.AttachedPropertyAppend}"                                       
         attachedBehaviors:TextBoxClearBehavior.TextBoxClear="{Binding LogBoxViewModel.AttachedPropertyClear}"                                    
         TextWrapping="Wrap">

Добавьте это прикрепленное свойство:

public static class TextBoxApppendBehaviors
{
    #region AppendText Attached Property
    public static readonly DependencyProperty AppendTextProperty =
        DependencyProperty.RegisterAttached(
            "AppendText",
            typeof (string),
            typeof (TextBoxApppendBehaviors),
            new UIPropertyMetadata(null, OnAppendTextChanged));

    public static string GetAppendText(TextBox textBox)
    {
        return (string)textBox.GetValue(AppendTextProperty);
    }

    public static void SetAppendText(
        TextBox textBox,
        string value)
    {
        textBox.SetValue(AppendTextProperty, value);
    }

    private static void OnAppendTextChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if (args.NewValue == null)
        {
            return;
        }

        string toAppend = args.NewValue.ToString();

        if (toAppend == "")
        {
            return;
        }

        TextBox textBox = d as TextBox;
        textBox?.AppendText(toAppend);
        textBox?.ScrollToEnd();
    }
    #endregion
}

И это прикрепленное свойство (очистить поле):

public static class TextBoxClearBehavior
{
    public static readonly DependencyProperty TextBoxClearProperty =
        DependencyProperty.RegisterAttached(
            "TextBoxClear",
            typeof(bool),
            typeof(TextBoxClearBehavior),
            new UIPropertyMetadata(false, OnTextBoxClearPropertyChanged));

    public static bool GetTextBoxClear(DependencyObject obj)
    {
        return (bool)obj.GetValue(TextBoxClearProperty);
    }

    public static void SetTextBoxClear(DependencyObject obj, bool value)
    {
        obj.SetValue(TextBoxClearProperty, value);
    }

    private static void OnTextBoxClearPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs args)
    {
        if ((bool)args.NewValue == false)
        {
            return;
        }

        var textBox = (TextBox)d;
        textBox?.Clear();
    }
}   

Затем, если вы используете инфраструктуру внедрения зависимостей, такую ​​как MEF, вы можете поместить весь специфичный для журналирования код в его собственную ViewModel:

public interface ILogBoxViewModel
{
    void CmdAppend(string toAppend);
    void CmdClear();

    bool AttachedPropertyClear { get; set; }

    string AttachedPropertyAppend { get; set; }
}

[Export(typeof(ILogBoxViewModel))]
public class LogBoxViewModel : ILogBoxViewModel, INotifyPropertyChanged
{
    private readonly ILog _log = LogManager.GetLogger<LogBoxViewModel>();

    private bool _attachedPropertyClear;
    private string _attachedPropertyAppend;

    public void CmdAppend(string toAppend)
    {
        string toLog = $"{DateTime.Now:HH:mm:ss} - {toAppend}\n";

        // Attached properties only fire on a change. This means it will still work if we publish the same message twice.
        AttachedPropertyAppend = "";
        AttachedPropertyAppend = toLog;

        _log.Info($"Appended to log box: {toAppend}.");
    }

    public void CmdClear()
    {
        AttachedPropertyClear = false;
        AttachedPropertyClear = true;

        _log.Info($"Cleared the GUI log box.");
    }

    public bool AttachedPropertyClear
    {
        get { return _attachedPropertyClear; }
        set { _attachedPropertyClear = value; OnPropertyChanged(); }
    }

    public string AttachedPropertyAppend
    {
        get { return _attachedPropertyAppend; }
        set { _attachedPropertyAppend = value; OnPropertyChanged(); }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Вот как это работает:

  • ViewModel переключает вложенные свойства для управления TextBox.
  • Поскольку он использует «Добавить», это молниеносно.
  • Любой другой ViewModel может генерировать сообщения регистрации, вызывая методы в ViewModel ведения журнала.
  • Поскольку мы используем ScrollViewer, встроенный в TextBox, мы можем автоматически прокручивать его до конца текстового поля каждый раз, когда добавляется новое сообщение.
Контанго
источник
4
<ScrollViewer MaxHeight="50"  
              Width="Auto" 
              HorizontalScrollBarVisibility="Disabled"
              VerticalScrollBarVisibility="Auto">
     <TextBlock Text="{Binding Path=}" 
                Style="{StaticResource TextStyle_Data}" 
                TextWrapping="Wrap" />
</ScrollViewer>

Я делаю это по-другому, помещая MaxHeight в ScrollViewer.

Просто настройте MaxHeight, чтобы показать больше или меньше строк текста. Легко.

Тони Ву
источник
1

Я пытался заставить эти предложения работать для текстового блока, но не смог заставить его работать. Я даже пытался заставить его работать у дизайнера. (Посмотрите в Layout и разверните список, нажав стрелку вниз «V» внизу). Я попытался установить для scrollviewer значение Visible, а затем Auto , но оно все равно не работает.

В конце концов я сдался и изменил на TextBlocka TextBoxс набором атрибутов Readonly , и это сработало как шарм.

Скотт Борделон
источник
0

Не знаю , если кто - то имеет эту проблему , но обертывание мою TextBlockв ScrollViewersomewhow перепутались мой UI - как простое решение , я понял, что замена TextBlockна TextBoxкак этот

<TextBox  Name="textBlock" SelectionBrush="Transparent" Cursor="Arrow" IsReadOnly="True" Text="My Text" VerticalScrollBarVisibility="Auto">

создает, TextBoxкоторый выглядит и ведет себя как TextBlockс полосой прокрутки (и вы можете сделать все это в дизайнере).

Dunkleosteus
источник
0

Это простое решение этого вопроса. Вертикальная прокрутка будет активирована только при переполнении текста.

<TextBox Text="Try typing some text here " ScrollViewer.VerticalScrollBarVisibility="Auto" TextWrapping="WrapWithOverflow" />

Зухайр
источник