Tuesday, May 7, 2013

Working Progress Form



On this post I documented how to realize a working progress Window, with two progress bars. One bar for a potential main loop and another one for a secondary loop. For example, this could be useful if you need to import many file with many records, in this case the main bar represent the file and the second bar a current record. Another typical situation is when you have to compute an algorithm that require two nested loops etc..
This Window must be independent in order to show a progression but also to control the process, allowing to Cancel the current computation.
Above How I designed a Window.

In this example, I have omitted the control part, like Cancel or Stop management.
To be independent the Window must be a Thread that live a part, so define a Thread object inside the class.

public partial class WorkInProgress : Form
{
   public static Thread moThread = null;
   public static Form moOwner = null;
   public static WorkInProgress moForm = null;

The idea is to have a unique static Class so it should be easy to use it with proper static methods like:

static public void StartFormAsThread(Form oOwner)
{
    moOwner = oOwner;
    moThread = new Thread(new ThreadStart(ShowForm));
    moThread.Start();
}
static public void StopFormAsThread()
{
    if (moForm == null) return;
    moThread.Abort();
    moThread = null;
    moOwner.Invoke((MethodInvoker)delegate
    {
        moForm.Close();
        moForm = null;
        moOwner = null;
    });
}

You can see that no Form is initialized or showed directly with this method, this is because there is another method that is involved in this, see ShowForm(). When thread starts a ShowForm() is called.
The purpose of UpdateProgress() is to show a Window, and refresh the bar progression:

public static void UpdateProgress(ProgressArgs arg)
{
    if (moForm == null) return;
    try
    {
        moForm.pbFiles.Minimum = arg.FileMin;
        moForm.pbFiles.Maximum = arg.FileMax;
        moForm.pbFiles.Value = arg.FilePos;
        moForm.pbFiles.Refresh();

        moForm.pbRows.Minimum = arg.Min;
        moForm.pbRows.Maximum = arg.Max;
        moForm.pbRows.Value = arg.Pos;
        moForm.pbRows.Refresh();

        moForm.labInfos.Text = arg.FileName;
        moForm.labInfos.Refresh();
        moForm.BringToFront();
    }
    catch (Exception ex)
    {
        MessageBox.Show(arg.FilePos.ToString() + ", " + arg.Pos.ToString());
        // throw;
    }
}

static public void ShowForm()
{
    moForm = new WorkInProgress();
    //moForm.TopMost = true;
    moForm.ShowDialog();
}

static public void StopForm()
{
    if (moForm == null) return;
    moForm.Close();
    moForm = null;
    moOwner = null;
}


It Accept, as input, a ProgressArgs object with all information needed. Here a simple example how to use it:

private void btnThread_Click(object sender, EventArgs e)
{
    const int MAX_FILE = 5;
    const int MAX_REC = 3600;
    int iPerc = 0;
    WorkInProgress.StartFormAsThread(this);

    for (int j = 1; j <= MAX_FILE; j++)
    {
        for (int i = 1; i <= MAX_REC; i++)
        {
            iPerc = (int)((i/(float)MAX_REC ) * 100 );
            Debug.Print(string.Format("record: {0} ({1}%) file: {2}", i, iPerc, j));
            WorkInProgress.UpdateThreadProgress(new ProgressArgs(MAX_FILE, 0, j, 100, 0, iPerc, "file: " + j.ToString()));
        }
    }

    WorkInProgress.StopFormAsThread();
}


/// 
/// Argument class used to monitoring the computation progress 
/// 
public class ProgressArgs : System.EventArgs
{
    public int FileMax;
    public int FileMin;
    public int FilePos;
    public int Max;
    public int Min;
    public int Pos;
    public string FileName;

    public ProgressArgs()
    {
        Max = 0;
        Min = 0;
        Pos = 0;
        FileMax = 0;
        FileMin = 0;
        FilePos = 0;
        FileName = "";
    }

    public ProgressArgs(int filemax, int filemin, int filepos, int max, int min, int pos, string fileName)
    {
        Max = max;
        Min = min;
        Pos = pos;
        FileMax = filemax;
        FileMin = filemin;
        FilePos = filepos;
        FileName = fileName;
    }
}