views:

41

answers:

3

I have a background process that i want to regularly maintain the state of gps location. I am not clear on how to invoke a delegate on the main thread in the ui layer when the threaded method is in another class. Here is sample code. My form launches the thread on load:

public partial class MainScreen : Form
    {
    .
    . // form stuff
    .
    private void MainScreen_Load(object sender, EventArgs e)
    {
        var gpsStatusManager = new GpsStatusManager();
        Thread t = new Thread(gpsStatusManager.UpdateLocation);
        t.IsBackground = true;
        t.Start();
    }

    delegate void GpsDataParameterDelegate(GpsStatus value);
    public void UpdateGpsStatus(GpsStatus value)
    {
        if (InvokeRequired)
        {
            // We're not in the UI thread, so we need to call BeginInvoke
            BeginInvoke(new GpsDataParameterDelegate(UpdateGpsStatus), new object[] { value });
            return;
        }
        // Must be on the UI thread if we've got this far
        gpsStatus.SetGpsStatus(value);
    }
}

I have a domain object class for the gps information:

public class GpsStatus
{
    public void SetGpsStatus(GpsStatus gpsStatus)
    {
        Latitude = gpsStatus.Latitude;
        Longitude = gpsStatus.Longitude;
        CurrentDateTime = gpsStatus.CurrentDateTime;
        NumberOfSatellites = gpsStatus.NumberOfSatellites;
        TotalNumberSatellites = gpsStatus.TotalNumberSatellites;
    }

    public float Latitude { get; private set; }
    public float Longitude { get; private set; }
    public DateTime CurrentDateTime { get; private set; }
    public int NumberOfSatellites { get; private set; }
    public int TotalNumberSatellites { get; private set; }
}

Then, my manager class where i update status in the secondary thread:

public class GpsStatusManager
{
    private GpsStatus _gpsStatus;

    public void UpdateLocationx()
    {
        while (UpdateGpsData()) 
        {
            Thread.Sleep(2000);
        }
    }

    private bool UpdateGpsData()
    {
        SError error;
        SGpsPosition gpsPosition;
        try
        {
            if (CApplicationAPI.GetActualGpsPosition(out error, out gpsPosition, true, 0) != 1)
                return false;
        }
        catch (Exception)
        {
             return false;
        }

        var numberOfSatellites = gpsPosition.Satellites;
        var totalSatellites = gpsPosition.satellitesInfo;
        var datetime = gpsPosition.Time;
        var lat = gpsPosition.Latitude;
        var lon = gpsPosition.Longitude;
        _gpsStatus.SetGpsStatus(lat, lon, datetime, numberOfSatellites, totalSatellites);

        //How do I invoke the delegate to send the _gpsStatus data to my main thread?
        return true;
    }
}

Thanks for any assistance.

A: 

You should use the SynchronizationContext class.

In the UI thread (in any class), set a field (perhaps static) to SynchronizationContext.Current.

You can then call Send or Post on the saved instance to execute code on the UI thread.

SLaks
A: 

Here's one way to do it, just off the top of my head:

public class GpsStatusEventArgs : EventArgs
{
    public GpsStatus Status { get; private set; }
    public GpsStatusEventArgs(GpsStatus status)
    {
        Status = status;
    }
}

public class GpsStatusManager
{
    ...
    public event EventHandler<GpsStatusEventArgs> GpsStatusUpdated;

    private void OnGpsStatusUpdated(GpsStatus gpsStatus)
    {
        EventHandler<GpsStatusEventArgs> temp = GpsStatusUpdated;
        if (temp != null)
            temp.Invoke(this, new GpsStatusEventArgs(gpsStatus));
    }

}

public partial class MainScreen : Form
{
    ...
    private void MainScreen_Load(object sender, EventArgs e)
    {
        var gpsStatusManager = new GpsStatusManager();
        gpsStatusManager.GpsStatusUpdated += new EventHandler<GpsStatusEventArgs>(GpsStatusManager_GpsStatusUpdated);
        ...
    }

    private void GpsStatusManager_GpsStatusUpdated(object sender, GpsStatusEventArgs e)
    {
        UpdateGpsStatus(e.Status);
    }
    ...
}

Then add this to the bottom of UpdateGpsData:

OnGpsStatusUpdated(_gpsStatus);
Jeffrey L Whitledge
A: 

Here is another approach using the ISynchronizeInvoke interface. This is the same pattern the System.Timers.Timer class uses to raise the Elapsed event.

public class GpsStatusManager
{
  public ISynchronizeInvoke SynchronizingObject { get; set; }

  public event EventHandler Update;

  public void UpdateGpsData()
  {
    // Code omitted for brevity.
    OnUpdate(_gpsStatus);
    return true;
  }

  private OnUpdate(GpsStatus status)
  {
    if (SynchronizingObject != null && SynchronizingObject.IsInvokeRequired)
    {
      ThreadStart ts = () => { OnUpdate(status); };
      SynchronizingObject.Invoke(ts, null);
    }
    else
    {
      if (Update != null)
      {
        Update(this, status);
      }
    }
  }

  public class UpdateEventArgs : EventArgs
  {
    public GpsStatus Status { get; set; }
  }
}
Brian Gideon