Click here to Skip to main content
11,412,834 members (74,021 online)
Click here to Skip to main content

AJAX-style Asynchronous Progress Dialog for WinForms

, 2 Mar 2008 MPL
Rate this:
Please Sign up or sign in to vote.
A base class for adding a rich asynchronous progress animation to any Form.

asyncdialog-src

Introduction

My company develops a lot of rich-client applications, and we have forever wanted a nice "slick" way to indicate to the user when an activity is occurring in the background. Rarely do we know how long an operation will take (web services, remoting calls etc.), so we always use to just stick a little barber pole type animation in the top-right corner of the main application window. This was never a perfect solution though because we still had to do all the nasty "locking" of the Control/Form to make sure they couldn't queue up another action. Moreover, setting Enabled = False on many WinForms controls can look quite ugly and inconsistent, especially if the Form has got a variety of different controls.

In recent years, AJAX on the web has actually pioneered some interesting GUI concepts. I've always liked it when web sites pop-up with a central window that turns the background slightly darker and then ask you for some input. Then you type that input in and press an OK button, and then you get a nice little barber pole animation to indicate it has gone off back to the server and is waiting for a reply for the next step (if any).

That's basically what this project is about. Bringing that "cool" AJAX-style asynchronous indication behaviour to WinForms.

Background

The project consists of several fundamental concepts:

  • Capturing/snap-shooting the current appearance of a Form in a reliable and consistent way. Note: Control.DrawToBitmap() was not used because it has weird behaviour with some controls like RichTextBox.
  • Manipulating the captured bitmap to either blur or grayscale it in some way, in a similar way that most AJAX web sites do.
  • A barber pole type animation in the center of the Form. In this case, I used the excellent "Loading Circle" control by Martin Gagne - so thank you Martin for that Unsure | :~ )
  • From the outset, I ensured that whatever I developed would work on both normal Forms and MDI child's. This was crucial to me because many of our products use MDI user interfaces. Secondly, this ruled out the possibility of using Win2000-onwards composited layered translucent windows (which I experimented with initially).

Using the code

To use the base class, simply modify your Form to derive from my AsyncBaseDialog instead of the default System.Windows.Forms.Form. You then just call RunAsyncOperation() and pass in your delegate method as its parameter. This method handles all the nitty-gritty work of scheduling your work on a background thread.

Alternatively, if you want better control over things, then you can use BeginAsyncIndication() and EndAsyncIndication().

Internally, Begin/EndAsyncIndication() use a reference count so that you can call them multiple times in a stack-like fashion and still get the expected behaviour.

public partial class MyForm : AsyncBaseDialog {

   public ModalDlg() {
      InitializeComponent();
   }

   private void button1_Click(object sender, EventArgs e) {
      AsyncProcessDelegate d = delegate() {
         //
         // Do your long-duration work here
         // and remove the placeholder Sleep() below
         //
         System.Threading.Thread.Sleep(3000);
      };

      RunAsyncOperation(d);
   }

   private void button2_Click(object sender, EventArgs e) {
      //
      // Alternatively if you don't want to use the RunAsyncOperation() wrapper...
      // You can use BeginAsyncIndication() and EndAsyncIndication() explicitly.
      //
      BeginAsyncIndication();
   }

}//class

How it works

The Form is snapshot by opening up its DC (device context) and then copying its contents to a Bitmap. This bitmap is then manipulated using Martin Gagne's methods to grayscale it.

The snapshot of the Form was probably the hardest bit as I haven't done Win32 API for years! By the way, if you are have having teething issues with Control.DrawToBitmap(), then I recommend you look at this. Here it is:

//
// Get DC of the form...
IntPtr srcDc = GetDC(this.Handle);

//
// Create bitmap to store image of form...
Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);

//
// Create a GDI+ context from the created bitmap...
using (Graphics g = Graphics.FromImage(bmp)) {
   //
   // Copy image of form into bitmap...
   IntPtr bmpDc = g.GetHdc();
   BitBlt(bmpDc, 0, 0, bmp.Width, bmp.Height, srcDc, 0, 0, 0x00CC0020 /* SRCCOPY */);

   //
   // Release resources...
   ReleaseDC(this.Handle, srcDc);
   g.ReleaseHdc(bmpDc);

   //
   // Blur/grayscale it...
   Grayscale(bmp);

   //
   // Apply translucent overlay... fillBrush has an alpha-channel.
   g.FillRectangle(fillBrush, 0, 0, bmp.Width, bmp.Height);
}//using

There were a couple issues I had surrounding the user resizing, maximising, minimising, restoring, or double-clicking the title bar of the Form whilst the async. indication was active. Basically, these were redraw issues - particularly on pre-Vista Aero Glass machines. After weighing up possible solutions, I decided that the chances of a user wanting to resize/min/maximise the Form whilst the async. indication was active was pretty small and the annoyance to them would probably be very small. Therefore, I wrote some WndProc filters, as below:

protected override void WndProc(ref Message m) {
   if (IsAsyncBusy) {
      if (m.Msg == 0x112 /* WM_SYSCOMMAND */) {
         int w = m.WParam.ToInt32();

         if (w == 0xf120 /* SC_RESTORE */ || w == 0xf030 
                         /* SC_MAXIMIZE */ || w == 0xf020 
                         /* SC_MINIMIZE */)
            return; // short circuit

      } else if (m.Msg == 0xa3 /* WM_NCLBUTTONDBLCLK */)
         return; // short circuit
   }

   base.WndProc(ref m);
}

Thank you

Thanks for reading and I hope you like the control.

If you make any modifications/bug fixes/enhancements to this control, please post in the comments section with your source snippets and/or ideas.

History

  • 02/March/2008 - initial release.

License

This article, along with any associated source code and files, is licensed under The Mozilla Public License 1.1 (MPL 1.1)

Share

About the Author

Nathan Evans
Software Developer (Senior)
United Kingdom United Kingdom
I am the lead developer of numerous .NET-based networking and communication server systems for Windows, for a company based in Cambridge. Including SMS/SMPP, VOIP and VoiceXML technologies.

Comments and Discussions

 
GeneralRe: Cross Thread operation Pin
zosopat at 24-Jun-10 22:45
memberzosopat24-Jun-10 22:45 
GeneralEvents not called Pin
Holms at 24-Jul-09 21:31
memberHolms24-Jul-09 21:31 
GeneralRe: Events not called Pin
Member 4020713 at 2-Dec-09 0:21
memberMember 40207132-Dec-09 0:21 
GeneralRe: Events not called Pin
Member 8463192 at 5-Dec-11 23:12
memberMember 84631925-Dec-11 23:12 
Generalapply to user control Pin
aldo hexosa at 14-Apr-09 18:19
memberaldo hexosa14-Apr-09 18:19 
GeneralSmall change in RunAsyncOperation method Pin
CozyRoc at 8-Apr-09 17:57
memberCozyRoc8-Apr-09 17:57 
GeneralRe: Small change in RunAsyncOperation method Pin
Member 3805378 at 7-Apr-11 9:16
memberMember 38053787-Apr-11 9:16 
Questionhow can avoid unsafe code ??? Pin
alhambra-eidos at 23-Feb-09 22:12
memberalhambra-eidos23-Feb-09 22:12 
AnswerRe: how can avoid unsafe code ??? Pin
webxet at 7-Oct-11 6:29
memberwebxet7-Oct-11 6:29 
Questionany updates ? Pin
alhambra-eidos at 23-Feb-09 4:23
memberalhambra-eidos23-Feb-09 4:23 
QuestionCan you place a cancel button ? Pin
Pankajkumar Nikam at 19-Jan-09 3:50
memberPankajkumar Nikam19-Jan-09 3:50 
GeneralIt's very very good, but it can not cover the toolstrip Pin
javasleepless at 26-Aug-08 19:12
memberjavasleepless26-Aug-08 19:12 
GeneralRe: It's very very good, but it can not cover the toolstrip Pin
javasleepless at 26-Aug-08 19:25
memberjavasleepless26-Aug-08 19:25 
General[Message Removed] Pin
nompel at 20-Sep-08 15:54
membernompel20-Sep-08 15:54 
QuestionVS 2005? Pin
C#GIS at 9-Jul-08 12:50
memberC#GIS9-Jul-08 12:50 
AnswerRe: VS 2005? Pin
sonny_z at 26-Aug-08 11:36
membersonny_z26-Aug-08 11:36 
GeneralOne rectification is required for this control Pin
Tridip Bhattacharjee at 15-Jun-08 22:35
memberTridip Bhattacharjee15-Jun-08 22:35 
GeneralForm with Dock = DockStyle.Fill Pin
Martin456 at 28-May-08 7:03
memberMartin45628-May-08 7:03 
QuestionIs the updated version available yet? Pin
dotnet_spinner at 20-May-08 20:16
memberdotnet_spinner20-May-08 20:16 
GeneralQuestion on callbacks Pin
taherscherzay at 28-Mar-08 17:56
membertaherscherzay28-Mar-08 17:56 
GeneralVery nice work Pin
Avner Raz at 14-Mar-08 7:03
memberAvner Raz14-Mar-08 7:03 
GeneralRe: Very nice work Pin
Avner Raz at 14-Mar-08 22:26
memberAvner Raz14-Mar-08 22:26 
GeneralRe: Very nice work Pin
AhsanS at 6-May-09 19:32
memberAhsanS6-May-09 19:32 
Generalvery nice Pin
sprague295 at 10-Mar-08 12:57
membersprague29510-Mar-08 12:57 
GeneralOh my God...... [modified] Pin
Ice_LS00 at 10-Mar-08 7:37
memberIce_LS0010-Mar-08 7:37 
GeneralVery good! Pin
MaxGuernsey at 4-Mar-08 2:55
memberMaxGuernsey4-Mar-08 2:55 
GeneralRe: Very good! Pin
Nathan Evans at 5-Mar-08 9:30
memberNathan Evans5-Mar-08 9:30 
GeneralRe: Very good! Pin
MaxGuernsey at 5-Mar-08 9:40
memberMaxGuernsey5-Mar-08 9:40 
Generalnice, but... Pin
hth2000 at 2-Mar-08 7:10
memberhth20002-Mar-08 7:10 
GeneralRe: nice, but... Pin
Nathan Evans at 2-Mar-08 7:46
memberNathan Evans2-Mar-08 7:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150427.1 | Last Updated 2 Mar 2008
Article Copyright 2008 by Nathan Evans
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid