application and progress dialog in new thread - no worker thread

  • Thread starter Thread starter Alexander
  • Start date Start date
A

Alexander

Hi, I have written a program that takes on some operations much more
time than I expected. As I have seen users clicking wildly on the
screen to make something happen, I want to follow the microsoft
principles: always show them something ;)

First I made up a little status dialog containing a gif image to show
a little animation and a TextBox for a changing status message during
the work of the main program.
Just showing the dialog box (Form) at the start of the lengthy
operation does not work, as the dialog box is of course frozen because
it cannot work while the main message loop is stalled at the call of
the first method. Most solutions work with a non UI worker thread
which signals its status of the work progress to the UI thread. As my
main program is a UI program and all classes in there interact very
close it would be difficult for me to seperate a non UI thread for the
work. My idea was now to create a thread which shows the dialog and I
signal a new status message to the thread from my main UI thread
during the work, while disabling all inputs to the main window and
showing the status dialog topmost.

How would I do something like that?

I guess I just start the dialog by opening the dialog form with
Application.Run(new Statusdialog()) in the worker function of my
thread. But how do I access the dialog to signal new messages to it?
There is only one status dialog shown at a time so there is only one
instance of the dialog. I could create a static instance variable with
an access function in the dialog class to access the instance
following the singleton pattern. But from my experience with C++ this
is an unsafe way and I should use messages for the communication or
does C# have a synchronized keyword like Java to make such calls
thread safe?

Thanks for any help or suggestions,
Alexander
 
Alexander,

It would seem that you would have to do just as much work to create
another dialog in another thread, and marshal all the calls to that dialog
as you would if you separated the UI from the business logic from your
current implementation.

You might have a simpler option. While I don't advocate this, you could
call the static DoEvents method on the Application class, so that queued up
windows messages are processed, therefore making your UI more responsive.

Hope this helps.
 
Hi,

Most solutions work with a non UI worker thread
which signals its status of the work progress to the UI thread. As my
main program is a UI program and all classes in there interact very
close it would be difficult for me to seperate a non UI thread for the
work.

As in all win app. What is the nature of the operations you will perform?
I definely would go for the worker thread if possible. You can perform the
operations in that thread and notify the UI when done, then you can display
the results.

Let me know if you need some code for this.

Cheers,
 
Alexander,

you have to do something else:
Statusdialog dialog1 = new Statusdialog(); // Define this outside any
function because you will need it as a global variable.

to show the dialog:
dialog1.Show();

now you can change all properties of the dialog:
dialog1.Text = "Some text";

Now add the activate event to the main form, and add to the function for
that eventhandler:
dialog.Focus();

Vincent
 
I don't like it if people just take the answers and don't tell anybody
if it worked or not - so here is what I did.
I guess I followed the clean solution suggested by Nicholas. I had
only to cover two methods for closing the dialog and for passing
messages to it, so it was not too much nasty work ;)
The only thing I discovered with working with the dialog is that if
you are too fast in invoking functions on it, an exception is raised
because the dialog is not fully created yet, if at all. So as an
addition I passed an event flag to it, that is signaled if the dialog
is ready. I wait for that event on starting the tread (maybe I will
add a timeout here, because I don't want the application to stall just
because a little status box isn't opening).

So here is in a short version what I did:

----------------------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Threading;

namespace MyApplication
{
// declaration of delegates
public delegate void SetStatusMessageDelegate(string message);
public delegate void CloseDialogDelegate();

///////////////////////////////////
/// the status dialog
public class StatusDialog : System.Windows.Forms.Form
{
private System.ComponentModel.Container components = null;

private TextBox m_InfoText;

public SetStatusMessageDelegate m_SetStatusMessage;
public CloseDialogDelegate m_CloseDialog;
private System.Threading.ManualResetEvent m_Ready;

public StatusDialog(System.Threading.ManualResetEvent
dialogCreated)
{
this.m_SetStatusMessage = new
SetStatusMessageDelegate(SetStatusMessage);
this.m_CloseDialog = new CloseDialogDelegate(CloseDialog);
this.m_Ready = dialogCreated;
...
...
}

// signal that methods of the dialog can now be invoked
protected override void OnActivated(EventArgs e)
{
this.m_Ready.Set();
base.OnActivated (e);
}

private void SetStatusMessage(string message)
{
this.InfoText = message;
}

private void CloseDialog()
{
this.Close();
}

....
....
}



///////////////////////////////////
/// main app
public class MyForm : System.Windows.Forms.Form
{
private ManualResetEvent m_StatusDialogCreated;
private StatusDialog m_StatusDialog;
private System.Threading.Thread m_StatusDialogThread;

...
...

private void StatusDialogThread()
{
Application.Run(this.m_StatusDialog = new
StatusDialog(this.m_StatusDialogCreated));
}

public void OpenStatusDialog()
{
this.m_StatusDialogCreated = new ManualResetEvent(false);
this.m_StatusDialogThread = new Thread(new
ThreadStart(StatusDialogThread));
this.m_StatusDialogThread.Start();
// wait for the dialog to be ready
WaitHandle.WaitAll(new ManualResetEvent[]
{this.m_StatusDialogCreated});
}

public void SetStatusDialogMessage(string message)
{
if (this.m_StatusDialog!= null)
this.m_StatusDialog.BeginInvoke(this.m_StatusDialog.m_SetStatusMessage,
new Object[]{message});
}

public void CloseStatusDialog()
{
if (this.m_StatusDialog!= null)
this.m_StatusDialog.BeginInvoke(this.m_StatusDialog.m_CloseDialog,
null)
else // failsafe, dont want some half dead threads hanging
around - will work on this ;)
if (this.m_StatusDialogThread.ThreadState !=
ThreadState.Stopped) this.m_StatusDialogThread.Abort();

// activate this window
this.Activate();
}

...
}
}
----------------------------------------------------------------------------

That works for me...

Thanks for pointing me in the right direction,
Alexander
 
Back
Top