从后台线程更新UI,该后台线程在线程完成时在主UI中的循环中调用 [英] Updating UI from a background thread which is called in a loop in main UI when the thread finishes

查看:79
本文介绍了从后台线程更新UI,该后台线程在线程完成时在主UI中的循环中调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WinForms应用程序,该应用程序正在调用业务类方法,该方法执行一些繁重的操作,每次调用大约需要5秒钟.主要形式是在循环中调用此方法.此循环可以运行10次,甚至可能达到一万次.

I have a WinForms application that is calling a business class method that performs some heavy duty action taking about 5 seconds for each call. The main form calls this method in a loop. This loop can run from 10 times to maybe up to 10 thousand times.

WinForms应用程序将参数发送给业务类,并具有一个区域来显示每个方法调用所花费的时间以及该方法返回的值.如何通知我的主窗口并使用每次调用返回的方法更新winwin主窗口中的文本区域?

The WinForms application sends a parameter to the business class and has an area to display the time taken for each method call and what the value returned by the method. How do I inform my main window and update a text area in the main winform with what the method has returned for each call?

当前,所有线程完成后,数据立即全部收集完毕.一旦完成每个调用,是否可以为循环的所有迭代更新UI?我不介意是否也按顺序完成.

Currently the data comes all at once after all the threads have finished. Is there a way to update the UI for all the iterations of the loop once the each call is done? I don't mind if it is done sequentially also.

表格

    HeavyDutyClass hd;
    public Form1()
    {
        InitializeComponent();
        hd = new HeavyDutyClass();
    }


    //BUTTON CLICK
    private void Start_Click(object sender, EventArgs e)
    {

        int filecount = 5000;  //BAD - opening 5000 threads! Any other approach?
        hd.FileProcessed += new EventHandler(hd_FileProcessed);


        var threads = new Thread[filecount];

        for (int i = 0; i < filecount; i++)
        {
            threads[i] = new Thread(() => { hd.LongRunningMethod(); });
            threads[i].Start();   
        }

    }

    //BUSINESS CLASS EVENT THAT FIRES WHEN BUSINESS METHOD COMPELTES
    void hd_FileProcessed(object sender, EventArgs e)
    {

        if (dgv.InvokeRequired)
        {
            dgv.Invoke((MethodInvoker)delegate { UpdateGrid(); });

        }
    }

    private void UpdateGrid()
    {
        dgv.Rows.Add(1);
        int i = dgv.Rows.Count;
        dgv.Rows [ i-1].Selected = true;
        dgv.FirstDisplayedScrollingRowIndex = i - 1;

    }

商务重型班

    public event EventHandler FileProcessed;

    public HeavyDutyClass()
    {
    }

    protected virtual void OnMyEvent(EventArgs e)
    {
        if (FileProcessed != null)
        {
            FileProcessed(this, e);
        }
    }

    public bool LongRunningMethod()
    {
        for (double i = 0; i < 199990000; i++)
        {
            //time consuming loop
        }
        OnMyEvent(EventArgs.Empty);
        return true;
    }

推荐答案

添加Winforms项目,在窗体上放置标签控件,复制粘贴此代码并按F5

Add a Winforms Project, Drop a Label Control on the Form , Copy-Paste this code and Hit F5

:已更新,其中包含用户的business class注释

: Updated with the business class comment from the user

NB :我的表单类名为Form3.您可能需要更改Program.cs or vice-versa.

NB: My form class is named Form3. You may have to change your Program.cs or vice-versa.

using System.ComponentModel;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public class BusinessClass
    {
        public int MyFunction(int input)
        {
            return input+10;
        }
    }

    public partial class Form3 : Form
    {
        private BackgroundWorker _worker;
        BusinessClass _biz = new BusinessClass();
        public Form3()
        {
            InitializeComponent();
            InitWorker();
        }

        private void InitWorker()
        {
            if (_worker != null)
            {
                _worker.Dispose();
            }

            _worker = new BackgroundWorker
            {
                WorkerReportsProgress = true,
                WorkerSupportsCancellation = true
            };
            _worker.DoWork += DoWork;
            _worker.RunWorkerCompleted += RunWorkerCompleted;
            _worker.ProgressChanged += ProgressChanged;
            _worker.RunWorkerAsync();
        }


        void DoWork(object sender, DoWorkEventArgs e)
        {
            int highestPercentageReached = 0;
            if (_worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                double i = 0.0d;
                int junk = 0;
                for (i = 0; i <= 199990000; i++)
                {
                    int result = _biz.MyFunction(junk);
                    junk++;

                    // Report progress as a percentage of the total task.
                    var percentComplete = (int)(i / 199990000 * 100);
                    if (percentComplete > highestPercentageReached)
                    {
                        highestPercentageReached = percentComplete;
                        // note I can pass the business class result also and display the same in the LABEL  
                        _worker.ReportProgress(percentComplete, result);
                        _worker.CancelAsync();
                    }
                }

            }
        }

        void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                // Display some message to the user that task has been
                // cancelled
            }
            else if (e.Error != null)
            {
                // Do something with the error
            }
        }

        void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            label1.Text =  string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
        }
    }
}

使用此方法,您也可以非常轻松地实现取消"功能. 请注意,在初始化期间,我设置了WorkerSupportsCancellation = true&然后在DoWork中检查_worker.CancellationPending.因此,如果要通过Cancel Button click取消该过程,则将在按钮处理程序-_worker.CancelAsync();

With this you can achieve Cancel functionality also very easily. Observe that during initialisation, I set the WorkerSupportsCancellation = true & then I check for _worker.CancellationPending in the DoWork. So, if you want to cancel the process by a Cancel Button click, then you will write this code in the button handler- _worker.CancelAsync();

这篇关于从后台线程更新UI,该后台线程在线程完成时在主UI中的循环中调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆