WPF TextBlock中的自动垂直滚动条?

397

我在WPF中有一个TextBlock,我写了很多行文字,超出了它的垂直高度。我期望当这种情况发生时,竖直滚动条会自动出现,但实际上没有出现。我尝试在属性窗格中查找滚动条属性,但未能找到。

如何使我的TextBlock在其内容超过其高度时自动创建垂直滚动条?

澄清一下:我更愿意通过设计器完成而不是直接编写XAML。

10个回答

636

将其包装在滚动查看器中:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

注意:本答案适用于原问题中所要求的TextBlock(只读文本元素)。

如果您想在TextBox(可编辑文本元素)中显示滚动条,则可以使用ScrollViewer附加属性:

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

这两个属性的有效值包括DisabledAutoHiddenVisible


2
我该如何从设计师中实现它? - Bab Yogoo
19
抱歉,我不确定,我没有使用WPF设计器。我认为,如果您直接添加XAML,设计器将自动更新。 - Drew Noakes
5
@conqenator TextBox.ScrollToEnd(); - Petey B
2
@Greg,这个问题是关于TextBlock而不是TextBox的。 - Drew Noakes
7
如果包含元素没有强制设置高度,有时需要在ScrollViewer上设置MaxHeight以强制出现滚动条。 - HackerBaloo
显示剩余6条评论

114

现在可以使用以下代码:

<TextBox Name="myTextBox" 
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True">SOME TEXT
</TextBox>

22
我理解原问题是关于TextBlock而不是TextBox的(如标题和开头所述),但第二段提到了TextBox。为了明确,这个答案绝对是处理文本框的最佳方法,而我知道的最佳处理文本块的方法则是我的答案 :) - Drew Noakes
2
对我来说也行得通。至少对于一个TextBox,在使用像被采纳的答案中所述的ScrollViewer时,TextBox的边框会消失,因为整个控件被滚动了,而不仅仅是其内容。 - Fueled

25

更好的方式是:

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

使用网格可以确保文本块中的文本不会溢出并重叠在文本块下面的元素上,因为如果您不使用网格,则可能会发生这种情况。当我尝试其他解决方案时,即使文本块已经在带有其他元素的网格中,这种情况也发生了。请记住,文本块的宽度应该是自动的,并且您应该在网格元素中指定所需的宽度。我在我的代码中这样做,效果非常好。HTH。


13
<ScrollViewer MaxHeight="50"  
              Width="Auto" 
              HorizontalScrollBarVisibility="Disabled"
              VerticalScrollBarVisibility="Auto">
     <TextBlock Text="{Binding Path=}" 
                Style="{StaticResource TextStyle_Data}" 
                TextWrapping="Wrap" />
</ScrollViewer>

我通过在ScrollViewer中设置MaxHeight的方式以另一种方式来实现这一点。

只需调整MaxHeight即可显示更多或更少的文本行。非常容易。


9
<ScrollViewer Height="239" VerticalScrollBarVisibility="Auto">
    <TextBox AcceptsReturn="True" TextWrapping="Wrap" LineHeight="10" />
</ScrollViewer>

这是在XAML中使用滚动TextBox并将其用作文本区域的方法。

1
问题与 TextBlock 有关,而不是 TextBox - Afzaal Ahmad Zeeshan
1
虽然不是完全正确的答案,但我发现VerticalScrollBarVisibility是一个有用的提示,所以给你点赞。 - Malachi

4

不知道是否有其他人遇到过这个问题,将我的TextBlock包装成一个ScrollViewer会在某种程度上破坏我的UI - 作为一个简单的解决方法,我发现将TextBlock替换为像这样的TextBox

<TextBox  SelectionBrush="Transparent" 
          Cursor="Arrow" 
          IsReadOnly="True" 
          Text="{Binding Text}" 
          VerticalScrollBarVisibility="Auto">

创建一个类似于带滚动条的的(并且您可以在设计器中完成所有操作)。

3

以下答案描述了使用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。
  • 由于使用了“Append”,因此速度非常快。
  • 任何其他 ViewModel 都可以通过调用日志记录 ViewModel 上的方法来生成日志消息。
  • 由于我们使用了内置于 TextBox 的 ScrollViewer,因此每次添加新消息时可以使其自动滚动到文本框底部。

3

2
我尝试将这些建议应用于文本块,但无法使其正常工作。我甚至尝试从设计师中使其正常工作。(在布局中查看并通过单击底部的向下箭头“V”展开列表)我尝试将滚动视图设置为可见然后自动,但仍无法使其正常工作。
最终,我放弃了,并将TextBlock更改为具有只读属性集的TextBox,它就像魔法般地工作了。

1
这是一个简单的解决方案。只有在文本溢出时,垂直滚动条才会被激活。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接