Can't scroll to end of document

SyntaxEditor for WPF Forum

Posted 7 months ago by Martin Kohler
Version: 23.1.2
Platform: .NET 6.0
Environment: Windows 10 (64-bit)
Avatar

Hi, I am using SyntaxEditor as a logging kind of control where new text is constantly added to the end. In relation to this I also want the SyntaxEditor control to always scroll to the bottom. I have tried a lot of different ways, how can I do this? This is my current code

<Window x:Class="WpfApp1.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:syntaxeditor="http://schemas.actiprosoftware.com/winfx/xaml/syntaxeditor"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        
        <Button Click="ButtonBase_OnClick" Grid.Row="0" Width="50" Content="Run" />
        <syntaxeditor:SyntaxEditor Grid.Row="1" x:Name="TestRunFeedbackBox" CanScrollPastDocumentEnd="False" IsWordWrapEnabled="True" IsDocumentReadOnly="True" DocumentTextChanged="TestRunFeedbackBox_OnDocumentTextChanged" FontFamily="Consolas" Background="{DynamicResource Control.EditorBackgroundBrush}" Foreground="{DynamicResource Control.ForegroundBrush}" Text="{Binding Text}" />
    </Grid>
</Window>



using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using ActiproSoftware.Text;
using ActiproSoftware.Windows.Controls.SyntaxEditor;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private ViewModel _viewModel;

        public MainWindow()
        {
            InitializeComponent();

            _viewModel = new ViewModel();
            DataContext = _viewModel;
        }

        private void TestRunFeedbackBox_OnDocumentTextChanged(object? sender, EditorSnapshotChangedEventArgs e)
        {
            var lineCount = TestRunFeedbackBox.Document.CurrentSnapshot.Lines.Count;

            TestRunFeedbackBox.ActiveView.Selection.CaretPosition = new TextPosition(lineCount - 1, 5);
            TestRunFeedbackBox.ActiveView.Scroller.ScrollToCaret();
            TestRunFeedbackBox.ActiveView.Scroller.ScrollToDocumentEnd();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            Task.Run(() =>
            {
                for (int i = 0; i < 10000; i++)
                {
                    Application.Current.Dispatcher.Invoke(() => _viewModel.Text += $"{i}\r\n");

                    Thread.Sleep(50);
                }
            });
        }
    }

    public class ViewModel : INotifyPropertyChanged
    {
        private string _text;
        public string Text
        {
            get => _text;
            set => Set(_text, value, v => _text = v);
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }

        private void Set<T>(T oldValue, T newValue, Action<T> setValue, [CallerMemberName] string propertyName = "")
        {
            if ((oldValue == null && newValue != null) || (oldValue != null && !oldValue.Equals(newValue)))
            {
                setValue(newValue);
                RaisePropertyChanged(propertyName);
            }
        }
    }
}

Comments (2)

Posted 7 months ago by Martin Kohler
Avatar

I have also tried this example from the documentation and it is not working

var scrollState = new TextViewScrollState(new TextPosition(23, 0), TextViewVerticalAnchorPlacement.Center, 0, 0);
this.ActiveView.Scroller.ScrollTo(scrollState);
Answer - Posted 7 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Martin,

I think what's happening here is that you are scrolling within the OnDocumentTextChanged event handler, but the IEditorViewSelection object is also attached to that event and probably gets the handler afterward.  When it sees a document.Text replacement, it scrolls back to offset zero, which negates any scrolling you did.

You could work around that by adding a BeginInvoke and simplifying the code like this:

private void TestRunFeedbackBox_OnDocumentTextChanged(object? sender, EditorSnapshotChangedEventArgs e) {
    Application.Current.Dispatcher.BeginInvoke(() => {
        var lineCount = TestRunFeedbackBox.Document.CurrentSnapshot.Lines.Count;

        TestRunFeedbackBox.ActiveView.Selection.CaretPosition = new TextPosition(lineCount - 1, 5);
    }, System.Windows.Threading.DispatcherPriority.Send);
}

If you are simply doing append logging though, I would not recommend you do this how you are.  It would be more efficient to raise an event off your viewmodel with the new log text, handle that event in your view and call this in the event handler:

TestRunFeedbackBox.Document.AppendText(TextChangeTypes.Typing, appendedText);

Using append would minimize scrolling and all the text examination that has to be done for the entire document as it is now when the text is fully replaced.


Actipro Software Support

The latest build of this product (v24.1.2) was released 4 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.