Thread: Japo's code
View Single Post
Old 26-09-2010, 02:47 PM   #4
Japo
Autonomous human
 
Japo's Avatar


 
Join Date: Mar 2006
Location: ,
Posts: 4,613
Default xThreading 3

And then I wanted another thing--actually this was the first one I wanted to make, and what got me into authoring the rest, that this is built onto. I wanted a splash window to show while a parallel task is carried out in the background, but I wanted the background task to be able to show its status in the splash screen.

Here it is. The constructor takes a delegate to the action you want to perform in parallel, and just creating the splash window starts the task immediately. You won't even need to keep a reference to the window most times, as it closes itself down when the task is done; so even this minimal statement works:
Code:
new xSplash(method);
without assigning a reference to the created method to any variable. The "method" has to match the Action<xLabel> delegate.

The class doesn't provide for returning values from the Task (an System.Action<> returns void), passing parameters to it, or cancelling it from the main thread, simply because I haven't needed it; although it wouldn't be hard to add that.

By the way, if a parallel thread throws an exception, it doesn't reach the main thread immediately. But with xSplash, every time you click on it, it will check the task for exceptions, and if there's any it will be thrown back into the main thread; if there's none, nothing will happen. (I guess if the user sees that the task is taking too long and the splash window remains there forever without reporting any change of status, he will instinctively click on it.)

Here's the code:
Code:
using System.Windows.Forms;
using System.Threading.Tasks;

namespace SEADM.xThreading
{
   public class xSplash:Form
   {
      Task guest;

      Label lbl;

      public xSplash(Action<xLabel> method)
      {
         InitializeComponent();
         var frmSplash = new xForm(this);
         var lblSplash = new xLabel(lbl);
         guest = Task.Factory.StartNew( () =>
         {
            method(lblSplash);
            frmSplash.Close();
         } );
         MouseDown += frm_Click;
         lbl.MouseDown += frm_Click;
         Show();
      }

      void frm_Click(object sender, MouseEventArgs e)
      {
         if(guest.IsFaulted) throw guest.Exception;
         // don't use Task.Wait() instead, in case
         // the splash is clicked before guest is through.
      }
   }
}
Notice that the delegate taken by the constructor requires one argument, and the argument to that delegate will be provided by xSplash, it's actually a xLabel to its own Label, which will display the status of the task. At every point of the function pointed by the delegate, that you want to report the status, you simply assign the Text property of the xSplash parameter.

There's actually more code of course (InitializeComponent() etc.), what was written automatically with the Visual Studio designer, including non default values for some properties, to make the splash window look the way I wanted--even though it's really nothing fancy and has no graphics etc. I used dynamic layout (everyone should), so the label and the window resize automatically according to the text, depending of the system font size etc.

Also you shouldn't let the user close the window, in principle, otherwise when the task is finished and it tries to close it again, you'll get an exception--and without the splash you won't have any way to retrieve it.
__________________
Life starts every day anew. Prospects not so good...

Last edited by Japo; 05-01-2011 at 06:34 PM.
Japo is offline                         Send a private message to Japo
Reply With Quote