1

I am trying to update a label from a BackgroundWorker thread that calls a method from another class outside the Form. So I basically want to do this:

MainForm.counterLabel.Text = Counter.ToString();

but the label is private. I have looked into things like using BackgroundWorker's progressupdate function, invoke, etc but they don't seem to be what I need.

here is some more of my code:

the MainForm:

clickThread.DoWork += (s, o) => { theClicker.Execute(speed); };
clickThread.RunWorkerAsync();

The Class/Method called:

public void Execute(int speed)
{
    while (running)
    {
       Thread.Sleep(speed);
       Mouse.DoMouseClick();
       Counter++;
       //Update UI here
    }
}

I think I have overly complicated my code a bit :\ and backed myself into a corner.

James
  • 45
  • 5
  • 12
  • if the label is private, do you expose a method or property to set it? – hendryanw Apr 13 '16 at 07:14
  • So to understand the problem: The object "theClicker" has no access to the private label of MainForm, correct? A really poor way of solving this problem would be to pass the label to the Execute method of "theClicker". – Shamshiel Apr 13 '16 at 07:17
  • 5
    The basics of BW are simple - do **not** access UI from inside `DoWork`. Do the non UI stuff in `DoWork` and UI stuff in `ProgressChanged`. – Ivan Stoev Apr 13 '16 at 07:18
  • What is `theClicker`? – Jcl Apr 13 '16 at 07:26
  • @IvanStoev how would access the clickThread from inside the Execute method? – James Apr 13 '16 at 07:28
  • theClicker is a class eg. `var theClicker = new Clicker();` – James Apr 13 '16 at 07:29
  • that's what the `sender` of the event is for. – Ivan Stoev Apr 13 '16 at 07:30
  • 2
    The previous comments were in general. If the only "work" of your clicker is to perform mouse clicks (UI action) on regular interval, BW is not appropriate at all - simple `Windows.Forms.Timer` will be sufficient. – Ivan Stoev Apr 13 '16 at 07:34
  • As others already mentioned, **DO NOT** try to access the UI inside the `DoWork` method. Doing so simply abuses BGW. It was *specifically* created so that you *don't* need to invoke anything from inside `DoWork`. Do any changes you want in the ProgressChanged event. – Panagiotis Kanavos Apr 13 '16 at 07:40
  • @IvanStoev It's not the only work. – James Apr 13 '16 at 07:40
  • 2
    There is no point using a BGW if you don't want to use ProgressChanged. You could just use a raw Thread and call BeginInvoke on the controls. Of course, a *far* better solution would be to use .NET's async/await support so you can run asynchronous Tasks as well as update the UI from the same method. – Panagiotis Kanavos Apr 13 '16 at 07:42

2 Answers2

3

You should use the ProgressChanged-Event to update the UI. The code for the BackgroundWorker should look something like:

internal static void RunWorker()
{
    int speed = 100;
    BackgroundWorker clickThread = new BackgroundWorker
    {
        WorkerReportsProgress = true
    };
    clickThread.DoWork += ClickThreadOnDoWork;
    clickThread.ProgressChanged += ClickThreadOnProgressChanged;
    clickThread.RunWorkerAsync(speed);

}

private static void ClickThreadOnProgressChanged(object sender, ProgressChangedEventArgs e)
{

    someLabel.Text = (string) e.UserState;

}

private static void ClickThreadOnDoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;
    int speed = (int) e.Argument;

    while (!worker.CancellationPending)
    {
        Thread.Sleep(speed);
        Mouse.DoMouseClick();
        Counter++;
        worker.ReportProgress(0, "newText-Parameter");
    }
}

}

Tomtom
  • 9,087
  • 7
  • 52
  • 95
  • 2
    Who downvoted the only correct usage of BGW? One could argue that using Tasks is better and easier, but that doesn't mean that using the purpose-built ProgressChanged event is *wrong* – Panagiotis Kanavos Apr 13 '16 at 07:45
  • I guess this solution would be best. I guess I let myself get married to the idea of having a separate class for the Execute method. Once I refactor and it works, I will accept this answer. – James Apr 13 '16 at 07:51
  • Note that "someLabel.Text = (string) e.UserState;" does not work because the method is static. – James Apr 16 '16 at 19:23
-3

Try to invoke the method.

For Example:-

this.Invoke((Action)(() => Resources.xobjMF.Enabled = true));
Nehal
  • 1,542
  • 4
  • 17
  • 30
sirz
  • 9
  • 2