将参数传递到backgroundWorker(用作取消按钮)

发布于 2024-12-15 19:07:39 字数 2593 浏览 0 评论 0原文

一般来说,我是 C# 和面向对象编程的新手。我一直在尝试在 GUI 中实现“取消”按钮,以便用户可以在过程中停止它。

我读过这个问题:如何实现停止/取消按钮?并确定backgroundWorker对我来说应该是一个不错的选择,但给出的示例没有解释如何将参数传递给backgroundWorker。

我的问题是我不知道如何将参数传递给backgroundWorker以使其停止进程;我只能让backgroundWorker 自行停止。

我创建了以下代码来尝试学习这一点,其中我的表单有两个按钮(buttonStart 和buttonStop)和一个backgroundWorker(backgroundWorkerStopCheck):

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

namespace TestBackgroundWorker
{
    public partial class Form1 : Form
    {
        public Form1()
        {         
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
        }

        private System.Timers.Timer myTimer;

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            //If cancellation is pending, cancel work.  
            if (backgroundWorkerStopCheck.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            // Notify the backgroundWorker that the process is starting.
            backgroundWorkerStopCheck.RunWorkerAsync();
            LaunchCode();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void LaunchCode()
        {
            buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
            myTimer = new System.Timers.Timer(5000); // Waste five seconds.
            myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
            myTimer.Enabled = true; // Start the timer.
        }

        void myTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
        }
    }
}

如果代码正常工作,则在用户单击“Start”后,该代码将在那里停留五秒钟在重新启用“开始”按钮之前,或者如果用户单击“停止”,将快速重新激活“开始”按钮。

这段代码有两个问题我不确定如何处理:

1)“myTimer_Elapsed”方法在尝试启用“开始”按钮时会导致 InvalidOperationException,因为“跨线程操作无效”。如何避免跨线程操作?

2)现在backgroundWorker没有完成任何事情,因为我不知道如何向它提供参数,这样当它被取消时,它会停止计时器。

我将不胜感激任何帮助!

I'm new to C# and object-oriented programming in general. I've been trying to implement a "Cancel" button into my GUI so that the user can stop it mid-process.

I read this question: How to implement a Stop/Cancel button? and determined that a backgroundWorker should be a good option for me, but the example given doesn't explain how to hand arguments to the backgroundWorker.

My problem is that I do not know how to pass an argument into backgroundWorker such that it will stop the process; I have only been able to get backgroundWorker to stop itself.

I created the following code to try to learn this, where my form has two buttons (buttonStart and buttonStop) and a backgroundWorker (backgroundWorkerStopCheck):

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

namespace TestBackgroundWorker
{
    public partial class Form1 : Form
    {
        public Form1()
        {         
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
        }

        private System.Timers.Timer myTimer;

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            //If cancellation is pending, cancel work.  
            if (backgroundWorkerStopCheck.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            // Notify the backgroundWorker that the process is starting.
            backgroundWorkerStopCheck.RunWorkerAsync();
            LaunchCode();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void LaunchCode()
        {
            buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
            myTimer = new System.Timers.Timer(5000); // Waste five seconds.
            myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
            myTimer.Enabled = true; // Start the timer.
        }

        void myTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
        }
    }
}

The code, if it worked properly, would just sit there for five seconds after the user clicked "Start" before re-enabling the Start button, or would quickly reactivate the Start button if the user clicked "Stop".

There are two problems with this code that I am not sure how to handle:

1) The "myTimer_Elapsed" method results in an InvalidOperationException when it attempts to enable the Start button, because the "cross-thread operation was not valid". How do I avoid cross-thread operations?

2) Right now the backgroundWorker doesn't accomplish anything because I don't know how to feed arguments to it such that, when it is canceled, it will stop the timer.

I'd appreciate any assistance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一个人的旅程 2024-12-22 19:07:39

首先,避免“跨线程操作无效”的问题是使用 调用控件。您不能使用来自不同线程的控件。

关于第二个问题,我会按照以下方式实现。这是具有取消支持的最小后台工作实现。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
            backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
        }

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                for (int i = 0; i < 50; i++)
                {
                    if (backgroundWorkerStopCheck.CancellationPending)
                    {
                        // user cancel request
                        e.Cancel = true;
                        return;
                    }

                    System.Threading.Thread.Sleep(100);
                }
            }
            finally
            {
                InvokeEnableStartButton();
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            //disable start button before launch work
            buttonStart.Enabled = false;

            // start worker
            backgroundWorkerStopCheck.RunWorkerAsync();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void InvokeEnableStartButton()
        {
            // this method is called from a thread,
            // we need to Invoke to avoid "cross thread exception"
            if (this.InvokeRequired)
            {
                this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
            }
            else
            {
                EnableStartButton();
            }
        }

        private void EnableStartButton()
        {
            buttonStart.Enabled = true;
        }
    }

    internal delegate void EnableStartButtonDelegate();
}

关于向工作线程传递参数,您可以在 RunWorkerAsync() 方法中传递任何对象,并在 backgroundWorkerStopCheck_DoWork 方法中接收它:

  ...
  backgroundWorkerStopCheck.RunWorkerAsync("hello");
  ...

  private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
  {
      string argument = e.Argument as string;
      // argument value is "hello"
      ...
  }

希望它有所帮助。

First of all, the problem to avoid "cross-thread operation was not valid" is use Invoke on controls. You cannot use a control from a different thread.

About the second issue, I would implement it in the following way. This is a minimum background worker implementation with cancel support.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
            backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
        }

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                for (int i = 0; i < 50; i++)
                {
                    if (backgroundWorkerStopCheck.CancellationPending)
                    {
                        // user cancel request
                        e.Cancel = true;
                        return;
                    }

                    System.Threading.Thread.Sleep(100);
                }
            }
            finally
            {
                InvokeEnableStartButton();
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            //disable start button before launch work
            buttonStart.Enabled = false;

            // start worker
            backgroundWorkerStopCheck.RunWorkerAsync();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void InvokeEnableStartButton()
        {
            // this method is called from a thread,
            // we need to Invoke to avoid "cross thread exception"
            if (this.InvokeRequired)
            {
                this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
            }
            else
            {
                EnableStartButton();
            }
        }

        private void EnableStartButton()
        {
            buttonStart.Enabled = true;
        }
    }

    internal delegate void EnableStartButtonDelegate();
}

About passing arguments to the worker, you can pass any object in the RunWorkerAsync() method, and its reveived in the backgroundWorkerStopCheck_DoWork method:

  ...
  backgroundWorkerStopCheck.RunWorkerAsync("hello");
  ...

  private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
  {
      string argument = e.Argument as string;
      // argument value is "hello"
      ...
  }

Hope it helps.

小兔几 2024-12-22 19:07:39

尝试这个示例,您将看到如何将数据传入和传出BackgroundWorker:

public partial class Form1 : Form
{
    BackgroundWorker bw = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();

        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw.WorkerSupportsCancellation = true;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        btnStart.Enabled = false;
        btnCancel.Enabled = true;

        double[] data = new double[1000000];
        Random r = new Random();
        for (int i = 0; i < data.Length; i++)
            data[i] = r.NextDouble();

        bw.RunWorkerAsync(data);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btnStart.Enabled = true;
        btnCancel.Enabled = false;

        if (!e.Cancelled)
        {
            double result = (double)e.Result;
            MessageBox.Show(result.ToString());
        }
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        double[] data = (double[])e.Argument;

        for (int j = 0; j < 200; j++)
        {
            double result = 0;
            for (int i = 0; i < data.Length; i++)
            {
                if (bw.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }
                result += data[i];
            }
            e.Result = result;
        }
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        bw.CancelAsync();
        btnStart.Enabled = true;
        btnCancel.Enabled = false;
    }
}

try this example and you will see how to pass data to and from the BackgroundWorker:

public partial class Form1 : Form
{
    BackgroundWorker bw = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();

        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw.WorkerSupportsCancellation = true;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        btnStart.Enabled = false;
        btnCancel.Enabled = true;

        double[] data = new double[1000000];
        Random r = new Random();
        for (int i = 0; i < data.Length; i++)
            data[i] = r.NextDouble();

        bw.RunWorkerAsync(data);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        btnStart.Enabled = true;
        btnCancel.Enabled = false;

        if (!e.Cancelled)
        {
            double result = (double)e.Result;
            MessageBox.Show(result.ToString());
        }
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        double[] data = (double[])e.Argument;

        for (int j = 0; j < 200; j++)
        {
            double result = 0;
            for (int i = 0; i < data.Length; i++)
            {
                if (bw.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }
                result += data[i];
            }
            e.Result = result;
        }
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        bw.CancelAsync();
        btnStart.Enabled = true;
        btnCancel.Enabled = false;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文