使用线程新建WPF窗体(公用进度条窗体)
项目中需要一个公用的进度条窗体.大家知道在wpf中,有两个线程,一个是UI线程,另一个是监听线程(一直监听用户的输入).如果我们后台有阻塞UI线程的计算存在,那么界面上的比如进度条什么动态的UI都会被因为阻塞而静止不动.
所以我的想法就是新起一个线程来负责进度条窗体的显示与关闭,具体实现代码如下:
private static Thread th; /// <summary> /// 显示进度条 线程 /// </summary> /// <param name="msg">进度条界面需要显示的文字</param> /// <param name="sleep">增加进度条存在的延迟时间,单位为毫秒</param> public static void BeginProgressBar(string msg, int sleep) { try { if (th == null || th.ThreadState == ThreadState.Aborted) { th = new Thread(new ThreadStart(() => { WinProgressBar win = new WinProgressBar(); win.Tips = msg; win.Topmost = true; win.Show(); System.Windows.Threading.Dispatcher.Run(); })); th.SetApartmentState(ApartmentState.STA); th.IsBackground = true; } if (th.ThreadState != ThreadState.Background || th.ThreadState == ThreadState.Unstarted || th.ThreadState != ThreadState.Running) { th.Start(); } } catch (Exception) { th = new Thread(new ThreadStart(() => { WinProgressBar win = new WinProgressBar(); win.Tips = msg; win.Topmost = true; win.Show(); System.Windows.Threading.Dispatcher.Run(); })); th.SetApartmentState(ApartmentState.STA); th.IsBackground = true; th.Start(); } Thread.Sleep(sleep); } /// <summary> /// 结束进度条 /// </summary> public static void EndProgressBar() { if (th.IsAlive) th.Abort(); }
呵呵,大家仔细分析下代码看有什么问题没有?而你们又有什么好的解决方案没有?
在使用过程中发现一个很严重的问题,出现一个进度条窗体,系统内存就会增大一点,到最后要么卡死,要么自动退出.
原来在多线程程序运行中,由用户取消操作是一种非常常见的场景,比如用户使用windows资源管理器在当前文件夹中搜索文件时,可以通过点击其它文件夹而取消搜索。
中途停止一个线程的执行,通常用Thread.Abort方法,但这种方式会造成程序涉及的数据完整性受到破坏,线程所占用的一些系统资源(比如文件句柄等)也可能无法完成。比较合理的方式是外界提出"取消操作"的请求,然后由线程自身来决定如何处理这一请求。
在设计多线程程序时,可设置一个用于接收外部取消消息的属性,然后在线程函数中分阶段地检测这一属性,每个阶段的检查点由软件开发者确定,并且决定线程如何优雅退出。
所以对上述代码进行了改进
首先设置一个公共变量:
public static bool IsClsoeProgeressBar = true;
然后在进度条窗体中实时读这个数据
/// <summary> /// WinProgressBar.xaml 的交互逻辑 /// </summary> public partial class WinProgressBar : Window { public string Tips { get; set; } DispatcherTimer timer; public WinProgressBar() { InitializeComponent(); timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(200); timer.Tick += new EventHandler(timer_Tick); timer.IsEnabled = true; this.Topmost = true; //this.Loaded += delegate //{ // if (this.Tips != "") // tbTips.Text = this.Tips; //}; } void timer_Tick(object sender, EventArgs e) { if (Common.CommWindow.IsClsoeProgeressBar) { if (this.Visibility == Visibility.Visible) { progressBar1.IsIndeterminate = false; this.Hide(); } } else { if (this.Visibility != Visibility.Visible) { progressBar1.IsIndeterminate = true; this.Show(); } } } }
然后在系统加载的时候,创建这个进度条窗体
try { Thread th = new Thread(new ThreadStart(() => { WinProgressBar win = new WinProgressBar(); // win.Tips = msg; win.Show(); System.Windows.Threading.Dispatcher.Run(); })); th.SetApartmentState(ApartmentState.STA); th.IsBackground = true; th.Start(); } catch (Exception) { }
我们只需改变公共变量来控制窗体的显示与隐藏
/// <summary> /// 显示进度条 线程 /// </summary> public static void BeginProgressBar() { IsClsoeProgeressBar = false; } /// <summary> /// 结束进度条 /// </summary> public static void EndProgressBar() { //cts.Cancel(); IsClsoeProgeressBar = true; }
还有一个比较好的方法:
#region Methods /// <summary> /// 一个耗时的任务 /// </summary> private void BigTask() { Thread.Sleep(3000); } private void button1_Click(object sender, RoutedEventArgs e) { MyProgressBar(BigTask); } private void MyProgressBar(Action mywork) { var worker = new BackgroundWorker(); var window = new BusyWindow(); worker.DoWork += (s, e2) => { mywork(); }; worker.RunWorkerCompleted += (s, e2) => { MessageBox.Show("任务已经完成"); window.Close(); }; worker.RunWorkerAsync(); window.Show(); } #endregion
这个由网友热情的沙漠提供,也很不错,大家可以参考下