views:

127

answers:

3

I am trying to disable parts of the UI in a .NET app based on polling done on a background thread. The background thread checks to see if the global database connection the app uses is still open and operable.

What I need to do, and would prefer to do it without polling on the UI thread, is to add an event handler that can be raised by the background thread if the connection status changes. That way, any form can have a handler that will disable those parts of the UI that require the connection to function.

Attempting to use a straight event declaration in the class that holds the thread sub, and raising the event in the background thread causing cross-thread execution errors regarding accessing UI controls from other threads.

Obviously, there is a correct way to do this, but we have limited experience with events (single threaded apps mainly), and almost none with delegates. I've read through documentation and examples for delegates, and it seems to be closer to what we need, but I'm not sure how to make it work in this instance.

The app is written mainly in VB.NET, but an example or help in C# is fine too.

A: 

You solve the cross thread execution error by using Control.Dispatcher.Invoke:

public void EnventHandlerMethod(object sender, EventArgs e)
{
    myLabel.Dispatch.Invoke(new Action(() => { myLabel.Content = "Updated"; }));
}

The signature of the event handler can vary depending on the event you're handling. Of course if you're simply changing the control in another thread, you only need the body of the above method.

Also this (I think) assumes .NET 3.5

Matt Ellen
This solves it on the client subscription side, but not on the connection pooling side...
Reed Copsey
A: 

Try the Invoke method of the control you want to alter, or any control really, and supply a small function to do the work you want. Or see BeginInvoke for doing it asynchronously.

Just be careful about threading issues, altering variables the "main" thread is altering, etc.

Kevin
This (almost) solves it on the client subscription side, but not on the connection pooling side...
Reed Copsey
+2  A: 

You can marshal the raising of the event back onto the UI thread. Since this is likely (if you've designed well) being done from a separate class, my recommendation would be to pass a copy of the current SynchronizationContext into your class that handles the background process.

Here's some sample code in C#:

public class BackgroundWork
{
    public SynchronizationContext Context { get; private set; }
    public BackgroundWork(SynchronizationContext context)
    {
        this.Context = context;
    }

    // Thread handler, etc...

    // Method to raise event
    void RaiseEvent()
    {
         Context.Post( (state) =>
         {
             // Raise the event
             this.ConnectionStatusChanged(this, EventArgs.Empty);
         }, null);
    }
}

Then, when you construct the class (in your UI thread), pass it the current context:

BackgroundWork worker = new BackgroundWork(SynchronizationContext.Current); // This passes the UI thread context...
worker.Start();

(This, by the way, is the same technique the BackgroundWorker class uses, which is why it works identically for both Windows Forms and WPF...)

Reed Copsey