wpf 附加文本时如何防止TextBox自动滚动?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18793548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent TextBox auto scrolls when append text?
提问by Huy Nguyen
I have a multi-line TextBox with a vertical scrollbar that logs data from a realtime process. Currently, whenever a new line is added by textBox.AppendText(), the TextBox scrolls to the bottom so you can see the last entry, this great. But I have a checkbox to decides when the TextBox is allowed to auto scrolls. Is there anyway to do this?
我有一个带有垂直滚动条的多行 TextBox,用于记录来自实时进程的数据。目前,每当添加新行时textBox.AppendText(),TextBox 都会滚动到底部,以便您可以看到最后一个条目,这很棒。但是我有一个复选框来决定何时允许 TextBox 自动滚动。有没有办法做到这一点?
Note:
笔记:
- I want to uses the TextBox because the added text has multi-lines and formated by whitespaces so it's not simple to uses with a ListBox or a ListView.
- I tried to add a new line by
textBox.Text += text, but the TextBox always scrolls to the top.
- 我想使用 TextBox,因为添加的文本具有多行并由空格格式化,因此与 ListBox 或 ListView 一起使用并不简单。
- 我试图添加一个新行
textBox.Text += text,但 TextBox 总是滚动到顶部。
If we have a solution to do that, then one more question is how to prevent that the TextBox auto scrolls when the user uses the scrollbar to view somewhere else in the TextBox while the TextBox appends text?
如果我们有一个解决方案来做到这一点,那么还有一个问题是如何防止当用户使用滚动条查看 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.
}
}
Update 1: As saggiosuggests, 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:
更新 1:正如saggio 所建议的,我也认为这个问题的解决方案是在附加文本之前确定当前文本中显示在 TextBox 中的第一个字符的位置,然后再恢复它。但是如何做到这一点呢?我试图像这样记录当前的光标位置,但没有帮助:
int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;
Update 2(the issue was resolved): I found a solutionthat can solve my issue here on Stack Overflow. I have optimized their code to suit my issue as follows:
更新 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);
}
Another way:
其它的办法:
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);
}
}
I post these solutions for those who have a similar problem. Thanks for cgyDeveloper's source code.
我为那些有类似问题的人发布了这些解决方案。感谢cgyDeveloper的源代码。
Does anyone have a simpler way?
有人有更简单的方法吗?
回答by Sorceri
This seems pretty straight forward but I may be missing something. Use append text to scroll to the position if Autochecked is true and just add the text if you do not wish to scroll.
这看起来很简单,但我可能会遗漏一些东西。如果 Autochecked 为真,则使用附加文本滚动到该位置,如果您不想滚动,则只需添加文本。
Update...I was missing something. You want to set the selection point and then scroll to the caret. See below.
更新...我错过了一些东西。您想设置选择点,然后滚动到插入符号。见下文。
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));
}
回答by DevEstacion
You have to do it something like this,
你必须做这样的事情,
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();
Tested and working on VS2010 c# Winforms, i dont know about WPF but google probably has the answer for you.
在 VS2010 c# Winforms 上测试和工作,我不知道 WPF,但谷歌可能为你提供了答案。

