ScrollToEnd does not scroll to end when using WordWrap #226

Open
opened 2020-03-19 03:27:00 -05:00 by dbCF · 11 comments
dbCF commented 2020-03-19 03:27:00 -05:00 (Migrated from github.com)

My AvalonEdit has to show the content of a log file. Changes to the log file will be attached automatically with AppendText(). Afterwards the editor should scroll to the end of the document. But it does not. A few lines (the ones after the selected one) are (almost) always missing. See screenshot:
Untitled

XAML:

<avalonEdit:TextEditor xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
  Grid.Row="0"
  Name="LogBox"
  FontFamily="Consolas"
  FontSize="9pt"
  HorizontalScrollBarVisibility="Hidden"
  VerticalScrollBarVisibility="Visible"
  />

Some code:

LogBox.Document.UndoStack.SizeLimit = 0;
LogBox.Options.EnableRectangularSelection = true;
LogBox.Options.EnableVirtualSpace = true;
LogBox.Options.HighlightCurrentLine = true;
LogBox.TextArea.TextView.LineTransformers.Clear();
LogBox.TextArea.TextView.LineTransformers.Add(new CustomDocumentColorizingTransformer());
LogBox.IsReadOnly = true;
LogBox.WordWrap = true;
SearchPanel.Install(LogBox);

LogBox.ScrollToEnd();

I've also tried ScrollToLine() and ScrollToVerticalOffset() but the result stays the same.

The only thing which solves my issue (but is not an option for me) is to set WordWrap to false.

Any suggestions?

Best regards,
Carsten

My AvalonEdit has to show the content of a log file. Changes to the log file will be attached automatically with AppendText(). Afterwards the editor should scroll to the end of the document. But it does not. A few lines (the ones after the selected one) are (almost) always missing. See screenshot: ![Untitled](https://user-images.githubusercontent.com/42198697/77044725-37430d80-69c0-11ea-8971-f07ed69f38fb.png) XAML: ``` <avalonEdit:TextEditor xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit" Grid.Row="0" Name="LogBox" FontFamily="Consolas" FontSize="9pt" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Visible" /> ``` Some code: ``` LogBox.Document.UndoStack.SizeLimit = 0; LogBox.Options.EnableRectangularSelection = true; LogBox.Options.EnableVirtualSpace = true; LogBox.Options.HighlightCurrentLine = true; LogBox.TextArea.TextView.LineTransformers.Clear(); LogBox.TextArea.TextView.LineTransformers.Add(new CustomDocumentColorizingTransformer()); LogBox.IsReadOnly = true; LogBox.WordWrap = true; SearchPanel.Install(LogBox); LogBox.ScrollToEnd(); ``` I've also tried ScrollToLine() and ScrollToVerticalOffset() but the result stays the same. The only thing which solves my issue (but is not an option for me) is to set WordWrap to false. Any suggestions? Best regards, Carsten
blakepell commented 2020-03-22 15:52:04 -05:00 (Migrated from github.com)

I have this issue currently as well (ScrollToEnd works for me but is really slow, I'm using ScrollToVerticalOffset). To elaborate, does this happen when you have lines wrapped (e.g. is the line wrapping causing the issue, that's what triggers the issue for me and I need to leave line wrapping on myself)?

I have this issue currently as well (ScrollToEnd works for me but is really slow, I'm using ScrollToVerticalOffset). To elaborate, does this happen when you have lines wrapped (e.g. is the line wrapping causing the issue, that's what triggers the issue for me and I need to leave line wrapping on myself)?
dbCF commented 2020-03-23 01:42:05 -05:00 (Migrated from github.com)

As mentioned before:

The only thing which solves my issue (but is not an option for me) is to set WordWrap to false.

So, yes: Word wrapping causes my issue.

As mentioned before: > The only thing which solves my issue (but is not an option for me) is to set WordWrap to false. So, yes: Word wrapping causes my issue.
blakepell commented 2020-04-28 16:06:06 -05:00 (Migrated from github.com)

See if this words. I had it in an inherited class but you get the idea. It seems to perform as well as ScrollToVerticalOffset and also takes it all the way to the bottom (including wraps). I haven't tested it extensively but I'm going to.

        /// <summary>
        /// Similar functionality to ScrollToEnd but considerably more efficient.
        /// </summary>
        public void ScrollToLastLine()
        {
            this.ScrollTo(this.LineCount, 0);
        }
See if this words. I had it in an inherited class but you get the idea. It seems to perform as well as ScrollToVerticalOffset and also takes it all the way to the bottom (including wraps). I haven't tested it extensively but I'm going to. ``` /// <summary> /// Similar functionality to ScrollToEnd but considerably more efficient. /// </summary> public void ScrollToLastLine() { this.ScrollTo(this.LineCount, 0); } ```
dbCF commented 2020-04-29 03:38:55 -05:00 (Migrated from github.com)

Doesn't work for me. Or let's say: It does not work if there are wrapped lines in the view. If there are only "not wrapped" lines in the view, everything's fine.

I just saw the the search has the same issue: The found result may be out of the view if there are wrapped lines in the view. So you have to scroll a view lines to see the result.

Doesn't work for me. Or let's say: It does not work if there are wrapped lines in the view. If there are only "not wrapped" lines in the view, everything's fine. I just saw the the search has the same issue: The found result may be out of the view if there are wrapped lines in the view. So you have to scroll a view lines to see the result.
blakepell commented 2020-05-17 20:06:59 -05:00 (Migrated from github.com)

Apparantly I spoke too quickly. The scroll to still seems inefficient when I profile my app. I finally figured out how to determine if a line is wrapped though (and then can use the vertical offset approach when the last line isn't wrapped). I inherited from the editor.

public bool IsLineWrapped(int lineNumber)
{
    return (this.TextArea.TextView.VisualLines.FirstOrDefault(x => x.FirstDocumentLine.LineNumber == lineNumber)?.TextLines.Count ?? 0) > 1;
}

public int LinesCountWithWrap(int lineNumber)
{
    return this.TextArea.TextView.VisualLines.FirstOrDefault(x => x.FirstDocumentLine.LineNumber == lineNumber)?.TextLines.Count ?? 0;
}

However, an issue still exists with this where the VisualLines are sometimes not valid when requested. If EnsureVisualLines() is called the collection has 0 items. So. :/ I guess I'm still trying to refine this to get a reliable way to check for wrapping on the visible part of the screen.

Apparantly I spoke too quickly. The scroll to still seems inefficient when I profile my app. I finally figured out how to determine if a line is wrapped though (and then can use the vertical offset approach when the last line isn't wrapped). I inherited from the editor. ``` public bool IsLineWrapped(int lineNumber) { return (this.TextArea.TextView.VisualLines.FirstOrDefault(x => x.FirstDocumentLine.LineNumber == lineNumber)?.TextLines.Count ?? 0) > 1; } public int LinesCountWithWrap(int lineNumber) { return this.TextArea.TextView.VisualLines.FirstOrDefault(x => x.FirstDocumentLine.LineNumber == lineNumber)?.TextLines.Count ?? 0; } ``` However, an issue still exists with this where the VisualLines are sometimes not valid when requested. If `EnsureVisualLines()` is called the collection has 0 items. So. :/ I guess I'm still trying to refine this to get a reliable way to check for wrapping on the visible part of the screen.
goswinr commented 2020-05-18 14:55:22 -05:00 (Migrated from github.com)

I solved it by calling ScrollToEnd twice.

I solved it by calling ScrollToEnd twice.
blakepell commented 2020-05-18 17:28:35 -05:00 (Migrated from github.com)

Here's where I finally fell. I set a bool property on whether anything is wrapped during the VisualLineChanged event. Since my logger is constantly scrolling to the bottom it was pretty accurate for me (even if it ran before the lines updated). I was able to determine which way to scroll then. In the chance that the VisualLinesValid came back false I left the HasVisibleWrapped property in it's previous state. This was acceptable for my use case, I can see why it wouldn't be for others.

My event looked something like this:

private void TextView_VisualLinesChanged(object sender, EventArgs e)
{
    if (this.TextArea.TextView.VisualLinesValid && this.TextArea.TextView.VisualLines.Any())
    {
        foreach (var line in this.TextArea.TextView.VisualLines)
        {
            if (line.TextLines[0].DependentLength > 0)
            {
                this.HasVisibleWrappedLines = true;
                return;
            }
        }

        this.HasVisibleWrappedLines = false;
    }
}

Edit: I updated this to use VisualLineChanged instead of VisualLinesChanging and also to use the DependentLength property instead of TextLines.Count > 0.

Here's where I finally fell. I set a bool property on whether anything is wrapped during the `VisualLineChanged` event. Since my logger is constantly scrolling to the bottom it was pretty accurate for me (even if it ran before the lines updated). I was able to determine which way to scroll then. In the chance that the VisualLinesValid came back `false` I left the `HasVisibleWrapped` property in it's previous state. This was acceptable for my use case, I can see why it wouldn't be for others. My event looked something like this: ``` private void TextView_VisualLinesChanged(object sender, EventArgs e) { if (this.TextArea.TextView.VisualLinesValid && this.TextArea.TextView.VisualLines.Any()) { foreach (var line in this.TextArea.TextView.VisualLines) { if (line.TextLines[0].DependentLength > 0) { this.HasVisibleWrappedLines = true; return; } } this.HasVisibleWrappedLines = false; } } ``` _Edit:_ I updated this to use `VisualLineChanged` instead of `VisualLinesChanging` and also to use the `DependentLength` property instead of `TextLines.Count > 0`.
dbCF commented 2020-05-19 04:05:00 -05:00 (Migrated from github.com)

I solved it by calling ScrollToEnd twice.

I was desperate enough to give it a try but it did not work for me.

> I solved it by calling ScrollToEnd twice. I was desperate enough to give it a try but it did not work for me.
dbCF commented 2020-05-19 04:14:25 -05:00 (Migrated from github.com)

So now you have HasVisibleWrappedLines either true or false. What are you doing in either case to scroll to the very bottom? I have tried ScrollToVerticalOffset(), ScrollToEnd() in various combinations without any success, I do not really see how this new variable should help.

So now you have `HasVisibleWrappedLines` either `true` or `false`. What are you doing in either case to scroll to the very bottom? I have tried `ScrollToVerticalOffset()`, `ScrollToEnd()` in various combinations without any success, I do not really see how this new variable should help.
blakepell commented 2020-05-20 17:33:21 -05:00 (Migrated from github.com)

This is what I'm using in my mud client:

public void ScrollToLastLine()
{
    if (this.HasVisibleWrappedLines)
    {
        this.ScrollTo(this.LineCount, 0);
    }
    else
    {
        this.ScrollToVerticalOffset(this.TextArea.TextView.GetVisualTopByDocumentLine(this.LineCount));
    }
}

ScrollToVerticalOffset always works for me IF there are no wrapped lines (it uses significantly less CPU when profiled). Whatever works for you that's that's fool proof (but less efficient) can go into the top part of the if. I've been testing with this for 4-5 days now and I haven't had any issues. I'm going to make a release for my testers and see how that goes when it's in the wild.

This is what I'm using in my mud client: ``` public void ScrollToLastLine() { if (this.HasVisibleWrappedLines) { this.ScrollTo(this.LineCount, 0); } else { this.ScrollToVerticalOffset(this.TextArea.TextView.GetVisualTopByDocumentLine(this.LineCount)); } } ``` `ScrollToVerticalOffset` always works for me IF there are no wrapped lines (it uses significantly less CPU when profiled). Whatever works for you that's that's fool proof (but less efficient) can go into the top part of the if. I've been testing with this for 4-5 days now and I haven't had any issues. I'm going to make a release for my testers and see how that goes when it's in the wild.
Flithor commented 2025-11-27 05:23:15 -06:00 (Migrated from github.com)

Workaround here:

editor!.ScrollToEnd();
// for WPF, AvaloniaUI.AvaloniaEdit replace Dispatcher caller to: Dispatcher.UIThread
this.Dispatcher.InvokeAsync(() =>
    editor.ScrollTo(editor.LineCount, 0, VisualYPosition.TextBottom, editor.Bounds.Height, 0),
    DispatcherPriority.Background);
Workaround here: ``` editor!.ScrollToEnd(); // for WPF, AvaloniaUI.AvaloniaEdit replace Dispatcher caller to: Dispatcher.UIThread this.Dispatcher.InvokeAsync(() => editor.ScrollTo(editor.LineCount, 0, VisualYPosition.TextBottom, editor.Bounds.Height, 0), DispatcherPriority.Background); ```
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-icsharpcode/AvalonEdit#226
No description provided.