0

I've been trying to implement multi-class-thread GUI management. As In I want different threads spread across multiple classes in different .cs files to update the UI as needed.

I've searched stackoverflow and other sources and found that most people use Dispatcher.Invoke or something similar. So I decided to start testing...

So below is a thread in a class called wThread.cs,

public class wThread
{
    public EventHandler SignalLabelUpdate;
    public Dispatcher uiUpdate;

    public wThread()
    {
        uiUpdate = Program.myForm.dispat;
        //the uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher?
        Thread myThread = new Thread(run);
        myThread.Start();
    }
    Action myDelegate = new Action(updateLabel);
    // is there a way i can pass a string into the above so updatelabel will work? 

    public void updateLabel(String text)
    {
        if (SignalLabelUpdate != null)
            SignalLabelUpdate(this, new TextChangedEvent(text));
    }

    public void run()
    {
        while (uiUpdate == null)
            Thread.Sleep(500);
        for (int i = 0; i < 1000; i++)
        {
            //I hope that the line below would work 
            uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
            // was also hoping i can do the below commented code
            // uiUpdate.Invoke(myDelegate)
            Thread.Sleep(1000);
        }
    }
}

Below is my form1.cs it's the pre-loaded code from visual studio 2012,

public partial class Form1 : Form
{
    public Dispatcher dispat;
    public Form1()
    {
        dispat = Dispatcher.CurrentDispatcher;
        InitializeComponent();
        wThread worker = new wThread();
    }
}

Most of my questions are in the comments above but here are them listed:

  1. The uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher? (wThread.cs problem)

    uiUpdate = Program.myForm.dispat'
    
  2. Is there a way i can pass a string into the above so updatelabel will work?

    Action myDelegate = new Action(updateLabel);
    
  3. I hope that the line below would work

    uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
    
  4. Was also hoping i can do the below commented code

    uiUpdate.Invoke(myDelegate)
    

EDIT: I moved the wThread constructor wThread worker = new wThread() out of the form1 initialization area... and it fixed my nullpointer. Instead I move the wThread constructor into the static main void where the form is constructed... like Application.Run(myForm);

Unfortunately the wThread will not start until I close the UI.. What is the best thing to do about this? Make another thread before the Application.Run starts my Form and use that thread to start my real thread?

Ya Wang
  • 1,758
  • 1
  • 19
  • 41
  • 2
    what UI tech are you using? I see the Dispatcher and also Form? WinForms or WPF? – bic Dec 10 '14 at 21:48
  • The easiest way to implement working on background threads is to use MVVM (assuming you're in WPF): http://stackoverflow.com/a/2034333/1561465 – siva.k Dec 10 '14 at 21:49

1 Answers1

0

@1: In Form1 constructor, place a breakpoint at dispat = Dispatcher.CurrentDispatcher; and check what's the value. It may be just that the Dispatcher.CurrentDispatcher is null and you have to get it i.e. a bit later, not right in the ctor

@2 yes. Use Func<string> instead of action. The Invoke and BeginInvoke take a delegate, and also a set of params object[] that form the parameters of invocation of the delegate. Of course, the passed parameters array must exactly match the delegate signature, so when using Func<string> use a object[] with just one item: the string.

@3 - yes, that would work, as long as the i is a simple local variable thatcan be captured by the delegate closure. If it's foreach iterator or some nonlocal member, it can have problems, but, well, different story.

@4 - yes it would work, just remember about passing the params for Func<string> too.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • 1
    This is more of a question than an answer, but wouldn't it be better to use Task over BeginInvoke as in http://stackoverflow.com/questions/22686467/use-task-run-instead-of-delegate-begininvoke – tradetree Dec 10 '14 at 22:51
  • @tradetree - Just about anything would be better, Rx, Task, even good old BackgroundWorker would suffice and save some typing. However, I got the impression that the OP asked specifically about that set of tools. Recently, it's quite easy to find articles/tutorials about Tasks, even articles about using threads implicitly through the BGW are still out there and they pop out from google when asking for winforms and threads.. So, this time, I only intended to answer the specific questions. – quetzalcoatl Dec 10 '14 at 23:03
  • Heh, also must note that in some places Tasks and their whole underlying framework are actually much more complex and harder to fully understand than just "bounce this code to that dispatcher/thread" + some locking or thread syncing :) But, that's not the place/time. My opinion: BackgroundWorker for beginners, Tasks for advanced, BeginInvoke for desperates a.k.a. "Really no time"/"Do and forget"/"I write and outahere,bye". – quetzalcoatl Dec 10 '14 at 23:07