Dimitri Dimitri - 3 months ago 17
C# Question

Trouble with UI Threads and Backgroundworker

What I'm trying to achieve is simple. I have a dynamic timer (one that can be changed by the user) which calls on background worker to go and fetch the user's external IP address. The combination of

Timer
and
BackgroundWorker
is causing some problems. Here's the code:

namespace IPdevices
{
/// <summary>
/// Interaction logic for Main.xaml
/// </summary>
public partial class Main : Window
{

private readonly BackgroundWorker worker;

private IPret iprep;

private Timer timer;


public Main(Client client)
{

InitializeComponent();

iprep = new IPret();

startClock();

worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += worker_ProgressChanged;

}

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ipAdd.Content = e.UserState;
}

private void startClock()
{
timer = new Timer();
timer.Interval = 2000;
timer.Elapsed += new ElapsedEventHandler(clockTimer_Tick);
timer.Start();
}

private void clockTimer_Tick(object sender, ElapsedEventArgs e)
{
timer.Stop();
worker.RunWorkerAsync();
}

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Checking ip");
iprep.refresh();
worker.ReportProgress(0, iprep.getExternalIp());
Console.WriteLine("Found ip");
}

private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
timer.Start();
}

}
}


Essentially, once the timer fires, I wish to fetch the ip address and output on a label in the application. However, I get an exception in the
ProgressChanged
method saying that it can't be changed because another thread owns it. Which thread is that? Is it the
iprep
that is owned by another thread? In fact,
RunWorkerCompleted
never gets fired. I'm having trouble understanding which threads own what and how objects are locked...Any insight would be appreciated.

Answer

This appears to fix it in my test of it

    private void clockTimer_Tick(object sender, ElapsedEventArgs e)
    {
        timer.Stop();
        Action a = () =>
        {
            worker.RunWorkerAsync();
        };
        Application.Current.Dispatcher.BeginInvoke(a);
    }

Also, I'll note this is consistent behavior for Timer in WPF (I hadn't used it in WPF before); trying ipAdd.Content = "Tick"; in the clockTimer_Tick causes the same error. System.Timers.Timer's tick event does not happen on the UI thread.

Comments