前言
使用WPF 中的RichTextBox控件实现添加文本后自动滚动末尾。因为RichTextBox无法直接绑定数据,所以通过引用System.Windows.Interactivity实现(System.Windows.Interactivity.WPF)
代码
MainWindow.xaml
csharp
<Window x:Class="WPF_MvvmDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:behavior="clr-namespace:WPF_MvvmDemo"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<RichTextBox Name="OutputListBox" Grid.Row="0"
Margin="5" Background="#FFFFFF" Focusable="True"
VerticalScrollBarVisibility="Auto">
<!--数据绑定-->
<i:Interaction.Behaviors>
<behavior:RichTextBoxBehavior Document="{Binding RichTextContent, Mode=TwoWay}" />
</i:Interaction.Behaviors>
<!--自动滚动-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<ei:CallMethodAction MethodName="ScrollToEnd" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RichTextBox>
<Button Grid.Row="2" Width="150" Margin="5" HorizontalAlignment="Right"
Command="{Binding AppendTextCommand}">AddText</Button>
</Grid>
</Window>
MainWindow
csharp
public partial class MainWindow : Window
{
MainViewModel viewModel;
public MainWindow()
{
InitializeComponent();
viewModel=new MainViewModel();
this.DataContext = viewModel;
}
}
RelayCommand
csharp
public class RelayCommand : ICommand
{
public Action<string> execute;
public RelayCommand(Action<string> execute)
{
this.execute = execute;
}
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return CanExecuteChanged!=null;
}
public void Execute(object? parameter)
{
execute?.Invoke(parameter?.ToString());
}
}
RichTextBoxBehavior
csharp
/// <summary>
///RichTextBox 的 Document 属性不是一个依赖属性(DependencyProperty),因此不能直接进行双向数据绑定。
///我们需要使用一种间接的方式来实现数据绑定。
///解决方案:
///使用 Behavior 来实现 RichTextBox 的数据绑定。Behavior 是一个附加行为,可以在 XAML 中使用,
///通过代码来实现复杂的行为逻辑。
/// </summary>
public class RichTextBoxBehavior : Behavior<RichTextBox>
{
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document", typeof(FlowDocument), typeof(RichTextBoxBehavior),
new FrameworkPropertyMetadata(null, OnDocumentChanged));
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as RichTextBoxBehavior;
if (behavior != null)
{
behavior.UpdateRichTextBox((FlowDocument)e.NewValue);
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
AssociatedObject.TextChanged += OnTextChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
AssociatedObject.TextChanged -= OnTextChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
UpdateRichTextBox(Document);
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
Document = AssociatedObject.Document;
}
private void UpdateRichTextBox(FlowDocument document)
{
if (document != null && AssociatedObject.Document != document)
{
AssociatedObject.Document = document;
}
}
}