如何让带有滚动条的WPF TextBox在添加行时自动滚动到底部?

问题描述 投票:0回答:5

例如,像 Visual Studio 的“输出”窗口一样。

有没有办法在XAML中做到这一点?

.net wpf scroll
5个回答
75
投票

您可以处理 TextChanged 事件,只要您更改 TextBox 的文本,该事件就会触发:

TextBoxBase.ScrollToEnd()
.


11
投票

在 XAML 中有一种方法可以做到这一点,您可以使用此样式来像控制台一样显示它(请注意缺点,它只是看起来像控制台,但并不完全像控制台一样)

        <Style x:Key="ConsoleTextBox" TargetType="{x:Type TextBox}">
            <Setter Property="IsReadOnly" Value="True"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <ScrollViewer RenderTransformOrigin="0.5,0.5" VerticalScrollBarVisibility="Auto">
                            <ScrollViewer.RenderTransform>
                                <ScaleTransform ScaleY="-1"/>
                            </ScrollViewer.RenderTransform>
                            <TextBox Text="{TemplateBinding Text}" RenderTransformOrigin="0.5,0.5">
                                <TextBox.RenderTransform>
                                    <ScaleTransform ScaleY="-1"/>
                                </TextBox.RenderTransform>
                            </TextBox>
                        </ScrollViewer>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

它是如何工作的

在 TextBox 内部,垂直翻转 ScrollViewer(“新”行添加在底部)

在 ScrollViewer 中,还有另一个文本框,可以垂直翻转以正确显示文本(而不是颠倒)。

使用样式

将其包含在您的 App.xaml 中或通过 ResourceDictionary 并将 TextBox 的样式设置为 ConsoleTextBox。

<TextBox Style="{StaticResource ConsoleTextBox}"/>

缺点

  • 当您从此“控制台”复制文本时,将不会出现换行符。
  • 鼠标滚动是反向的

9
投票

您可以编写一个附加属性,甚至更好的行为来侦听TextChanged事件并在回调中滚动到底部


7
投票

Visual Studio 输出窗口的行为很特殊,因为如果插入符号位于文本框的末尾,它只会保持自动向下滚动,这样您就可以检查输出,而不会在添加新行时受到干扰。

我用这段代码有这样的行为

bool scrollToEnd = TbEvents.CaretIndex == TbEvents.Text.Length;
TbEvents.AppendText(text + Environment.NewLine);
if (scrollToEnd)
{
    TbEvents.CaretIndex = TbEvents.Text.Length;
    TbEvents.ScrollToEnd();
}

0
投票

如果您将文本框包裹在 ScrollViewer 中,就像我的如下所示:

<ScrollViewer x:Name="ConsoleOutputScrollViewer" Background="Black">
    <StackPanel>
        <TextBox x:Name="ConsoleOutputTextBox" TextChanged="ConsoleOutputTextBox_TextChanged"
                 Background="Transparent" Foreground="White" BorderThickness="0" FontSize="15"
                 Padding="5 0 0 0" FontFamily="Consolas" TextWrapping="Wrap" IsReadOnly="True"/>
        <Grid Height="100"/>
    </StackPanel>
</ScrollViewer>

然后,您可以处理

TextChanged
事件并调用
ScrollToEnd()
,就像此处其他答案中提到的那样。然而,这样做的缺点是,添加新行时您将无法查看文本历史记录,这可能会给用户带来非常糟糕的体验,特别是当您不断添加新行时。

一个简单的解决方案是检查并比较当前滚动查看器

VerticalOffset
与 ScrollViewer 的总
ScrollableHeight
,如下所示:

private void ConsoleOutputTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    if (ConsoleOutputScrollViewer.VerticalOffset == ConsoleOutputScrollViewer.ScrollableHeight)
    {
        ConsoleOutputScrollViewer.ScrollToEnd();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.