C# Wpf異步修改UI,多線程修改UI(二)


1.使用定時器異步修改

這是相對比較簡單的方法

在Wpf中定時器使用DiapatcherTimer,不使用Timer原因:

在一個應用程序中,Timer會重復生成time事件,而DispatcherTimer是一個集成到了Dispatcher隊列中的時鍾,這可以使它被按照指定的時間間隔以指定的priority定期執行。

對於一個Timer時鍾事件,系統並不能保證在時間間隔到達后被立即執行,但是能夠確保在時間間隔到達之前不被執行。這是因為DispatcherTimer像其他操作一樣被放置在了Dispatcher隊列中。何時執行DispatcherTimer事件依賴於隊列中的其他任務以及他們的優先級.

如果一個WPF應用程序使用了Timer時鍾,那么它的事件必須在一個單獨的時鍾線程中運行,而不是在UI線程中,這對於WPF應用程序毫無用處——你沒法在UI線程之外直接訪問UI元素,而只能通過Invoke或者BeginInvoke將操作發送給Dispatcher 對象,委托Dispatcher去執行UI操作

看到這里,你大概知道了為什么我們在WPF中應該用DispatcherTimer而不是Timer了:DispatcherTimer與Dispatcher運行於同一個線程中——UI線程,而且具有相同的DispatcherPriority優先級。

實例:

Xaml代碼:

<Grid>
<TextBox x:Name="textBox" Padding="10"
            Height="45" 
            TextWrapping="Wrap" Text="TextBox"
            VerticalAlignment="Top" Margin="10,105,10,0"/>
</Grid>

后台代碼:

//啟動其他線程處理,調用失敗
//Task.Run(()=> {
//    DispatcherTimerHelper.DoWork(textBox);
//});

//主線程調用成功
DispatcherTimerHelper.DoWork(textBox);
public class DispatcherTimerHelper
{
    //定時器,在指定事件內重復執行
    //在非主線程中,會出現異常,當前線程結束
    static DispatcherTimer _timer = new DispatcherTimer();
    public static void DoWork(TextBox textBlock)
    {
        _timer.Interval = new TimeSpan(0, 0, 1);
        EventHandler event1 = new EventHandler(timer_Tick);
        _timer.Tick += event1;
        _timer.Tag = textBlock;
        _timer.Start();
    }
    public static void Stop()
    {
        _timer.Stop();
    }
    static void timer_Tick(object sender, EventArgs e)
    {
        DispatcherTimer timer = sender as DispatcherTimer;
        TextBox box = timer.Tag as TextBox;
        box.Text = "張三"+DateTime.Now;
    }
}

2.使用BackgroundWorker

這個類是專門用於簡化Windows Form程序與線程相關的問題設計的,同樣適用於WPF程序.適合於一個長期的后台進程,支持進度通知,取消支持,完成通知等功能.

使用方法也很簡單,創建一個BackfruopWorker實例,它有幾個事件.

DoWork事件會在另外一個線程中執行,用RunWorkerAsync()啟動.所以在這個事件中不要去處理修改界面的事情

RunWorkerCompleted事件,在DoWork事件返回時(正常或者異常返回),在圖形的線程中執行,所以可以修改界面

ProgressChanged事件,使用ReportProgress()方法調用,同時是在圖形界面的線程中執行,通常負責修改一下進度條什么的.而ReportProgress()方法,通常會在DoWork的事件中調用,然后給一個百分比的值.要使用這個功能,需要把WorkerReportsProgress屬性設置成true

另外值得一說的是,要取消支持需要把WorkerSupportsCancellation屬性設為true,使用CancelAsync()方法調用,但是這個調用不會終止進程,所以在DoWork事件中需要判斷CancellationPending.

實例:

Xaml代碼:

<StackPanel>
    <ProgressBar Name="progressBar" Height="20" Width="200" Margin="10,80,20,10"></ProgressBar>
    <Button Name="btnProcess" Width="100" Click="btnProcess_Click" Margin="5">開始后台任務</Button>
    <Button Name="btnCancel" Width="100" Click="btnCancel_Click" Margin="5">取消后台任務</Button>
    <Label x:Name="label" Content="Label" Margin="10"/>
</StackPanel>

C#后台代碼:

/// <summary>
/// Thread9.xaml 的交互邏輯
/// </summary>
public partial class Thread9 : Window
{
    BackgroundWorker bgWorker = new BackgroundWorker();
    public Thread9()
    {
        InitializeComponent();

        bgWorker.WorkerReportsProgress = true;
        bgWorker.WorkerSupportsCancellation = true;
        //執行任務代碼
        bgWorker.DoWork += DoWork_Handler;
        //執行過程觸發
        bgWorker.ProgressChanged += ProgressChanged_Handler;
        //執行結束,或有異常結束觸發
        bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler;
    }
    private void btnProcess_Click(object sender, RoutedEventArgs e)
    {
        //開始執行
        if (!bgWorker.IsBusy)
        {
            bgWorker.RunWorkerAsync();
        }
    }
    private void ProgressChanged_Handler(object sender, ProgressChangedEventArgs args)
    {
        //在過程改變事件中可以修改UI內容
        progressBar.Value = args.ProgressPercentage;
        label.Content = "ProgressChanged方法執行完成" + args.ProgressPercentage;
    }
    private void DoWork_Handler(object sender, DoWorkEventArgs args)
    {
        //在DoWork中修改UI同樣會拋出異常
        //label.Content = "DoWork方法執行完成";
        BackgroundWorker worker = sender as BackgroundWorker;
        for (int i = 1; i <= 100; i++)
        {
            if (worker.CancellationPending)
            {
                args.Cancel = true;
                break;
            }
            else
            {
                //手動觸發觸發過程,代碼執行
                worker.ReportProgress(i);
                Thread.Sleep(100);
            }
        }
    }
    private void RunWorkerCompleted_Handler(object sender, RunWorkerCompletedEventArgs args)
    {
        progressBar.Value = 0;
        if (args.Cancelled)
        {
            MessageBox.Show("后台任務已經被取消。", "消息");
        }
        else
        {
            MessageBox.Show("后台任務正常結束。", "消息");
        }
    }
    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
        //結束執行
        bgWorker.CancelAsync();
    }
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM