簡體   English   中英

追加文本時如何防止文本框自動滾動?

[英]How to prevent TextBox auto scrolls when append text?

我有一個帶有垂直滾動條的多行文本框,用於記錄來自實時處理的數據。 目前,每當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的源代碼。

有沒有人有更直接的方法?

這看起來很簡單,但我可能會遺漏一些東西。 如果 Autochecked 為真,則使用附加文本滾動到該位置,如果您不想滾動,只需添加文本。

更新...我錯過了一些東西。 您要設置選擇點,然后滾動到插入符號。 見下文。

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

你必須這樣做

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 c# Winforms 上工作,我不了解 WPF,但谷歌可能有你的答案。

所需的操作是:

  • 當滾動條被拖到底部時打開自動滾動。
  • 當 scollbar 被拖動到其他任何地方時關閉自動滾動。

創建以下類

    public class AutoScrollTextBox : TextBox
    {
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
        }

        protected override void OnTextChanged(TextChangedEventArgs e)
        {
            bool isScrolledToEnd = VerticalOffset + ViewportHeight == ExtentHeight;
            base.OnTextChanged(e);
            CaretIndex = Text.Length;
            if (isScrolledToEnd)
            {
                ScrollToEnd();
            }                
        }
    }

並在您的 XML 中將 TextBox 替換為 AutoScrollTextBox 並在內容到達顯示時附加到 TextToDisplay 綁定

<local:AutoScrollTextBox Text="{Binding TextToDisplay }" />

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM