Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maintain TextBox scroll position while adding line

In my WinForm application I have a multiline TextBox control (uiResults) which is used for reporting progress while processing a large number of items. Using AppendText works great for automatically scrolling to the bottom at every update, but if the user scrolls back to read some older data I need to turn off the autoscroll. I would rather stay away from P/Invoke calls if possible.

Is it possible to detect if the user has scrolled back without using P/Invoke? For now, I just check SelectionStart which works but requires the user to move the caret from the end of the textbox to stop the autoscroll:

if(uiResults.SelectionStart == uiResults.Text.Length)
{
  uiResults.AppendText(result + Environment.NewLine);
}

My main problem is that when appending a string using the Text property, the textbox is scrolled to the beginning. I tried to solve this by storing the caret position and resetting and scrolling to it after the update, but this causes the current line to move to the bottom (of course, since ScrollToCaret scrolls no more than the necessary distance to bring the caret into view).

[Continued from above]
else
{
  int pos = uiResults.SelectionStart;
  int len = uiResults.SelectionLength;
  uiResults.Text += result + Environment.NewLine;
  uiResults.SelectionStart = pos;
  uiResults.SelectionLength = len;
  uiResults.ScrollToCaret();
}
like image 248
Anlo Avatar asked Apr 16 '12 13:04

Anlo


2 Answers

Auto-scrolling text box uses more memory than expected

The code in the question implements exactly what you are looking for. Text is added, but scrolling only occurs if the scroll bar is at the very bottom.

like image 175
John Arlen Avatar answered Oct 18 '22 09:10

John Arlen


I have had the same problem. And finally, I made an easy way. (Sorry, I'm not good at English.)

key point is get the first displayed char index using GetCharIndexFromPosition method.

//Get current infomation
int selStart = textBox.SelectionStart;
int selLen = textBox.SelectionLength;
int firstDispIndex = textBox.GetCharIndexFromPosition(new Point(3, 3));

//Append Text
textBox.AppendText(...);

//Scroll to original displayed position
textBox.Select(firstDispIndex, 0);
text.ScrolltoCaret();

//Restore original Selection
textBox.Select(selStart, selLen);

And, if textbox is flicking, use this extention. Call textBox.Suspend() before adding text, and call textBox.Resume() after adding text.

namespace System.Windows.Forms
{
    public static class ControlExtensions
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool LockWindowUpdate(IntPtr hWndLock);

        public static void Suspend(this Control control)
        {
            LockWindowUpdate(control.Handle);
        }

        public static void Resume(this Control control)
        {
            LockWindowUpdate(IntPtr.Zero);
        }

    }
}

Hope this will help you. Thank you~

like image 24
JHK Avatar answered Oct 18 '22 08:10

JHK