我正在寻找一种有效的方法来实现Windows窗体应用程序的日志窗口。过去,我已经使用TextBox和RichTextBox实现了几个日志窗口,但我仍然对这些功能不完全满意。
此日志旨在为用户提供各种事件的最新历史记录,主要用于数据收集应用程序,在这些应用程序中,用户可能会对特定事务是如何完成的感到好奇。在这种情况下,日志不必是永久性的,也不必保存到文件中。
一、提出一些要求:
- 高效快捷;如果快速连续地向日志中写入数百行,则需要消耗最少的资源和时间。
- 能够提供多达2000行左右的可变滚动。任何更长的都是不必要的。
- 首选突出显示和颜色。不需要字体效果。
- 达到卷动限制时自动修剪行。
- 添加新数据时自动滚动。
- 奖金但非必需:在手动交互期间暂停自动滚动,例如用户正在浏览历史记录。
到目前为止,我一直在使用什么来编写和整理日志:
我使用以下代码(从其他线程调用):
// rtbLog is a RichTextBox
// _MaxLines is an int
public void AppendLog(string s, Color c, bool bNewLine)
{
if (rtbLog.InvokeRequired)
{
object[] args = { s, c, bNewLine };
rtbLog.Invoke(new AppendLogDel(AppendLog), args);
return;
}
try
{
rtbLog.SelectionColor = c;
rtbLog.AppendText(s);
if (bNewLine) rtbLog.AppendText(Environment.NewLine);
TrimLog();
rtbLog.SelectionStart = rtbLog.TextLength;
rtbLog.ScrollToCaret();
rtbLog.Update();
}
catch (Exception exc)
{
// exception handling
}
}
private void TrimLog()
{
try
{
// Extra lines as buffer to save time
if (rtbLog.Lines.Length < _MaxLines + 10)
{
return;
}
else
{
string[] sTemp = rtxtLog.Lines;
string[] sNew= new string[_MaxLines];
int iLineOffset = sTemp.Length - _MaxLines;
for (int n = 0; n < _MaxLines; n++)
{
sNew[n] = sTemp[iLineOffset];
iLineOffset++;
}
rtbLog.Lines = sNew;
}
}
catch (Exception exc)
{
// exception handling
}
}
这种方法的问题是,每当调用TrimLog时,我就会丢失颜色格式。对于普通的TextBox,这种方法效果很好(当然需要做一点修改)。
对这个问题的解决方案的搜索从来没有真正令人满意。一些人建议在RichTextBox中通过字符数而不是行数来修剪多余的内容。我也见过使用ListBox,但还没有成功地尝试过。
7条答案
按热度按时间txu3uszq1#
我建议你不要用一个控件作为你的日志,而是写一个log collection 类,它有你想要的属性(不包括display属性)。
然后编写将该集合转储到各种用户界面元素所需的少量代码。就个人而言,我会将
SendToEditControl
和SendToListBox
方法放入我的日志记录对象中。我可能会为这些方法添加过滤功能。您可以只在有意义的时候更新UI日志,这样可以获得最佳性能,更重要的是,可以在日志快速更改时减少UI开销。
重要的是不要把你的日志记录绑定到一个UI上,那是错误的。
从长远来看,一个好的记录器UI可能是一个自定义控件。但从短期来看,你只想把你的日志记录从任何一个特定的UI中分离出来。
j2datikz2#
这是我根据前一段时间写的一个更复杂的记录器拼凑起来的东西。
这将支持基于日志级别的列表框颜色,支持Ctrl+V和右键单击以复制为RTF,并处理从其他线程到列表框的日志记录。
您可以使用其中一个建构函式多载覆写ListBox中保留的行数(预设为2000)以及消息格式。
z2acfund3#
我将把它存储在这里,以便当我想再次使用RichTextBox记录彩色行时为Future Me提供帮助。下面的代码删除RichTextBox中的第一行:
我花了很长时间才弄明白,将Selectedrtf设置为“”不起作用,但将其设置为“适当的”RTF(没有文本内容)是可以的。
64jmpszr4#
我创建一个基本日志窗口的解决方案与John Knoeller在他的回答中所建议的完全相同。避免将日志信息直接存储在TextBox或RichTextBox控件中,而是创建一个日志类,用于 * 填充 * 控件或写入文件等。
此示例解决方案包含几个部分:
1.日志记录类本身,
Logger
。1.修改RichTextBox控件以在更新后添加滚动到底部功能;
ScrollingRichTextBox
.1.演示其使用的主窗体,
LoggerExample
。一、伐木类:
Logger类合并了另一个类
LogEntry
,它跟踪行号、时间戳和所需的颜色。一个结构体用于构建富文本颜色表。下面是修改后的RichTextBox:
我在这里所做的就是继承一个RichTextBox并添加一个“滚动到底部”方法。关于如何在StackOverflow上实现这一点还有其他各种问题,我就是从这些问题中派生出了这个方法。
最后,在表单中使用这个类别的范例:
这个窗体是用两个计时器创建的,一个用来伪随机地生成日志条目,另一个用来填充RichTextBox本身。该RichTextBox控件颜色被设置为具有黑色背景和白色以及各种颜色的前景。生成文本的计时器间隔为100 ms,而更新日志窗口的计时器间隔为1000 ms。
示例输出:
它还远未完善或完成,但这里有一些警告和可以添加或改进的东西(其中一些我在以后的项目中做过):
1.如果
maximumEntries
的值很大,性能会很差。此日志记录类仅设计用于几百行的回滚。1.替换RichTextBox的文本可能会导致 Flink 。我总是将刷新计时器保持在一个相对较慢的时间间隔。(在本例中为一秒。)
1.除了上面的#2,我的一些项目在重绘RichTextBox内容之前检查日志是否有任何新条目,以避免不必要的刷新。
1.每个日志条目上的时间戳可以设置为可选,并允许使用不同的格式。
1.在这个例子中没有办法暂停日志,但是我的许多项目都提供了暂停滚动行为的机制,允许用户手动滚动、选择和复制日志窗口中的文本。
请随意修改和改进这个例子。欢迎反馈。
7jmck4yq5#
我会说ListView非常适合这个(在细节查看模式下),这正是我在一些内部应用程序中使用它的目的。
实用提示:如果你知道你将一次添加/删除很多项,使用BeginUpdate()和EndUpdate()。
daolsyd06#
我最近实现了一些类似的东西。我们的方法是保留一个滚动记录的环形缓冲区,并且只手动绘制日志文本(使用Graphics.DrawString)。然后,如果用户想要向后滚动、复制文本等,我们有一个“Pause”按钮,它会切换回普通的TextBox控件。
l5tcr1uw7#
如果您想要突出显示和颜色格式,我建议使用RichTextBox。
如果你想要自动滚动,那么使用ListBox。
在这两种情况下,都将其绑定到行的循环缓冲区。