例如,像 Visual Studio 的“输出”窗口一样。
有没有办法在XAML中做到这一点?
您可以处理 TextChanged 事件,只要您更改 TextBox 的文本,该事件就会触发:
TextBoxBase.ScrollToEnd()
.
在 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}"/>
缺点
您可以编写一个附加属性,甚至更好的行为来侦听TextChanged事件并在回调中滚动到底部。
Visual Studio 输出窗口的行为很特殊,因为如果插入符号位于文本框的末尾,它只会保持自动向下滚动,这样您就可以检查输出,而不会在添加新行时受到干扰。
我用这段代码有这样的行为
bool scrollToEnd = TbEvents.CaretIndex == TbEvents.Text.Length;
TbEvents.AppendText(text + Environment.NewLine);
if (scrollToEnd)
{
TbEvents.CaretIndex = TbEvents.Text.Length;
TbEvents.ScrollToEnd();
}
如果您将文本框包裹在 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();
}
}