日志查看器:显示日志记录信息的快速WPF控件

对于长时间运行的后台任务,用户了解当前执行的步骤很有帮助。LogViewer可以安全地多线程收集此信息,并将其显示为可滚动文本。LogViewer允许后台线程在不使用任何WPF代码的情况下写入格式化文本。当每秒需要写入数百条消息时,WPF更新每条消息的GUI的速度太慢。LogViewer收集消息,并每秒将它们发送到WPF线程10次。

日志查看器用例

在后台执行任务是很常见的,该任务可能会运行几分钟并执行数千个步骤。如果GUI在此期间未显示任何更改,则用户可能会认为应用已冻结。另一方面,他可能不需要看到所有数千步,而只需要看到几个主要的和当前的台阶。

最简单的方法是显示主要步骤,但是用户不知道许多细节步骤中的哪一个花费了太多时间。但是,如果他看到当前步骤显示几秒钟,他就知道出了问题。

由于后台任务可能不知道哪个步骤需要很长时间并显示出来,因此最简单的方法是将所有消息写入LogViewer,然后执行以下函数:

  • 接受多线程安全的日志记录消息,而不是在WPF线程上
  • 区分临时消息和永久消息。临时消息将被任何后续消息覆盖,而后台任务完成后仍向用户显示永久消息。
  • 收集0.1秒内收到的所有消息,并将它们一起传输到WPF线程。WPF线程每秒无法更新GUI数百次,并且用户无论如何都无法读取信息。
  • 将消息转换为WPF Flow<wbr />Document元素。控制字体、大小、边距等可能相当复杂,后台任务的编写者不需要知道WPF。为了指定所需的格式,他只需随消息一起传递一个专门为 StyledString .cs 中的应用定义的StringStyleEnum值。

日志查看器设计

本章介绍一些设计注意事项。如果您只是对使用日志查看器感兴趣,请跳转到在应用中使用LogViewer 一章。

多线程的实现

我正在考虑使用一些花哨的多线程安全集合来存储消息,但最终,我决定只使用简单的列表,并通过后台任务锁定每个Write()WPF线程的每个Read()。很快解释一下,Read()非常快,每秒只执行10次,这意味着几乎不会有任何同时锁定。

LogViewer.Write()获取锁,然后使用活动消息缓冲区执行三个活动:

  1. 如果缓冲区中的最后一条消息是临时消息,请将其删除。
  2. 将消息与StringStyleEnum值一起添加到活动缓冲区
  3. 如果未启动计时器:启动计时器延迟0.1秒,如果调用方实际上已在WPF线程上运行,请立即将消息写入GUI。

wpfTimerTick事件中,首先锁定缓冲区,然后切换缓冲区。如果在tick发生之前Write()使用Buffer1,它将在之后使用Buffer2。锁现在被释放,WPF线程具有处理非活动缓冲区所需的所有时间。完成后,计时器将在另外0.1秒内自行重新启动。一旦它获得空缓冲区,它就会停止重新启动。

支持格式化文本

后台任务通常属于业务层代码,该代码不应与WPF有任何依赖关系。出于这个原因,LogViewer定义自己的StringStyleEnum

public enum StringStyleEnum {
  none = 0,
  normal,
  label,
  header1,
  errorHeader,
  errorText,
  stats
}

根据应用程序的需要,这些值可以不同。文件StyledString.cs还包含static方法StyledString.ToInline(),该方法将消息及其StringStyleEnum值转换为WPF流文档Run

case StringStyleEnum.header1:
  styledParagraph.Margin = new Thickness(0, 24, 0, 4);
  inline = new Bold(new Run(message)) {
    FontSize = styledParagraph.FontSize * 1.2
  };
  break;

向纯字符串添加格式

后台任务使用以下LogViewer方法进行写入:

public void WriteLine()
public void WriteLine(string line)
public void WriteTempLine(string line)
public void WriteLine(string line, StringStyleEnum stringStyle)
public void WriteTempLine(string line, StringStyleEnum stringStyle)
public void Write(string text)
public void Write(string text, StringStyleEnum stringStyle)
public void Write(StyledString styledString)
public void Write(params StyledString[] styledStrings)

Write()包含消息和有关格式的一些信息,以及是否需要行尾。LogViewer将其转换为StyledString,然后将其转发到WPF

public class StyledString {
  public string String { get; private set; }
  public StringStyleEnum StringStyle { get; private set; }
  public LineHandlingEnum LineHandling { get; private set; }
  public DateTime Created { get; private set; }
}

每个应用程序的StringStyleEnum值都不同。LineHandling可以具有三个值noneendOfLinetemporaryEOL。基本上,可以使用Write()只写入一行的一部分,如果日志行的一部分应该加粗而另一部分应该加粗,这很有用,在这种情况下,后台任务将调用两次Write()

标记为temporaryEOL的消息只显示给用户,直到调用下一个Write()

在您的应用程序中使用日志查看器

您可以从Github获取LogViewer最新版本:

GitHub - PeterHuberSg/LogViewer: WPF control showing formated text scrolling line by line

我把它放在它自己的DLL LogViewerLib中。与其将该库链接到您的应用程序中,我建议只复制两个文件,LogViewer.csStyledString.cs。您需要根据应用的需求更改StyledString.cs的内容。因此,如果您不继续在GitHub上同步LogViewer代码,可能会更好。

LogViewerTestApp是我用来测试LogViewerWPF应用程序。

测试日志查看器

查看LogViewerTestApp代码以了解如何添加LogViewer到应用中可能会很有趣。运行LogViewerTest会让你感受到AppLogViewer的行为方式。

Test1写入一些不同格式的文本。请注意,最后一行tempLine3是临时行。一旦再次按下任何TestX按钮,该行就会被覆盖。

Test2尽快写入临时行。令人惊讶的是,LogViewer每秒可以处理一百万条临时消息!毕竟,它只是在RAM中收集它们,但每0.1秒只向WPF发送1个。

Test3不断写新行。这有助于测试用户是否可以停止并继续自动滚动。如果用户想要检查永久行,他可以滚动到该行,这会停止自动滚动。一旦他想再次激活自动滚动,他只需滚动到最后。

延伸阅读

如果你读到这里,你可能真的对WPF感兴趣,在这种情况下,我会热烈推荐我的其他一些WPF文章。我觉得现在的文章读起来不是那么有趣,但我的一些文章真的很有帮助,让你深入了解WPF,这是你在其他任何地方都找不到的:

我在Github上的项目

我还在Github上写了几个开源项目:

存储库

真正惊人的单用户WPF应用程序的数据库替换。程序员只需定义他要使用的C#类,代码生成器将用于在本地磁盘上永久创建、更新和删除数据的所有代码写入所有代码。这大大加快了开发人员的工作速度,因为他不再需要使用任何SQL,实体框架等。查询在Linq中完成,运行速度超快。代码的执行速度比任何数据库都快得多。支持事务、备份等。自5+年以来在多个项目中无错误运行。

TracerLib

实时高效地收集多线程数据,这些数据可以存储在文件中,也可以存储在RAM中并在一段时间后丢弃。这对于异常处理很有用,因为这允许告诉用户在异常发生之前()发生了什么。

WpfWindowsLib

用于数据输入的WPF控件,检测是否缺少所需数据或数据是否已更改。

https://www.codeproject.com/Articles/5340961/LogViewer-A-Fast-WPF-Control-Displaying-Logging-In

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值