A Component for Easilly Executing Async Tasks

An entry about c# 3.0 | windows forms Publication date 14. October 2007 14:23

With the 2.0 version of the.NET Framework, a component aptly named BackgroundWorker was introduced to simplify the execution of asynchronous tasks in Windows Forms development. A joy to use for sure, but you still have to manually manage queueing of tasks, and give the user an indication that something is happening. Wouldn't it be cool if the BackgroundWorker could disable the button that executed the task and automagically show a progress indicator while it is being carried out? In this post I'll be showing you how to develop a custom component that encapsulates these features, and more.

Lets begin at the end, and look at what we want to accomplish.-Basically, we want to be able to write code like this:

IAsyncTask task = _asyncWorker.NewTask();
task.ControlBehaviors.Add(new EnableControlBehavior(_task2Btn, false, ApplyBehaviorWhen.TaskEnqueued));
task.ControlBehaviors.Add(new ProgressBehavior(_progressBar, 10));
task.PerformTask +=
delegate(object obj, AsyncTaskEventArgs args)
{
for (int i = 0; i < 10; i++)
{
args.ReportProgress(i);
Thread.Sleep(150);
}
};
task.Enqueue();

What we see here, is the code for the _task1Btn click event. It creates a new task, says that the button should be disabled until the task completes and a progress bar displayed while executing, and puts the task on the work queue. The Enqueue() method returns immediately, while the task is added to the work queue and executed on a background thread as soon as possible. When clicking the button, the UI looks something like this:

Disabled button and progress bar while an asynchronous task is executing

Interrested in seing how this works? Read on...

The AsyncWorker

What we want to model are two things - a work manager, and the tasks it should perform. A task consists of a work load and optionally a list of control behaviors. When a task has been defined, it is put on the work managers queue, and at some point the work manager then executes it on a background thread. Lets look at the code that accomplishes this.

When an instance of the AsyncWorker component is constructed, it fires up a background worker thread:

public AsyncWorker(IContainer container)
{
container.Add(this);
if (null == this.Site || !this.Site.DesignMode)
{
// fire up the worker thread
        _workQueueThread = new Thread(new ThreadStart(DoWork));
_workQueueThread.Start();
}
}

This thread runs the DoWork method, which loops until the component is disposed, at which time the _alive variable becomes false and the background thread terminates gracefully:

private void DoWork()
{
do
    {
_emptyQueueResetEvent.WaitOne(); // wait until work is added to the queue
        while (_tasks.Count > 0) // while work is in the queue...
        {
AsyncTask task = _tasks.Dequeue(); // ... get the next task
            Invoke(new MethodInvoker(
delegate
                {
foreach (ControlBehavior behavior in task.ControlBehaviors)
{
behavior.TaskBegins();
}
}));
task.RaiseDoWork(); // perform task
            Invoke(new MethodInvoker(
delegate
                {
foreach (ControlBehavior behavior in task.ControlBehaviors)
{
behavior.TaskFinished();
}
}));
}
}
while (_alive);
}

When there is no work on the queue, the _emptyQueueResetEvent is in the non-signaled state, blocking the thread. But when a task is added to the work queue, this reset event gets signalled and the worker thread wakes up again to process the task:

private void Enqueue(AsyncTask task)
{
Invoke(new MethodInvoker(
delegate
        {
foreach (ControlBehavior behavior in task.ControlBehaviors)
{
behavior.TaskEnqueued();
}
}));
// enqueue the task
    _tasks.Enqueue(task);
// signal the worker thread to awaken it, if it is currently idling
    _emptyQueueResetEvent.Set();
}

Notice that throughout the life-time of a task, its behaviors are notified of the state changes (from enqueued to executing to finished). This enables behaviors to do things like disable controls until the task is finished, update a status message label and more. I've implemented a few different kinds of behaviors, and writing custom ones is as easy as inheriting the ControlBehavior class and overriding the appropriate virtual methods. As an example, lets look at the DisableControlBehavior, which we used in the initial example at the top of this post to disable the Execute button until the task completed:

public class EnableControlBehavior : ControlBehavior
{
ApplyBehaviorWhen _when;
bool _enable;
public EnableControlBehavior(Control control, bool enable, ApplyBehaviorWhen when)
: base(control)
{
_when = when;
_enable = enable;
}
protected internal override void TaskEnqueued()
{
if (_when == ApplyBehaviorWhen.TaskEnqueued)
{
this.Control.Enabled = _enable;
}
}
protected internal override void TaskBegins()
{
if (_when == ApplyBehaviorWhen.TaskBegins)
{
this.Control.Enabled = _enable;
}
}
protected internal override void TaskFinished()
{
this.Control.Enabled = !_enable;
}
}

Here I've hooked into the life cycle of the task in order to disable/enable the given control at the appropriate times. The sample code includes three more behaviors - one for toggling the visibility of controls, one for updating a label with state messages and one for displaying a progress bar (either as a marquee or accurately updated with progress).

I Know You Want Me

If what I've shown so far in this post has intrigued you, then feel free to download the source code for the AsyncWorker here and take a closer look. The download includes a few examples, and the code itself is fairly well documented and should be easy to follow.

Although the component seems to be fairly robust, I feel I should advise you to test it thouroughly before considering using it in any production environment. I wrote this over the weekend to try out a few ideas I had, and as with most of the code I post here on this blog, it should be consider a proof-of-concept and not production-ready code :)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Capture a Screenshot With Cursor

An entry about windows forms Publication date 2. October 2007 09:18

Here's a quick tip on how to capture a screenshot which includes the cursor:

Rectangle screenBounds = Screen.PrimaryScreen.Bounds;
using (Bitmap image = new Bitmap(screenBounds.Width, screenBounds.Height))
{
using (Graphics g = Graphics.FromImage(image))
{
Point p = new Point(0, 0);
g.CopyFromScreen(p, p, image.Size);
Rectangle cursorBounds = new Rectangle(Cursor.Position, Cursor.Current.Size);
Cursors.Default.Draw(g, cursorBounds);
}
image.Save("c:\\screen.jpg", ImageFormat.Jpeg);
}

Normally, Graphics.CopyFromScreen would just capture the screen without the cursor. Thus, we're 'faking' the cursor by drawing the default cursor on top of the captured image... :)

Be the first to rate this post

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Smarter Asynchronous Data Loading

An entry about windows forms Publication date 23. September 2007 13:23

Imagine you have a Windows Forms application that has a dialog for sending email messages. The 'Send Message' dialog has a button that opens a 'Find Recipient' dialog, which lets the user select a recipient from a rather large address book. It takes the data access layer several seconds to come up with the data for this dialog. Hopefully, warning lamps should go off in your head at seeing the following code:

private void FindRecipient_Load(object sender, EventArgs e)
{
_recipientsList.DataSource = AddressBook.GetRecipients();
}

Sure, it works - but since we are loading the data in the same thread as the UI, we're effectively blocking the message pump which makes the application seemingly hang for the time it takes GetRecipients() to return the data. If this method takes 5 seconds, 10 seconds or even a few minutes to return, the user would be forgiven for thinking your application has crashed.

We can surely do better. Lets move the loading of data into a separate thread, and let the user know that it may take a while:

private void FindRecipient_Load(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
PleaseWait pleaseWait = new PleaseWait();
worker.DoWork += new DoWorkEventHandler(
delegate(object s, DoWorkEventArgs args)
{
args.Result = AddressBook.GetRecipients();
});
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate(object s, RunWorkerCompletedEventArgs args)
{
_recipientsList.DataSource = args.Result;
pleaseWait.Dispose();
});
worker.RunWorkerAsync();
pleaseWait.ShowDialog();
}

Here, we're using the BackgroundWorker class to load the data asynchronously. That's a huge improvement, and in many scenarios it will be good enough. But lets take it one step further...

What if we could deduce that the user is likely to soon open the dialog? Then we could start loading the data even before the user requests it, having it ready in time for when it is needed. In this example, we can confidently assume that when the user opens the 'New Message' dialog, he will shortly after want to select a recipient. Why not start loading the address book data right away?

private void NewMessage_Load(object sender, EventArgs e)
{
// here, we start populating the cache asynchronously because we know the
    // user is likely to click the 'Find' button soon. 
    AddressBook.PopulateCacheAsync(); 
}

Now, as soon as the parent dialog opens we start loading the data asynchronously. If the user now spends a few seconds typing in a title for his message, and then clicks the 'Find' button to find a recipient, the data will already have had time to load the data, and the FindRecipient dialog shows up almost instantly.

However, to make this work properly we need to synchronise the PopulateCacheAsync and GetRecipients methods on AddessBook - because if the user clicks the 'Find' button before PopulateCacheAsync has finished populating the cache, then it and GetRecipients will likely get in a tangle. Luckilly, this is quite easy to sort out using the double-checked locking pattern:

public static class AddressBook
{
private static readonly int _noOfRecipients = 500;
static Action<int> _reportProgress = null;
private static IList<string> _cache = null;
private static object _cacheLock = new object();
private static IEnumerable<string> GetMockData()
{
for (int i = 0; i < _noOfRecipients; i++)
{
yield return "Recipient #" + i;
Thread.Sleep(10); // sleep for 10ms each loop to simulate a long-running operation
        }
}
public static void PopulateCacheAsync()
{
ThreadPool.QueueUserWorkItem(
delegate
            {
PopulateCacheCore();
});
}
private static void PopulateCacheCore()
{
if (null == _cache) // if the cache has not already been loaded, populate it
        {
lock (_cacheLock)
{
if (null == _cache) // the cache may have been popualted while we were waiting to get the lock, so check again
                {
_cache = new List<string>(GetMockData());
}
}
}
}
public static IList<string> GetRecipients()
{
PopulateCacheCore();
return _cache;
}
}

And thats it! We've radically improved the perceived performance of our application by being a bit clever about when we load our data. I've included a demo solution that includes the above three scenarios, so you can get a feel for how they perform. The downloadable code also goes even further and adds a progres bar to the 'please wait' dialog.

Download the demo code here (Visual Studio 2008 beta 2 solution)

Be the first to rate this post

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Windows Forms IconAnimator

An entry about windows forms Publication date 26. March 2007 13:25

Out of the box, .NET doesn't have any kind of support for animated icons, which is fair enough as its not something you find yourself in need of very often. However, recently I wrote an application that lives in the tray area of the taskbar, and I ended up using a set of color-coded icons to give a visual clue to the user on the state of the application (connected, disconnected, error etc). I found myself wanting to use an animated icon for when the application was doing work, to differentiate it from the idle states.

What I came up with, was a component I've called the IconAnimator. The IconAnimator basically allows you to specify a control for which it will animate the Icon of (which can be any component with a public mutable Icon property - a Form or NotifyIcon usually), and exposes Start and Stop methods for controlling the animation. The frames of the animation is stored in an ImageList, which has a rather nice "side-effect" - you dont have to use .ico files, it will work just as well with any kind of image type the ImageList supports. Credit to Ryan Farley for working out how to get Icons out of an ImageList :)

To use the IconAnimator, just drop it onto a Form along with an ImageList component:

Components

Next, set its TargetComponent to the component that you want to animate the Icon for (in this example, the Form itself), and point the Frames property to the ImageList:

IconAnimator property window

Finally, add the frames of the animation to the ImageList:

ImageList

With everything set up, you can then simply call the Start() and Stop() methods on the IconAnimator in your code whenever you want to start/stop the animation.

Here is the source code, feel free to use it in your own projects:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
public class IconAnimator : Component
{
    #region Fields
private Component _target;
Timer _timer;
PropertyInfo _iconProperty;
private ImageList _frames;
int _currentIndex = 0;
    #endregion
#region Constructor
/// <summary>
    /// Initializes a new instance of the <see cref="IconAnimator"/> class.
    /// </summary>
    public IconAnimator()
{
_timer = new Timer();
_timer.Tick += new EventHandler(_timer_Tick);
}
    #endregion
#region Animation
[MethodImpl(MethodImplOptions.Synchronized)]
void _timer_Tick(object sender, EventArgs e)
{
Icon icon = Icon.FromHandle((this.Frames.Images[_currentIndex] as Bitmap).GetHicon());
IDisposable previousIcon = _iconProperty.GetValue(this.TargetComponent, null) as IDisposable;
_iconProperty.SetValue(_target, icon, null);
if (null != previousIcon)
{
previousIcon.Dispose();
}
++_currentIndex;
if (_currentIndex >= this.Frames.Images.Count)
{
_currentIndex = 0;
}
}
    #endregion
#region Properties
/// <summary>
    /// Gets or sets the component whose icon should be animated by the IconAnimator.
    /// </summary>
    /// <value>The component to animate.</value>
    public Component TargetComponent
{
get { return _target; }
set
        {
if (null == value)
{
_target = null;
}
else
            {
_iconProperty = value.GetType().GetProperty("Icon");
if (null == _iconProperty || _iconProperty.PropertyType != typeof(Icon))
{
throw new ArgumentException("Unsupported Control type. " + 
"Must be a control with a public Icon property of type Icon.");
}
_target = value;
}
}
}
/// <summary>
    /// Gets or sets the frames of the animation
    /// </summary>
    /// <value>The frames.</value>
    public ImageList Frames
{
get { return _frames; }
set { _frames = value; }
}
/// <summary>
    /// Gets or sets the interval between each frame in the animation.
    /// </summary>
    /// <value>The interval in miliseconds.</value>
    public int Interval
{
get { return _timer.Interval; }
set { _timer.Interval = value; }
}
    #endregion
    /// <summary>
    /// Starts the animation.
    /// </summary>
    public void Start()
{
if (null == this.TargetComponent)
{
throw new InvalidOperationException("Cannot start animating when ControlToAnimate is null");
}
if (null == this.Frames || this.Frames.Images.Count == 0)
{
throw new InvalidOperationException("Cannot start animating when Frames is null or holds zero images");
}
_timer.Start();
}
/// <summary>
    /// Stops the animation.
    /// </summary>
    public void Stop()
{
_timer.Stop();
}
}

Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Creating a Single Instance Application in C#

An entry about c# 3.0 | windows forms Publication date 24. March 2007 11:47

Sometimes, it's desirable to ensure that there is only ever one instance of your application running at any given time. Take Windows Live Messenger for instance - if you try to launch it whilst it is already running, it will just bring itself to the foreground instead.

Unfortunately, a lot of people try to recreate this behavior by simply checking if a process with the same name is currently running. As K. Scott Allen explains, this is not a good idea. The correct way to implement a single instance application, is to use a named mutex.

The word mutex is short for mutual exclusion, and is a synchronisation object that can only be owned by a single thread at any given time. Specifying a name for the mutex is optional - an unnamed mutex is scoped to the current process, while a named one is associated with an operating system object and can thus be used for interprocess synchronisation. Quite simply then, we can launch our application like this:

bool createdNew = true;
using (Mutex mutex = new Mutex(true, "MyApplicationName", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}

That ensures that there's only a single instance of our application running. Now, the above code just 'does nothing' if the application is already running - it would be nice if it instead tried to give the main window focus. To do this, we need to find the process instance, and then pinvoke the SetForeGroundWindow method of the Win32 API. Our final Main method then looks like this:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "MyApplicationName", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
else
        {
Process current = Process.GetCurrentProcess();
foreach (Process process in Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
SetForegroundWindow(process.MainWindowHandle);
break;
}
}
}
}
}

On a final note, I would urge you to read Raymond Chen's excellent post "A single-instance program is it's own denial of service", which talks about the security implications of implementing single-instance applications.

Currently rated 4.3 by 10 people

  • Currently 4.3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

NDC 2010

The conference to attend this summer happens June 16th-18th in Oslo, Norway. Are you going? Be sure to catch my talk on AOP while you're there!

 

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS