-1

Help me please.

I translate the program from the console into Windows forms. After loading and displaying the form, the StartWork() function should work. The code from console Main() I wrote in

private void Form1_Shown (object sender, EventArgs e) 
{
   StartWork();
}

But I'm faced with a problem, the form does not appear until the entire StartWork() function has completed. This takes a long time.

Then I drove StartWork() into a parallel thread, so as not to interfere with the display of the form.

private void Form1_Shown(object sender, EventArgs e)
{
     Thread hread = new Thread(StartWork);
     thread.Start();
}

But there was another problem: in the StartWork() function. I refer to Label.Text and I want to assign it a string, but I get an exception, that I can't do this because I get access to Label.Textnot from the thread where it was created.

How to solve this?

zRirez
  • 109
  • 2
  • 10
  • 2
    Make `StartWork` an `async` method which you can `await`, and then move the user control access to the line behind `await`. Well, google async/await in C# to learn the technique. – Lex Li Mar 20 '17 at 01:34
  • 1
    `new Thread` is for fire-and-forget threads that you never exchange data with. If you want to send data from the thread to your form you should use async/await. – Dour High Arch Mar 20 '17 at 01:40
  • The marked duplicate addresses the specific concern in your question. It may be possible to refactor your `StartWork()` method to be compatible with the older `BackgroundWorker`, the somewhat newer `Task`+`Progress` pattern, or the modern async/await pattern, but there's not enough information in your question to know for sure, never mind give you good, specific advice about how exactly to do that. – Peter Duniho Mar 20 '17 at 02:10

2 Answers2

0

I think is best to use a BackgroundWorker object to do the work and display progress and/or results using the Control.Invoke() method to always make sure UI changes are done on the main thread.

public partial class Form1 : Form
{
    BackgroundWorker bk;
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        var bk=new BackgroundWorker();
        // This gets run when work is complete
        bk.RunWorkerCompleted+=(s, ev) =>
        {
          label1.Text=String.Format("Result={0}", ev.Result.ToString());
        };
        // This gets run to start the work
        bk.DoWork+=(s, ev) => { ev.Result=this.StartWork((double)ev.Argument); };
        bk.RunWorkerAsync(1.234);
    }

    // Form method for setting the label and refreshing the screen (needed)
    void SetLabel(string text)
    {
        label1.Text=text;
        this.Refresh();
    }
    // A delegate declaration for the above method (needed)
    delegate void LabelAction(string text);

    // This is your start work procedure. It is static because it should 
    // only interact with the form via `Control.Invoke()`.
    public static double StartWork(double argument)
    {
        // If in different thread use the `LabelAction` delegate to set the label
        if (label1.InvokeRequired)
        {
            label1.Invoke(new LabelAction(SetLabel), $"Work Started with {argument}");
        }
        // Do all the work here
        Thread.Sleep(2000);
        // We're done now

        // If in different thread use the `LabelAction` delegate to set the label
        if (label1.InvokeRequired)
        {
            label1.Invoke(new LabelAction(SetLabel), "Work Finished");
        }
        Thread.Sleep(500);
        return Math.PI;
    }
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
0

I decided to add another answer using the newer and easier async/await pattern. I hope I did it correctly as I don't have much experience with it

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        StartWork(1.234).Start();
    }

    public async Task StartWork(double argument)
    {
        label1.Text=$"Work Started with {argument}";
        this.Refresh();
        // Do all the work here
        await Task.Run(() => Thread.Sleep(2000));
        // 
        label1.Text="Work Finished";
        this.Refresh();
        Thread.Sleep(500);
        label1.Text=String.Format("Result = {0}", Math.PI);
        this.Refresh();
    }
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133