title: How to prevent TextBox auto scrolls when append text?
body: <p>I have a multi-line TextBox with a vertical scrollbar that logs data from real-time processing. Currently, whenever a new line is added by <code>textBox.AppendText()</code>, the TextBox scrolls to the bottom so you can see the last entry, this great. But I have a checkbox to indicate whether TextBox is allowed to auto-scroll. Is there any way to do this?</p>
  <p>Note:</p>
  <ul>
  <li>I want to use the TextBox because the added text has multi-lines and alignment by whitespace, so it's not simple to use with a ListBox or a ListView.</li>
  <li>I tried to add a new line by <code>textBox.Text += text</code>, but the TextBox constantly scrolls to the top.</li>
  </ul>
  <p>If we have a solution to do that, then one more question is how to prevent the TextBox auto scrolls when the user uses the scrollbar to view somewhere else in the TextBox while the TextBox appends text?</p>
  <pre><code>private void OnTextLog(string text)
  {
      if (chkAutoScroll.Checked)
      {
          // This always auto scrolls to the bottom.
          txtLog.AppendText(Environment.NewLine);
          txtLog.AppendText(text);
  
          // This always auto scrolls to the top.
          //txtLog.Text += Environment.NewLine + text;
      }
      else
      {
          // I want to append the text without scrolls right here.
      }
  }
  </code></pre>
  <p><strong>Update 1</strong>: As <a href="https://stackoverflow.com/users/564636/saggio">saggio</a> suggests, I also think the solution to this problem is to determine the position of the first character in the current text that is displayed in the TextBox before appending text and restoring it after that. But how to do this? I tried to record the current cursor position like this, but it did not help:</p>
  <pre><code>int selpoint = txtLog.SelectionStart;
  txtLog.AppendText(Environment.NewLine);
  txtLog.AppendText(text);
  txtLog.SelectionStart = selpoint;
  </code></pre>
  <p><strong>Update 2</strong> <em>(the issue was resolved)</em>: I found a <a href="https://stackoverflow.com/questions/1743448/auto-scrolling-text-box-uses-more-memory-than-expected">solution</a> that can solve my issue here on Stack Overflow. I have optimized their code to suit my case as follows:</p>
  <pre><code>// Constants for extern calls to various scrollbar functions
  private const int SB_VERT = 0x1;
  private const int WM_VSCROLL = 0x115;
  private const int SB_THUMBPOSITION = 0x4;
  
  [DllImport(&quot;user32.dll&quot;, CharSet = CharSet.Auto)]
  private static extern int GetScrollPos(IntPtr hWnd, int nBar);
  [DllImport(&quot;user32.dll&quot;)]
  private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
  [DllImport(&quot;user32.dll&quot;)]
  private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
  [DllImport(&quot;user32.dll&quot;)]
  private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
  
  private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
  {
      int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
      textbox.AppendText(text + Environment.NewLine);
      if (autoscroll)
      {
          int VSmin, VSmax;
          GetScrollRange(textbox.Handle, SB_VERT, out VSmin, out VSmax);
          int sbOffset = (int)((textbox.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / (textbox.Font.Height));
          savedVpos = VSmax - sbOffset;
      }
      SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
      PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
  }
  
  private void OnTextLog(string text)
  {
      AppendTextToTextBox(txtLog.Text, Environment.NewLine + text, chkAutoScroll.Checked);
  }
  </code></pre>
  <p>Another way:</p>
  <pre><code>private const int SB_VERT = 0x1;
  private const int WM_VSCROLL = 0x115;
  private const int SB_THUMBPOSITION = 0x4;
  private const int SB_BOTTOM = 0x7;
  
  [DllImport(&quot;user32.dll&quot;, CharSet = CharSet.Auto)]
  private static extern int GetScrollPos(IntPtr hWnd, int nBar);
  [DllImport(&quot;user32.dll&quot;)]
  private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
  [DllImport(&quot;user32.dll&quot;)]
  private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
  
  private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
  {
      int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
      textbox.AppendText(text + Environment.NewLine);
      if (autoscroll)
      {
          PostMessageA(textbox.Handle, WM_VSCROLL, SB_BOTTOM, 0);
      }
      else
      {
          SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
          PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
      }
  }
  </code></pre>
  <p><em>I post these solutions for those who have a similar issue. Thanks for <a href="https://stackoverflow.com/users/131327/cgydeveloper">cgyDeveloper</a>'s source code.</em></p>
  <p>Does anyone have a more straightforward way?</p>