WPF文本块中的自动垂直滚动条?

时间:2022-05-18 08:42:23

I have a TextBlock in WPF. I write many lines to it, far exceeding its vertical height. I expected a vertical scroll bar to appear automatically when that happens, but it didn't. I tried to look for a scroll bar property in the Properties pane, but could not find one.

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

How can I make vertical scroll bar created automatically for my TextBlock once its contents exceed its height?

当文本块的内容超过其高度时,如何自动创建垂直滚动条?

Clarification: I would rather do it from the designer and not by directly writing to the XAML.

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

8 个解决方案

#1


459  

Wrap it in a scroll viewer:

将它封装在滚动查看器中:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

NOTE this answer applies to a TextBlock (a read-only text element) as asked for in the original question.

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

If you want to show scroll bars in a TextBox (an editable text element) then use the ScrollViewer attached properties:

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

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

Valid values for these two properties are Disabled, Auto, Hidden and Visible.

这两个属性的有效值是禁用的、自动的、隐藏的和可见的。

#2


90  

can use the following now:

现在可以使用以下内容:

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

#3


18  

Something better would be:

更好的是:

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

This makes sure that the text in your textblock does not overflow and overlap the elements below the textblock as may be the case if you do not use the grid. That happened to me when I tried other solutions even though the textblock was already in a grid with other elements. Keep in mind that the width of the textblock should be Auto and you should specify the desired with in the Grid element. I did this in my code and it works beautifully. HTH.

这确保textblock中的文本不会溢出,并且不会与textblock下面的元素重叠,如果不使用网格,可能会出现这种情况。当我尝试其他解决方案时,我就遇到了这种情况,尽管textblock已经与其他元素放在一个网格中。请记住,textblock的宽度应该是自动的,您应该在Grid元素中指定所需的宽度。我在代码中做了这个,它运行得很好。HTH。

#4


4  

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

This is way to use the scrolling TextBox in XAML and use it as a text area.

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

#5


3  

This answer describes a solution using MVVM.

这个答案描述了一个使用MVVM的解决方案。

This solution is great if you want to add a logging box to a window, that automatically scrolls to the bottom each time a new logging message is added.

如果您想向窗口添加一个日志记录框,那么这个解决方案是很好的,每当添加一个新的日志记录消息时,该窗口就会自动滚动到底部。

Once these attached properties are added, they can be reused anywhere, so it makes for very modular and reusable software.

一旦添加了这些附加的属性,它们就可以在任何地方重用,因此这就形成了非常模块化和可重用的软件。

Add this XAML:

添加这个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">

Add this attached property:

添加这个附加属性:

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
}

And this attached property (to clear the box):

并附此属性(清除框):

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

Then, if you're using a dependency injection framework such as MEF, you can place all of the logging-specific code into it's own ViewModel:

然后,如果您使用一个依赖注入框架,比如MEF,您可以将所有特定于logging的代码放在它自己的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
}

Here's how it works:

它是如何工作的:

  • The ViewModel toggles the Attached Properties to control the TextBox.
  • 视图模型切换附加的属性来控制文本框。
  • As it's using "Append", it's lightning fast.
  • 因为它使用的是“追加”,所以速度非常快。
  • Any other ViewModel can generate logging messages by calling methods on the logging ViewModel.
  • 任何其他的ViewModel都可以通过调用日志ViewModel上的方法来生成日志消息。
  • As we use the ScrollViewer built into the TextBox, we can make it automatically scroll to the bottom of the textbox each time a new message is added.
  • 当我们使用内置在文本框中的ScrollViewer时,我们可以使它在每次添加新消息时自动滚动到文本框的底部。

#6


3  

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

I am doing this in another way by putting MaxHeight in ScrollViewer.

我用另一种方式在ScrollViewer中输入MaxHeight。

Just Adjust the MaxHeight to show more or fewer lines of text. Easy.

只需调整MaxHeight以显示更多或更少的文本行。一件容易的事。

#7


2  

You can use

您可以使用

ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Visible"

These are attached property of wpf. For more information

这些是wpf的附加属性。的更多信息

http://wpfbugs.blogspot.in/2014/02/wpf-layout-controls-scrollviewer.html

http://wpfbugs.blogspot.in/2014/02/wpf-layout-controls-scrollviewer.html

#8


0  

Dont know if someone else has this problem but wrapping my TextBlock into a ScrollViewer somewhow messed up my UI - as a simple workaround I figured out that replacing the TextBlock by a TextBox like this one

我不知道是不是有人有这个问题,但是把我的TextBlock封装到一个ScrollViewer中,这会把我的UI搞砸——作为一个简单的解决方案,我发现用一个像这样的文本框来替换TextBlock

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

creates a TextBox that looks and behaves like a TextBlock with a scrollbar (and you can do it all in the designer).

创建一个文本框,它的外观和行为就像一个带有滚动条的文本块(您可以在设计器中完成所有操作)。

#1


459  

Wrap it in a scroll viewer:

将它封装在滚动查看器中:

<ScrollViewer>
    <TextBlock />
</ScrollViewer>

NOTE this answer applies to a TextBlock (a read-only text element) as asked for in the original question.

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

If you want to show scroll bars in a TextBox (an editable text element) then use the ScrollViewer attached properties:

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

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

Valid values for these two properties are Disabled, Auto, Hidden and Visible.

这两个属性的有效值是禁用的、自动的、隐藏的和可见的。

#2


90  

can use the following now:

现在可以使用以下内容:

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

#3


18  

Something better would be:

更好的是:

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

This makes sure that the text in your textblock does not overflow and overlap the elements below the textblock as may be the case if you do not use the grid. That happened to me when I tried other solutions even though the textblock was already in a grid with other elements. Keep in mind that the width of the textblock should be Auto and you should specify the desired with in the Grid element. I did this in my code and it works beautifully. HTH.

这确保textblock中的文本不会溢出,并且不会与textblock下面的元素重叠,如果不使用网格,可能会出现这种情况。当我尝试其他解决方案时,我就遇到了这种情况,尽管textblock已经与其他元素放在一个网格中。请记住,textblock的宽度应该是自动的,您应该在Grid元素中指定所需的宽度。我在代码中做了这个,它运行得很好。HTH。

#4


4  

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

This is way to use the scrolling TextBox in XAML and use it as a text area.

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

#5


3  

This answer describes a solution using MVVM.

这个答案描述了一个使用MVVM的解决方案。

This solution is great if you want to add a logging box to a window, that automatically scrolls to the bottom each time a new logging message is added.

如果您想向窗口添加一个日志记录框,那么这个解决方案是很好的,每当添加一个新的日志记录消息时,该窗口就会自动滚动到底部。

Once these attached properties are added, they can be reused anywhere, so it makes for very modular and reusable software.

一旦添加了这些附加的属性,它们就可以在任何地方重用,因此这就形成了非常模块化和可重用的软件。

Add this XAML:

添加这个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">

Add this attached property:

添加这个附加属性:

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
}

And this attached property (to clear the box):

并附此属性(清除框):

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

Then, if you're using a dependency injection framework such as MEF, you can place all of the logging-specific code into it's own ViewModel:

然后,如果您使用一个依赖注入框架,比如MEF,您可以将所有特定于logging的代码放在它自己的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
}

Here's how it works:

它是如何工作的:

  • The ViewModel toggles the Attached Properties to control the TextBox.
  • 视图模型切换附加的属性来控制文本框。
  • As it's using "Append", it's lightning fast.
  • 因为它使用的是“追加”,所以速度非常快。
  • Any other ViewModel can generate logging messages by calling methods on the logging ViewModel.
  • 任何其他的ViewModel都可以通过调用日志ViewModel上的方法来生成日志消息。
  • As we use the ScrollViewer built into the TextBox, we can make it automatically scroll to the bottom of the textbox each time a new message is added.
  • 当我们使用内置在文本框中的ScrollViewer时,我们可以使它在每次添加新消息时自动滚动到文本框的底部。

#6


3  

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

I am doing this in another way by putting MaxHeight in ScrollViewer.

我用另一种方式在ScrollViewer中输入MaxHeight。

Just Adjust the MaxHeight to show more or fewer lines of text. Easy.

只需调整MaxHeight以显示更多或更少的文本行。一件容易的事。

#7


2  

You can use

您可以使用

ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Visible"

These are attached property of wpf. For more information

这些是wpf的附加属性。的更多信息

http://wpfbugs.blogspot.in/2014/02/wpf-layout-controls-scrollviewer.html

http://wpfbugs.blogspot.in/2014/02/wpf-layout-controls-scrollviewer.html

#8


0  

Dont know if someone else has this problem but wrapping my TextBlock into a ScrollViewer somewhow messed up my UI - as a simple workaround I figured out that replacing the TextBlock by a TextBox like this one

我不知道是不是有人有这个问题,但是把我的TextBlock封装到一个ScrollViewer中,这会把我的UI搞砸——作为一个简单的解决方案,我发现用一个像这样的文本框来替换TextBlock

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

creates a TextBox that looks and behaves like a TextBlock with a scrollbar (and you can do it all in the designer).

创建一个文本框,它的外观和行为就像一个带有滚动条的文本块(您可以在设计器中完成所有操作)。