使用線程新建窗體
項目中需要一個公用的進度條窗體.大家知道在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
這個由網友熱情的沙漠提供,也很不錯,大家可以參考下
