一、前言
在 Winform 开发中经常有这样的需求,在用户执行一些操作不正确时,需要将错误信息反馈给用户,比如:登录密码不正确,无法连接到服务器等,一般常见的用法有两个:
1.弹框
使用 MessageBox.Show("密码错误"); 这样的方式,弹框后,用户必须点击确定后才能执行下一步操作,给用户的体验并不是特别好。
2.在界面中显示错误信息,定时清除
如果是输入框,直接用 ErrorProvider 控件就行了。
如果只是做一些简单的提示信息,那么就要定时清除日志,总不能让错误信息,一直显示在界面上,这容易误导用户,那么就问题就来了,要怎么去定时清理日志呢?
方法1:
使用 Task 去定时清理日志,可以这么写没错,但也有个问题,如果 Task 定时3秒,还没执行完,直接关掉界面,可能会发生报错,不是很安全的写法。
方法2:
使用定时器,这个方法值得推荐,而且相对比较稳定,甚至比上面用异步的方法更简单。
二、定时器
在C#中,定时器有两种,第一种是线程定时器,如果在 Winform 的 UI 线程中使用,会有跨线程问题,第二种,Winform 自带的 Timer 控件,不存在跨线程问题,但也有个问题,在其他线程中使用,可能会出现代码无法执行,所以我们平时在线程的使用上要非常注意。
1.线程定时器
代码
//日志定时器
System.Timers.Timer timer = null;
private void Init()
{
timer = new System.Timers.Timer(3000);//实例化Timer类,设置间隔时间(毫秒);
timer.Elapsed += new System.Timers.ElapsedEventHandler(theout);//到达时间的时候执行事件;
timer.AutoReset = false;//设置是执行一次(false)还是一直执行(true);
}
/// <summary>
/// 显示日志
/// </summary>
/// <param name="val"></param>
private void ShowLog(string val)
{
Label_Log.Text = val;
if(timer != null && timer.Enabled)
{
timer.Enabled = false;
}
timer.Interval = CoolingTime;
timer.Enabled = true;
}
public void theout(object source, System.Timers.ElapsedEventArgs e)
{
//跨线程访问
this.Invoke(new MethodInvoker(delegate {
Label_Log.Text = string.Empty;
}));
}
2.Winform定时器
代码:
private System.Windows.Forms.Timer Timer = null;
private void Init()
{
Timer = new System.Windows.Forms.Timer();//实例化Timer类,设置间隔时间(毫秒);
Timer.Interval = 3000;
Timer.Tick += Timer_Tick;//到达时间的时候执行事件
}
//显示日志
private void ShowLog(string log)
{
Label_Log.Text = log;
if (Timer != null && Timer.Enabled)
Timer.Enabled = false;
Timer.Enabled = true;
}
private void Timer_Tick(object sender, EventArgs e)
{
Label_Log.Text = string.Empty;
}
个人觉得,还是第二种,更适合Winform开发,所以,我基于上面代码的基础上做了一下封装
三、封装显示日志工具
新建一个Winform项目,界面如下
新建一个类 ShowLogTool,代码如下
using System;
using System.Windows.Forms;
namespace Utils
{
public class ShowLogTool
{
//日志定时器
private static System.Windows.Forms.Timer Timers = null;
//日志组件
private static System.Windows.Forms.Label Label_Log;
private static void Init()
{
Timers = new System.Windows.Forms.Timer();
Timers.Tick += Timer_Tick;//到达时间的时候执行事件
}
/// <summary>
/// 显示日志
/// </summary>
/// <param name="control">界面Form的Control</param>
/// <param name="label">日志组件Label</param>
/// <param name="log">日志内容</param>
/// <param name="millisecond">清空日志的间隔时间</param>
public static void ShowLog(Control control, Label label, string log, int millisecond, System.Drawing.Color color)
{
if (control.InvokeRequired)
{
//Console.WriteLine("非UI线程");
control.Invoke(new MethodInvoker(delegate
{
ShowLog(label, log, millisecond, color);
}));
}
else
{
//Console.WriteLine("UI线程");
ShowLog(label, log, millisecond, color);
}
}
private static void ShowLog(Label label, string log, int millisecond, System.Drawing.Color color)
{
Label_Log = label;
Label_Log.ForeColor = color;
Label_Log.Text = log;
if (Timers != null && Timers.Enabled)
Timers.Enabled = false;
Timers.Interval = millisecond;
Timers.Enabled = true;
}
private static void Timer_Tick(object sender, EventArgs e)
{
Label_Log.Text = string.Empty;
}
static ShowLogTool()
{
Init();
}
private ShowLogTool() { }
}
}
Form1代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Utils;
namespace 显示日志
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Task.Run(() =>
//{
// ShowLogTool.ShowLog(this, Label_Log, "我是一个日志", 5000, Color.Red);
//});
ShowLogTool.ShowLog(this, Label_Log, "我是一个日志", 3000, Color.Red);
}
}
}
运行后,就可以看到效果了,这里我测试了UI线程,和非UI线程,结果都能满足需求
==================================
2023.12.16
我对上面封装代码进行了优化
新建一个类 FormControlExtensions,用来切换线程
using System;
using System.Windows.Forms;
public static class FormControlExtensions
{
/// <summary>
/// Invokes actions in UI thread.
/// </summary>
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(new MethodInvoker(action));
}
else
{
action();
}
}
}
新建一个类 LogShow
using System;
using System.Windows.Forms;
public class LogShow
{
//日志组件
public Label Label_Log = null;
//日志定时器
private Timer Timers = null;
//间隔时间(秒)
private int TimeOut = 3;
private void Init()
{
Timers = new Timer();
Timers.Interval = (int)TimeSpan.FromSeconds(TimeOut).TotalMilliseconds;
Timers.Tick += Timers_Tick;
}
private void Timers_Tick(object sender, EventArgs e)
{
if (Label_Log == null) return;
Label_Log.Text = string.Empty;
Timers.Enabled = false;
}
/// <summary>
/// 显示日志
/// </summary>
/// <param name="message">日志内容</param>
public void Show(string message)
{
if (Label_Log == null) return;
FormControlExtensions.InvokeIfRequired(Label_Log, () =>
{
Label_Log.Text = message;
if (Timers.Enabled)
Timers.Enabled = false;
Timers.Enabled = true;
});
}
/// <summary>
/// 显示日志
/// </summary>
/// <param name="message">日志内容</param>
/// <param name="objs">可变参数</param>
public void Show(string message, params object[] objs)
{
if (Label_Log == null) return;
FormControlExtensions.InvokeIfRequired(Label_Log, () =>
{
string content = string.Empty;
if (objs != null && objs.Length > 0)
content = string.Format(message, objs);
else
content = message;
Label_Log.Text = content;
if (Timers.Enabled)
Timers.Enabled = false;
Timers.Enabled = true;
});
}
public LogShow()
{
Init();
}
}
用法
在界面拉入一个 Lable 控件,取名为 Label_Log,并讲 Text 属性设置为多个空格
并加入一个按钮,用来显示日志
Form1 代码:
using System;
using System.Windows.Forms;
namespace 定时清理日志
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private LogShow LogShows = new LogShow();
private void Form1_Load(object sender, EventArgs e)
{
LogShows.Label_Log = Label_Log;
}
private void button1_Click(object sender, EventArgs e)
{
LogShows.Show("我是一个日志");
}
}
}
运行后
过了3秒,日志会自动消失,如果在显示日志期间再次点击,计时会重新开始。
结束
如果这个帖子对你有用,欢迎关注 + 点赞 + 留言,谢谢
end