如何避免GUI界面冻结?

5
我可以帮你翻译成中文。以下是需要翻译的内容:

我有一段代码,像这样:

private void testToolStripMenuItem_Click(object sender, EventArgs e)
{
    toolStripStatusLabel1.Text = " Device Testing...";

    positive = false;

    clearsensors_gui();
    datarec = false;
    cmd = 04;
    datarec = serialport_FT(0, 1);

    if (datarec)
    {
        char ab = Convert.ToChar(rec_data[1]);
        //MessageBox.Show("\n" + ab + "\n");
        int cab = Convert.ToInt16(ab);
        int cabc1 = cab & 1;
        int cabc2 = cab & 2;
        int cabc3 = cab & 4;
        int cabc4 = cab & 8;
        int cabc5 = cab & 16;
        int cabc6 = cab & 32;

        if (cabc1 == 1)
            ovalShape1.FillColor = Color.Green;
        else
            ovalShape1.FillColor = Color.Red;

        if (cabc2 == 2)
            ovalShape2.FillColor = Color.Green;
        else
            ovalShape2.FillColor = Color.Red;

        if (cabc3 == 4)
            ovalShape3.FillColor = Color.Green;
        else
            ovalShape3.FillColor = Color.Red;

        if (cabc4 == 8)
            ovalShape4.FillColor = Color.Green;
        else
            ovalShape4.FillColor = Color.Red;

        if (cabc5 == 16)
            ovalShape5.FillColor = Color.Green;
        else
            ovalShape5.FillColor = Color.Red;

        if (cabc6 == 32)
            ovalShape6.FillColor = Color.Green;
        else
            ovalShape6.FillColor = Color.Red;

        toolStripStatusLabel1.Text = " Device Tested";
    }
    else
    {
        toolStripStatusLabel1.Text = "Try Again or Communication With Device Failure....";
    }
}

以上代码是用来读取传感器数据的,即datarec = serialport_FT(0, 1);函数在GUI界面上提供了一个传感器输出,稍后将使用红色/绿色椭圆形(ovalShapeX(1-6))表示。

问题:这个函数需要一点时间,因此GUI会在此期间冻结,如何避免这种情况?

我尝试使用后台工作程序,但不知道在哪里放置整个过程,当它转到椭圆形并更改其属性时,还遇到了跨线程操作错误。

我不知道该在后台使用哪部分函数以及何时何地返回到第一个线程,请帮助我使用backgroundworker或使用invoke(如果必须使用线程)。


将对serialport_FT()函数的调用放在后台线程中 - 参见http://stackoverflow.com/search?q=c%23+gui+background。 - Slugart
使用BackgroundWorker,在DoWork()中放置serialport_FT(0, 1),并在Completed()事件中放置if (datarec) { ... } - H H
为了避免跨线程异常,可以像这样做:https://dev59.com/knM_5IYBdhLWcg3w-4rw#1136406 - Mohammad
谢谢你的回复,我正在浏览所有这些文档。 - pdthekd
5个回答

13
你可以像这样做:
toolStripStatusLabel1.Text = " Device Testing...";
positive = false;
clearsensors_gui();
datarec = false;
cmd = 04;

BackgroundWorker worker = new BackgroundWorker();

worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    // Will be run on background thread
    args.Result = serialport_FT(0, 1);
};

worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
    bool result = (bool)args.Result;

    if (result)
    {
        // Do your UI updates here
    }
};

worker.RunWorkerAsync();

一个改进的方法是将 datarecrec_data 合并为元组,作为 args.Result 的一部分。


谢谢...尝试了这个...这次我没有收到任何错误,但是UI没有更新。我把块:**if(datatarec){...}放在if(result){...}**下面。我做错了什么? - pdthekd
你检查了serialport_FT是否返回true以及结果是否为true了吗? - ekholm
是的,结果为真...所以在那之后我调用另一个函数,在**if(datarec){...}**下有行代码。[在问题帖子中] ovalShapesX [1-6]既没有更新也没有给我异常或错误。 - pdthekd
嗯...数据看起来没问题,我假设?UI的东西之前有工作过吗?文本字段有更新吗? - ekholm
数据正常,函数调用完成后使用 toolStripStatusLabel1.Text = "tested"; 进行显示。我上面发布的代码是可行的,除了执行 datarec= serial_FT(0,1); 时会有一个小的冻结点,因此我现在想使用后台工作线程。 - pdthekd
显示剩余3条评论

3
在后台工作者中,您使用 DoWork 事件。
worker.DoWork += new DoWorkEventHandler(yourEventHandler); 

void yourEventHandler(object sender, DoWorkEventArgs e)
{
//your work here
}

1

由于您正在使用WinForms,这里有一篇很棒的MSDN文章,可以帮助您开始在应用程序中使用多个线程:使用多个线程为基于.NET的应用程序提供快速和响应式的UI

这篇文章“几天前”发布,但是其中的原则今天仍然完全有效。

如果您正在使用.NET 4.x版本,还可以使用任务并行库使多线程工作更加容易。

即将推出的.NET 4.5还提供了更加舒适的await和asyc关键字:使用Async和Await进行异步编程


0

使用一个标签,实时更新任务进度。你可以尝试使用这段代码[使用BackGroundWorker]。查看DoWork,在其中放置你的业务逻辑[参见代码中BusinessClass的用法],然后看看ProgressChanged,在任务进度实时变化时后台任务向UI信号发出,最后查看RunWorkerCompleted,在任务完成、错误或取消后处理代码。

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

namespace WindowsFormsApplication1
{
    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);
        }
    }

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

我不明白...你能否再详细解释一下? - pdthekd

0
将此放入后台线程中,因为您已经尝试过(或更好的方法是使用Task),但要小心仅通过Control.Invoke(对于WinForms)或Dispatcher.Invoke(对于WPF)调用与GUI相关的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接