Click here to Skip to main content
16,017,207 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
Hello all,

I am trying to wrap a non-interactive maintenance program with a GUI. The purpose of the GUI is to provide the user with information about the status of the maintenance activity (Is it still running? Did the task succeed? Etc). Optimally, I'd like it to work like this:

1) The GUI appears containing only a label (text is something like "Running. Please stand by".)
2) The maintenance code is executed.
3) Based on the result of the maintenance code, the label text on the GUI changes (indicating success or failure).

The problem I have run into is that the GUI will not appear until I've left the form constructor context. If I place the maintenance code directly in the constructor or call it from the constructor in a separate method, the GUI will not finally appear on the display until the constructor has terminated. As a result, the user does not see the GUI at all until the maintenance code has finished.

So, to sum up, what I need to do is to be able to trigger an event after the GUI has been constructed without the user having to click on any controls. It seems like this should be relatively simple to do, but I am really stumped. Any help would be greatly appreciated.
Posted
Comments
[no name] 12-Jul-12 12:57pm    
Since you have already asked this, why do you think that you need to trigger form events yourself? Events are triggered all the time without user interaction. Simple asnswer is to take your "dostuff" code out of the constructor, define a form_load event and paste your dostuff code there...

You should not place such activity in the constructor. You break the lifecycle of the forms application.
Make a worker[^] (youtube sample[^]) from the activity itself.
Let the framework create and display the form, and than start the worker from the Load[^] event.
As your form and your worker will be different threads, you can not simply modify the form from the worker, but it is really not complicated. See this article: ProgressForm: A simple form linked to a BackgroundWorker[^]. Actually you can use the ProgressChanged[^] event handler to update the Form directly, or use the Control.invoke[^] method to call a delegate.
 
Share this answer
 
v3
Comments
CertNerd 12-Jul-12 14:48pm    
Thanks, I wasn't familiar with workers. The question still remains, however, where do I call RunWorkerAsync() if I don't want the user to have to click anything in order for the maintenance task to take place?
Zoltán Zörgő 12-Jul-12 14:55pm    
As I mentioned, call it in the form's Load event handler. Load is called when the form is ready, but not yet shown. As worker is on separate thread, showing the form will proceed. You also can use the Shown event. It depends on when you want to make the first modification on the form.
Do you mean something like Application.DoEvents? If it's a very simple application you should be okay though note that the effects of DoEvents can be unpredictable. Here is a discussion with some alternatives: Application.DoEvents alternative[^].
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 12-Jul-12 13:32pm    
Well, it is predictable in certain situations and can be used to change the order of handling of some events; such situations are very rare, and it never should be used to "simulate" threads -- it would make a disaster. My 5.
--SA
This is actually not as easy as one would think it is.
Windows Forms starts UI processing and then continues processing on that thread.
If you try and call a UI function, say Paint, on the same thread, you will need to wait until the previous process on that thread completes.

Try using BeginInvoke() to force the UI label to paint with the status information.
What's up with BeginInvoke?[^] neatly explains the reasoning behing BeginInvoke().

Here are a couple of other links that should set you up.
https://jamesrossiter.wordpress.com/2008/08/22/updating-gui-from-other-threads-in-c/[^]
http://kristofverbiest.blogspot.in/2007/02/avoid-invoke-prefer-begininvoke.html[^]
 
Share this answer
 
v2
This can easily be acheived using a BackgroundWorker and overriding a suitable event on the form to start it.
This sketch code should give you enough to sort it out:
C#
using System;
using System.ComponentModel;
using System.Windows.Forms;

public partial class FormMain : Form
{
    private BackgroundWorker backgroundWorker;
    // private YourResultType result;

    public FormMain()
    {
        InitializeComponent();
        backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += DoWork;
        backgroundWorker.RunWorkerCompleted += RunWorkerCompleted;
    }

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);
        backgroundWorker.RunWorkerAsync();
    }
    private void DoWork(object sender, DoWorkEventArgs e)
    {
        // Run maintainance code here and save results to local field
        // result = yourResult;
    }
    private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // update GUI here from local field as on the UI thread
        // label.Text = yourResult.Text;
        // restart worker if needed
    }
}
 
Share this answer
 
Ok folks, I got it. A background worker did the trick, but it took some Googling to actually get it working. Here's what I did:

namespace MyNameSpace
{
    public partial class Form1 : Form
    {
        BackgroundWorker bw = new BackgroundWorker();

        public Form1()
        {
            InitializeComponent();
            this.Show();

            //Don't need to support cancellation or monitor progress.
            bw.WorkerSupportsCancellation = false;
            bw.WorkerReportsProgress = false;
        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
                //Maintenance task code goes here.
        }

        //Not knowing about the Shown event was a huge 
        //missing piece of the puzzle for me.
        private void Form1_Shown(Object sender, EventArgs e)
        {
            //If the following 2 lines aren't present, DoWork 
            //simply won't execute.  This had me stumped for a while.
            //This article helped me a lot: http://tinyurl.com/878nf4x
            this.bw.DoWork += bw_DoWork;
            this.bw.RunWorkerCompleted += bw_RunWorkerCompleted;

            bw.RunWorkerAsync();
        }

        private void bw_RunWorkerCompleted(object sender, _
                                           RunWorkerCompletedEventArgs e)
        {
            //I didn't bother to learn how to use the 'Result' attribute.
            //Looking for this indicator file works fine for me since I have to 
            //lay it down anyway.
            if (File.Exists(successIndicatorFile))
                success();
            else
                failure();
        }


Thanks to everyone who advised me.
 
Share this answer
 
Comments
Zoltán Zörgő 12-Jul-12 16:17pm    
Good for you! Actually the += rows are better placed in the constructor, but this is only a design guide.
You should honor all that really helped you by accepting their answers.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900