0

I am building a Windows Forms App, where I created a UserControl (called MyControl).

This UserControl can generate and return a value using a BackgroundWorker.

My question is, how can my form1 show in real-time the value produced by the UserControl when the calculation is completed?

public partial class MyControl : UserControl
{
    BackgroundWorker bgWorker;
    private string _num;

        public string Num
        {
            get
            {
                return _num;
            }
            set
            {
                _num = value;
            }
        }

        public MyControl()
        {
            InitializeComponent();

            bgWorker = new BackgroundWorker();
            bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
            bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
        }

        void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.BeginInvoke((MethodInvoker)delegate
            {
                //Done
                MessageBox.Show(e.Result.ToString());
                //↑↑↑↑↑ HERE
                //I want to return to form1
                // like to rewrite: form1.textbox1.text = e.Result.ToString();
            });

            btnStartAsyncOperation.Enabled = true;
            btnCancel.Enabled = false;
        }

        void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string Num = A_huge_calculation();
            e.Result = Num;
        }

        private void RunCalculate(string Num)
        {
            if (bgWorker.IsBusy != true)
            {
                bgWorker.RunWorkerAsync(Num);
            }
        }

        private void btnStartAsyncOperation_Click(object sender, EventArgs e)
        {
            RunCalculate(_num);

        }
    }
}

Why use BackgroundWorker? Because it will do an unpredictable thing(like generate the MD5 and SHA1 checksum for any file), and I don't wanna the main form fell asleep for end user.

Besides form2 will addition this UserControl like form1, and assign unique control to show the value.

form2.cs

public partial class Form2 : Form
{
    MyControl mycontrol;
    private void Form2_Load(object sender, EventArgs e)
    {
        mycontrol.SentTO = 'richtextBox1';
        //It can be assign any container name to receive the value when completed.
        //when trigger the execute button inside the MyControl
    }
    private void button1_Click(object sender, EventArgs e)
    {
        mycontrol = new MyControl();
        mycontrol.Num = "xxxxxx";

        //run mycontrol
        //mycontrol.run();

        richtextBox2.Text = mycontrol.value; //Here, when the MyControl calculation is completed.

    }
}

I also have been thinking to set a timer listen to this user control get value every 1000ms.

form1.cs

    private void timer1_Tick(object sender, EventArgs e)
    {
        if( ! string.IsNullOrEmpty(mycontrol.value))
        {
            richtextBox2.Text = mycontrol.value; //here is when the MyControl calculation is completed.
            timer1.Stop();
        }
    }

The old ideal was to access the public value of MyControl by a timer, when MyControl completed, it will return a non-null value, and the timer stop MyControl detection. But it will derivative why the MyControl can know what is the timer name in form1? Or form1 be set two or more MyControls, needs more timers to detect every MyControl.

kkasp
  • 113
  • 1
  • 9
  • 1
    the form should probably listen to an event in the user control (you have to create an event in the user control and emit it, and provide it with the relevant data. Then when the form gets the data it can display it as it needs. This keeps your form and user control de-coupled and stops you having to pass round references to forms. It also deals nicely with the asynchronous nature of the background processing. – ADyson Dec 19 '18 at 10:35
  • 1
    Using BeginInvoke in the RunWorkerCompleted event handler is not useful, it already runs on the UI thread. If you need the form class to do anything with one of its controls then be sure to raise your own event to let it know. Do beware that starting a thread in a control is a bad idea. A control has no control over its lifetime, it gets disposed by the form. That makes it quite hard to safely stop that worker thread. Required, you can't just leave it running. – Hans Passant Dec 19 '18 at 10:36
  • Use the report progress event which can return a state object as well as an integer. – jdweng Dec 19 '18 at 10:42
  • If the computation doesn't take much time (so you don't need to set [WorkerReportsProgress](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker.workerreportsprogress) and use the [ProgressChanged](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker.progresschanged) Event), you could use a custom public event that is raised when the `RunWorkerCompleted` event is reached. Your Form can subscribe to this event and get direct updates. – Jimi Dec 19 '18 at 10:42
  • The same applies even when it's the `ProgressChanged` event that generates the updates. It just needs a more careful design, to avoid harassing the subscribers. – Jimi Dec 19 '18 at 10:54
  • Why use a BGW at all? Where is the background/asynchronous/real-time work you want to do? BGW can only perform the equivalent of `await Task.Run(someJob)`. It *can't* handle event streams, or events/notifications raised in threads other than the UI – Panagiotis Kanavos Dec 19 '18 at 10:55
  • You can probably replace all this with `async void btnStartAsyncOperation_Click(){ var result=await Task.Run(()=>SomeHeavyJob(Num)); MessageBox.Show($"{result}");}`. If the control doesn't do something more, you could remove it entirely and keep only the call to `var result=await Task.Run(()=>SomeHeavyJob(Num))` in your form – Panagiotis Kanavos Dec 19 '18 at 10:58
  • Use a BGW is for an unpredictable calculation. – kkasp Dec 20 '18 at 03:16
  • "... public event that is raised when the event is reached. Your Form can subscribe to this event and get direct updates ..." Yes, thanks Jimi, this way is I want, but the computation will be take a long time. – kkasp Dec 20 '18 at 04:17
  • I find a solution at https://stackoverflow.com/a/7880901/5049864 – kkasp Dec 20 '18 at 09:02

0 Answers0