Jak zapobiec automatycznemu przewijaniu TextBox podczas dołączania tekstu?

Mam wielowierszowy TextBox z pionowym paskiem przewijania, który rejestruje dane z procesu w czasie rzeczywistym. Obecnie za każdym razem, gdy dodawana jest nowa liniatextBox.AppendText(), TextBox przewija do dołu, dzięki czemu można zobaczyć ostatni wpis, to świetnie. Ale mam pole wyboru, aby zdecydować, kiedy TextBox ma zezwolenie na automatyczne przewijanie. Czy i tak to zrobić?

Uwaga:

Chcę używać TextBox, ponieważ dodany tekst ma wiele linii i jest sformatowany przez białe spacje, więc nie jest prosty w użyciu z ListBox lub ListView.Próbowałem dodać nową linię wedługtextBox.Text += text, ale TextBox zawsze przewija do góry.

Jeśli mamy rozwiązanie, to kolejne pytanie brzmi: jak zapobiec automatycznemu przewijaniu TextBox, gdy użytkownik korzysta z paska przewijania, aby zobaczyć gdzieś w polu tekstowym, gdy TextBox dołącza tekst?

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.
    }
}

Aktualizacja 1: Tak jaksaggio sugeruje, uważam też, że rozwiązaniem tego problemu jest określenie pozycji pierwszego znaku w bieżącym tekście wyświetlanego w TextBox przed dołączeniem tekstu i przywrócenie go po tym. Ale jak to zrobić? Próbowałem nagrać bieżącą pozycję kursora w ten sposób, ale to nie pomogło:

int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;

Aktualizacja 2 (problem został rozwiązany): Znalazłemrozwiązanie to może rozwiązać mój problem tutaj na Stack Overflow. Zoptymalizowałem ich kod, aby pasował do mojego problemu w następujący sposób:

// 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("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
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);
}

Inny sposób:

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("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
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);
    }
}

Zamieszczam te rozwiązania dla tych, którzy mają podobny problem. Dzięki zacgyDeveloperkod źródłowy.

Czy ktoś ma prostszy sposób?

questionAnswers(2)

yourAnswerToTheQuestion