Как предотвратить автоматические прокрутки TextBox при добавлении текста?

У меня многострочный TextBox с вертикальной полосой прокрутки, которая регистрирует данные из процесса реального времени. В настоящее время, всякий раз, когда новая строка добавляется textBox.AppendText(), TextBox прокручивается вниз, чтобы вы могли видеть последнюю запись, это замечательно. Но у меня есть флажок, чтобы решить, когда TextBox разрешено автоматически прокручивать. Есть ли способ сделать это?

Примечание:

  • Я хочу использовать TextBox, потому что добавленный текст имеет многострочные строки и формируется с помощью пробелов, поэтому он не просто используется с ListBox или ListView.
  • Я попытался добавить новую строку textBox.Text += text, но TextBox всегда прокручивается вверх.

Если у нас есть решение сделать это, то еще один вопрос заключается в том, как предотвратить автоматическое прокручивание TextBox, когда пользователь использует полосу прокрутки для просмотра в другом месте в TextBox, в то время как TextBox добавляет текст?

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

Обновление 1. Как предлагает saggio, я также считаю, что решение этой проблемы состоит в том, чтобы определить положение первого символа в текущем тексте, который отображается в TextBox перед добавлением текста и последующим восстановлением. Но как это сделать? Я попытался записать текущую позицию курсора, как это, но это не помогло:

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

Обновление 2 (проблема была решена): я нашел решение , которое может решить мою проблему здесь, в Stack Overflow. Я оптимизировал свой код в соответствии со своей проблемой следующим образом:

// 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);
}

Другой способ:

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);
    }
}

Я отправляю эти решения тем, у кого есть аналогичная проблема. Спасибо за cgyDeveloper исходный код.

Есть ли у кого более простой способ?

Ответы

Ответ 1

Это кажется довольно прямым, но я, возможно, что-то пропускаю. Используйте текст append для перехода к позиции, если Autochecked имеет значение true и просто добавляет текст, если вы не хотите прокручивать.

Обновление... Я что-то упустил. Вы хотите установить точку выбора, а затем прокрутите до каретки. См. Ниже.

    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
    {
        int caretPos = txtLog.Text.Length;
        txtLog.Text += Environment.NewLine + text;
        txtLog.Select(caretPos, 0);            
        txtLog.ScrollToLine(txtLog.GetLineIndexFromCharacterIndex(caretPos));
    }

Ответ 2

Вы должны сделать это примерно так,

textBox1.AppendText("Your text here");
// this selects the index zero as the location of your caret
textBox1.Select(0, 0);
// Scrolls to the caret :)
textBox1.ScrollToCaret();

Протестировано и работает на VS2010 С# Winforms, я не знаю о WPF, но Google, вероятно, имеет для вас ответ.