大家好!我是付工。

我个人开发上位机的习惯是,如果条件允许,主界面一定要有日志的一席之地。

上位机使用者大都是非技术人员,他们通常缺乏专业的系统诊断能力,清晰的日志能够以直观的语言(如“设备X通信失败,请检查网线连接”)替代晦涩的错误代码,帮助他们快速理解问题,也减少了一些不必要的沟通成本。

今天给大家分享如何实现上位机通用日志系统。

一、日志界面

我一般情况下会使用ListView来做日志。

1、日志界面设计。

  • 拖放一个ListView控件到界面上。
  • 将背景颜色设置为主窗体一致。

  • 点击右上方三角,选择编辑列,添加两列,分别是日期时间和日志内容。

这样我们的日志界面就设计好了。

图片

2、设置日志的图标显示。

  • 准备3个图标,对应Info、Warning、Error。

图片

  • 拖放一个ImageList组件到界面上。

  • 将3个图标文件添加到ImageList里,这里按照日志等级顺序添加。

图片

  • 点击ListView右上角三角,将视图设置为Details,小ImageList选择上面准备好的ImageList组件。

图片

  • 将HeaderStyle设置为None,然后创建几个按钮,为后面测试做准备。

图片

二、日志开发

1、创建一个日志等级枚举LogLevel,分别为信息、警告、错误。

/// 日志等级枚举
 /// </summary>
 public enum LogLevel
 {
     信息,
     警告,
     错误
 }

2、创建一个日志管理类LogManager,代码如下:

public class LogManager
{
    // 单例实例
    private static readonly LogManager instance = new LogManager();
    // 日志委托
    public Action<LogLevel, string> AddLog;
    // 私有构造函数(防止外部创建实例)
    private LogManager()
    {
    }
    /// <summary>
    /// 全局访问点
    /// </summary>
    public static LogManager Instance => instance;
    /// <summary>
    /// 记录日志
    /// </summary>
    /// <param name="level">日志等级</param>
    /// <param name="message">日志内容</param>
    public void Log(LogLevel level, string message)
    {
        // 触发事件,通知UI更新(跨线程安全)
        AddLog?.Invoke(level, message);
    }
}

3、在日志所在界面编写一个AddLog方法,代码如下:

private void AddLog(LogLevel level, string message)
{
    // 跨线程判断
    if (this.lst_View.InvokeRequired)
    {
        this.lst_View.Invoke(new Action<LogLevel, string>(AddLog), level, message);
        return;
    }
    else
    {
        // 创建列表项
        ListViewItem item = new ListViewItem("  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), (int)level);
        item.SubItems.Add(message);
        // 根据日志等级设置颜色
        switch (level)
        {
            case LogLevel.错误:
                item.ForeColor = System.Drawing.Color.Red;
                break;
            case LogLevel.警告:
                item.ForeColor = System.Drawing.Color.Orange;
                break;
            default:
                item.ForeColor = System.Drawing.Color.Black;
                break;
        }
        //如果希望最新的日志自动滚动到最后
        this.lst_View.Items.Add(item);
        this.lst_View.EnsureVisible(this.lst_View.Items.Count - 1);
        //如果希望最新的日志在最前面
        //this.lst_View.Items.Insert(0, item);
    }
}
  • 加跨线程访问判断,否则多线程调用会报错。

  • 代码中提供了两种日志显示的方法,一种是最新日志在最后并自动滚动,一种是最新日志在最前面,根据个人习惯选择。

  • 日期时间那里加了空格,避免与图标过于靠近。

三、日志使用

1、我们希望在哪个窗体中使用日志,那么在初始化时,添加以下代码,进行委托绑定。

LogManager.Instance.AddLog = this.AddLog;

2、然后就可以调用了,下面不同按钮调用不同的日志类型信息:

private void btn_Info_Click(object sender, EventArgs e)
{
    LogManager.Instance.Log(LogLevel.信息, "这是一条正常信息");
}
private void btn_Warn_Click(object sender, EventArgs e)
{
    LogManager.Instance.Log(LogLevel.警告, "这是一条警告信息");
}
private void btn_Error_Click(object sender, EventArgs e)
{
    LogManager.Instance.Log(LogLevel.错误, "这是一条错误信息");
}

3、运行软件,分别点击3个按钮,运行效果如下:

图片

4、当日志内容比较多的话,会出现这种情况:

图片

对这个问题的解决方案分为两步:

1、窗体初始化动态设置第2列的宽度。

this.lst_View.Columns[1].Width = this.lst_View.Width - this.lst_View.Columns[0].Width-20;

2、设置ListView的ShowItemToolTips为True。

图片

感谢阅读和关注,后续跟大家分享如何进行日志存储和多行显示。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐