Pocket PC

On the client-side, a Pocket PC application was used. Since this has no guaranteed connectivity, some additional techniques had to be used to improve the end-user experience.

First of all, when calling a webservice from a Pocket PC, it was possible that the call would take a long time. If this would have been done synchronously, the application would lock up as long as the call was being processed. To prevent this, the call was made asynchronously and a progress bar was displayed.

To achieve this, a Timer was used from the System.Threading class, to update the progress bar when it was visible. This caused the timer to run on a different thread from the application, and make call-backs at certain intervals to update the user interface containing the progress bar.

The following code was used to easily start and stop the progress bar:

 

using System;

using System.Threading;

 

namespace MediaService.Pocket {

  public class MediaForm : System.Windows.Forms.Form {

    private System.Threading.Timer progressTimer;

    private OpenNETCF.Windows.Forms.ProgressBarEx asyncProgress;

    private System.Windows.Forms.Label asyncLabel;

 

    public MediaForm(Int32 userId, String authTicket) {

      TimerCallback progressDelegate = new TimerCallback(this.UpdateProgress);

      this.progressTimer = new System.Threading.Timer(progressDelegate, null,

                                          Timeout.Infinite, Timeout.Infinite);

    } /* MediaForm */

 

    private void StartProgress(ProgressEnum progressType) {

      // Reset progressbar and show

      this.asyncProgress.Value = this.asyncProgress.Minimum;

      this.asyncProgress.Visible = true;

      this.asyncLabel.Visible = true;

      this.asyncLabel.Text = "Retrieving Content";

      this.progressTimer.Change(0, 100);

    } /* StartProgress */

 

    protected void UpdateProgress(Object state) {

      if (this.asyncProgress.Value + 1 > this.asyncProgress.Maximum) {

        this.asyncProgress.Value = this.asyncProgress.Minimum;

      } else {

        this.asyncProgress.Value++;

      }

    } /* UpdateProgress */

           

    private void StopProgress() {

      this.progressTimer.Change(Timeout.Infinite, Timeout.Infinite);

      this.asyncProgress.Visible = false;

      this.asyncLabel.Visible = false;

    } /* StopProgress */


After the progress bar was started, an asynchronous call was made to the webservice, preventing the application to lock up, using the following syntax:

 

AsyncCallback callBack = new AsyncCallback(this.OnGetSongs);

IAsyncResult songsResult = this.GetService().BeginGetSongs(callBack, null);


This started the call to the webservice on a different thread, and when the webservice call finished, it called back to the OnGetSongs method in this case. In this method, the results were retrieved and the user interface was updated.

 

private void OnGetSongs(IAsyncResult songsResult) {

  this.availableSongsCache = this.GetService().EndGetSongs(songsResult);

  if (this.InvokeRequired()) {

    this.Invoke(new EventHandler(this.UpdateAvailableSongs));

  } else {

    this.UpdateAvailableSongs(this, System.EventArgs.Empty);

  }

} /* OnGetSongs */


It was possible that the callback occurred from a different thread. In that case it was not possible to update the user interface, since the thread did not own the form controls. To detect if the callback occurred on another thread or not, the following code was used:

 

namespace MediaService.Pocket {

  public class MediaForm : System.Windows.Forms.Form {

    private readonly Thread formThread = Thread.CurrentThread;

 

    private Boolean InvokeRequired() {

      return !this.formThread.Equals(Thread.CurrentThread);

    } /* InvokeRequired */


If the callback happened on another thread, the Invoke method had to be used to handle the update of the user interface on the thread that owned the interface. For this reason, the method updating the interface had to have the following signature:

 

private void UpdateAvailableSongs(object sender, EventArgs e) {


At this point, it was possible to make a webservice call without locking the user interface, and informing the user something is going on thanks to the progress bar.

1 Comment

  • A couple of points, if you want them:

    1. Although your implementation of InvokeRequired is correct, if you just use Control.Invoke always without checking you'll probably find it is as fast.

    2. More importantly, unless I am missing something, you are very lucky with your progressbar updating. You are doing that straight from the Threading.Timer callback which *is* on a different thread. You should use Control.Invoke there as well.

Comments have been disabled for this content.