• 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();
        }
    }
  • 相关阅读:
    JS基础类型和引用类型
    ul的margin撑不开想要的距离的办法
    html中的列表
    BEM的命名规则
    意义模糊的函数签名……文档注释
    最基础,新手入门第一段代码
    Cookie的使用
    用servlet校验密码2
    用servlet进行用户名和密码校验
    登录页面
  • 原文地址:https://www.cnblogs.com/sjqq/p/6627973.html
Copyright © 2020-2023  润新知