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


335

TextBlock在WPF中有一个。我在上面写了很多行,远远超过了它的垂直高度。我希望垂直滚动条会在发生这种情况时自动出现,但事实并非如此。我试图在“属性”窗格中查找滚动条属性,但找不到一个。

TextBlock一旦内容超过其高度,如何使垂直滚动条自动创建?

澄清:我宁愿由设计人员来做,而不是直接写给XAML。


1
重读此问题后,我注意到您提到了TextBlock两次TextBox
Drew Noakes

Answers:


554

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

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

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

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

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

对于这两个属性的有效值为DisabledAutoHiddenVisible


2
我应该如何从设计师那里做?
Bab Yogoo

16
抱歉,我不确定,我没有使用WPF设计器。我认为,如果直接添加XAML,设计器将自行更新。
德鲁·诺阿克斯

5
@conqenator TextBox.ScrollToEnd();
Petey B

2
@Greg,问题TextBlock不是TextBox
Drew Noakes

7
有时,如果封闭元素没有强制任何高度,则需要在Scrollviewer上使用MaxHeight来强制scoll出现。
HackerBaloo

106

现在可以使用以下内容:

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

19
@jjnguy,我将最初的问题解释为TextBlock不是TextBox(如标题和开头部分),而是提到的第二段TextBox。明确地说,这个答案绝对是用于文本的最佳方法,而我的答案是我所知道的文本的最佳方法:)
Drew Noakes

@Drew,啊,很有道理。感谢您的澄清。
jjnguy 2011年

2
对我来说也更好。至少对于一个TextBox,当在其周围使用ScrollViewer时,就像在接受的答案中一样,TextBox的边框消失了,因为整个控件都滚动了,而不仅仅是滚动了它的内容。
加油

20

更好的是:

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

这可以确保您的文本块中的文本不会溢出,并且与不使用网格的情况一样,文本块下面的元素不会重叠。当我尝试其他解决方案时,即使文本块已经与其他元素一起位于网格中,这也发生了。请记住,文本块的宽度应为“自动”,并且应在Grid元素中指定所需的with。我在代码中做到了这一点,并且效果很好。HTH。


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

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


1
这个问题TextBlock与否有关TextBox
Afzaal Ahmad Zeeshan 2014年

答案不是很正确,但是我发现VerticalScrollBarVisibility是一个有用的提示,所以+1
Malachi 2014年

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上的方法来生成日志记录消息。
  • 当我们使用内置在TextBox中的ScrollViewer时,可以使它在每次添加新消息时自动滚动到文本框的底部。

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,但仍然无法正常工作。

我最终放弃了,将其更改TextBlockTextBox具有Readonly属性集的,它的工作方式就像是一种魅力。


0

不知道如果别人有这个问题,但我的包裹TextBlock成一个ScrollViewer搞砸了我的UI somewhow -作为一个简单的解决方法我想通了,更换TextBlock一个TextBox像这样的

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

创建一个TextBox外观和行为类似于的TextBlock滚动条(您可以在设计器中完成所有操作)。


0

这是对该问题的简单解决方案。仅当文本溢出时,才会激活垂直滚动。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.