简体   繁体   中英

How to autoscroll AvaloniaUI ScrollViewer when using DataBinding?

I have this setup in my AvaloniaUI application:

<ScrollViewer VerticalScrollBarVisibility="Auto"
              AllowAutoHide="True"
              Name="MessageLogScrollViewer">
  <TextBlock HorizontalAlignment="Stretch"
             VerticalAlignment="Stretch"
             TextWrapping="NoWrap"
             Text="{Binding ReceivedMessages}"></TextBlock>
</ScrollViewer>

The TextBlock basically displays log messages and I would like it to autoscroll to the bottom, when I append new lines to the String bound to the text property.

The ScrollViewer has a method called ScrollToEnd() , that I would need to call whenever I update the Text. So I tried to define a function in my code behind like so:

private ScrollViewer messageLogScrollViewer;    
public MainWindow()
{
    InitializeComponent();

    DataContext = new MainWindowViewModel(this);
    messageLogScrollViewer = this.FindControl<ScrollViewer>("MessageLogScrollViewer");
}

...

public void ScrollTextToEnd()
{
    messageLogScrollViewer.ScrollToEnd();
}

I then tried to call that function from my ViewModel:

private string receivedMessages = string.Empty;
public string ReceivedMessages
{
    get => receivedMessages;
    set => this.RaiseAndSetIfChanged(ref receivedMessages, value);
}

...

private MainWindow _window;
public MainWindowViewModel(MainWindow window)
{
    _window = window;
}

...

ReceivedMessage += "\n";
ReceivedMessages += ReceivedMessage;
_window.ScrollTextToEnd(); // Does not work.

But unfortunately, this the ScrollToEnd() function needs to be called from the UI-Thread, because I get an Exception:

System.InvalidOperationException: 'Call from invalid thread'

My question is how can I autoscroll my ScrollViewer to the end, whenever I update the TextBlocks Text property via DataBinding?

Ok, I figured it out, but leaving this here for reference.

In order to execute the function from another thread on the UI-Thread one needs to call

Dispatcher.UIThread.InvokeAsync(_window.ScrollTextToEnd);

But this scrolled only to the penultimate line, so it seems, the control is not updated yet, when I call ScrollTextToEnd() . Therefore I added a short timeout in between updating the text and calling the scroll function like this

ReceivedMessages += ReceivedMessage;
// A short timeout is needed, so that the scroll viewer will scroll to the new end of its content.
Thread.Sleep(10);
Dispatcher.UIThread.InvokeAsync(_window.ScrollTextToEnd);

It works now just like that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM