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();
    }
}

转载于:https://www.cnblogs.com/sjqq/p/6627973.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Resource Page Description 在以前的文章中,我曾多次强调应用程序中异步化的重要性。尤其对于IO密集型操作,异步执行对于应用程序的响应能力和伸缩性有非常关键的影响。正确使用异步编程能够使用尽可能少的线程来执行大量的IO密集型操作。可惜的是,即时异步编程有避免线程阻塞等诸多好处,但是这种编程方式至今没有被大量采用。其原因有很多,其中最主要的一点可能就是异步模型在编程较为困难,导致许多开发人员不愿意去做。 异步,则意味着一个任务至少要被拆分为“段式”的调用方式:一个方法用于发起异步请求,另一个方法用于异步任务完成后的回调。与传统方法调用方式相比,异步调用时的中间数据不能存放在线程栈上,方法之间的也不能简单地通过参数传递的方式来共享数据。此外,传统方法调用中的try…catch…finally,using等关键字都无法跨越方法边界,因此异步编程在处理异常,保护资源等方面也需要花更大的精力才行。如果一不小心,轻则造成资源泄露,重则使整个应用程序崩溃。 因此,无论是微软官方还是社区中都出现了一些简化异步编程方式的组件,例如微软并行与协调运行时和Wintellect's .NET Power Threading Library中的AsyncEnumerator。同时,我基于AsyncEnumerator构建了一个AsyncTaskDispatcher组件,使多个有依赖关系的异步操作之间的协作调用得以大大简化。 以上是引用,自己做了个更简单的demo

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值