20

リアルタイム プロセスからのデータを記録する垂直スクロールバーを備えた複数行の TextBox があります。現在、新しい行が によって追加されるたびtextBox.AppendText()に、TextBox が一番下までスクロールするので、最後のエントリが表示されます。これは素晴らしいことです。しかし、TextBox の自動スクロールをいつ許可するかを決定するチェックボックスがあります。とにかくこれを行うことはありますか?

ノート:

  • 追加されたテキストには複数行があり、空白でフォーマットされているため、TextBox を使用したいので、ListBox または ListView で使用するのは簡単ではありません。
  • で改行しようとしたtextBox.Text += textのですが、TextBox が常に一番上までスクロールしてしまいます。

それを行う解決策がある場合、もう 1 つの質問は、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のソースコードをありがとう。

誰にも簡単な方法がありますか?

4

2 に答える 2