在使用Winform开发程序时,经常会碰到一些耗时操作,如果不加处理的话,第一用户体验不好,第二如果操作会造成程序卡死。所以在这类情况下就要使用进度框来使程序在处理的同时显示一个进度框,效果截图如下:
如上这种方式显示进度框。但是显示进度框的时候也要同时阻断用户对窗体的操作,所以当进度框显示时可使用IMessageFilter的方式来过滤窗体消息,这样就可以达到类似模态对话框的目的,而进度对话框的调用则使用BackgroundWorker来完成,具体的代码如下:
WaitDialog.cs
using System.Linq; using System.Text; using System.Windows.Forms; namespace CommonControl.WaitDialog { /// <summary> /// 等待窗体 /// </summary> internal partial class WaitDialog : Form { /// <summary> /// 计时器,用来步进进度条 /// </summary> private Timer timer; private WaitDialog() { InitializeComponent(); } public WaitDialog(string strTip) : this() { lblWaitTip.Text = strTip; timer = new Timer(); timer.Tick += new EventHandler(timer_Tick); timer.Interval = 1000; timer.Start(); } /// <summary> /// 计时方法,用来步进进度条 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void timer_Tick(object sender, EventArgs e) { pgbWaitTip.Value = (pgbWaitTip.Value + 5) % pgbWaitTip.Maximum; } /// <summary> /// 当触发等待窗体结束时关闭等待窗体 /// </summary> public void WaitDialogEnd() { this.Invoke(new Action(delegate{ this.Close(); })); } /// <summary> /// 窗体关闭方法 /// </summary> /// <param name="e"></param> protected override void OnClosed(EventArgs e) { this.Close(); } } }
上面使用一个Timer来在窗体创建后每隔1秒来使进度条增加5,并循环执行。
WaitUtility.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; namespace CommonControl.WaitDialog { /// <summary> /// 等待窗体结束委托 /// </summary> public delegate void WaitDialogEndHandle(); /// <summary> /// 等待对话框操作类 /// </summary> public class WaitUtility { #region 使用单例模式来使用等待对话框 /// <summary> /// 同步锁 /// </summary> private static object objLock = new object(); private static WaitUtility _Instance; /// <summary> /// 等待对话框操作实体类 /// </summary> public static WaitUtility Instance { get { if (_Instance == null) { lock (objLock) { if (_Instance == null) { _Instance = new WaitUtility(); } } } return _Instance; } } #endregion #region 使用BackgroundWorker来调用等待对话框 /// <summary> /// 等待窗体结束事件 /// </summary> public event WaitDialogEndHandle WaitDialogEnd; /// <summary> /// 用来异步调用进度对话框 /// </summary> private BackgroundWorker bgWorker; /// <summary> /// 初始化BackgroundWorker对象 /// </summary> private void InitBgWorker() { try { if (bgWorker == null || !bgWorker.IsBusy) { bgWorker = new BackgroundWorker(); bgWorker.WorkerSupportsCancellation = true; bgWorker.DoWork += bgWorker_DoWork; bgWorker.RunWorkerAsync(); } } catch { bgWorker = null; _ShowDialog = false; strTip = String.Empty; } } /// <summary> /// 销毁BackgroundWorker对象 /// </summary> private void DestroyBgWorker() { try { WaitDialogEnd(); } catch { } bgWorker = null; _ShowDialog = false; strTip = String.Empty; } /// <summary> /// BackgroundWorker对象的后台工作方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void bgWorker_DoWork(object sender, DoWorkEventArgs e) { WaitDialog dlg = new WaitDialog(strTip); this.WaitDialogEnd += dlg.WaitDialogEnd; dlg.ShowDialog(); this.WaitDialogEnd -= dlg.WaitDialogEnd; DestroyBgWorker(); } #endregion #region 等待对话框操作接口 private bool _ShowDialog = false; /// <summary> /// 等待对话框是否显示 /// </summary> public bool ShowDialog { get { return _ShowDialog; } } /// <summary> /// 显示进度对话框 /// </summary> public void Begin() { Begin(""); } /// <summary> /// 进度对话框提示文本 /// </summary> private string strTip = string.Empty; /// <summary> /// 显示进度对话框 /// </summary> /// <param name="strTip">进度对话框提示文本</param> public void Begin(string strTip) { this.strTip = strTip; _ShowDialog = true; InitBgWorker(); } /// <summary> /// 关闭进度对话框 /// </summary> public void End() { DestroyBgWorker(); } #endregion } }
该类是主要的类,提供了Begin和End的对外接口,通过调用Begin来显示进度框,通过调用End来关闭进度框。
Form1.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using CommonControl.WaitDialog; using System.Threading; namespace CommonControl.Test { public partial class Form1 : Form, IMessageFilter { public bool PreFilterMessage(ref Message msg) { return WaitUtility.Instance.ShowDialog; } /// <summary> /// 窗体关闭方法 /// </summary> /// <param name="e"></param> protected override void OnClosed(EventArgs e) { ////将消息筛选器从应用程序的消息泵中删除 Application.RemoveMessageFilter(this); } public Form1() { InitializeComponent(); //将消息筛选器添加到应用程序的消息泵中 Application.AddMessageFilter(this); } private void btnRunWait_Click(object sender, EventArgs e) { WaitUtility.Instance.Begin("等待加载数据!"); Thread.Sleep(10000); WaitUtility.Instance.End(); } } }
凡是要使用进度框的窗体尽量要实现IMessageFilter接口,并时进度框显示时IMessageFilter.PreFilterMessage方法返回true,这样就可以阻断窗体对windows消息的响应,但同时要将窗体注册到消息泵中,并在窗体关闭时从消息泵中删除窗体,具体代码如上。
如上这些就完成了进度对话框的功能。主要难点有:1. 使用Backgroundworker组件来方便地实现进度对话框的调用和关闭;2. 使用IMessageFilter接口来实现消息过滤,以实现模态对话框的方式,同时不影响后台操作。
时间: 2024-10-27 01:41:39