Display a wait message.

J

Joe

Hello,

I have a situation where I need to display a wait message while the UI
performs a good amount of configuration. I would like everything to happen
in the background and then show it all at once. While the UI is being
configured I want to display a message saying so.
The issue is that none of what has to be done can be done on a second thread
since it is all UI related.

The ideal would be to create a form on a second thread, configure everything
I need and then move it to the primary thread. Of course this cannot happen
but this is pretty much what I would want. Or, display a wait screen on a
second thread but I don't think this can happen either.

Any suggestions?

Thanks,
Joe
 
J

Joe

Hi Peter,

Actually all the work is UI manipulation. There are a lot of controls that
need to be created. This takes a while.

I'll look into the Thread.SetApartmentState().

Thanks,
Joe
 
J

Joe

I read over the SetApartmentState but it doesn't seem to help. Is there
anything else that can be done?
 
P

Pavel Minaev

Hi Peter,

Actually all the work is UI manipulation. There are a lot of controls that
need to be created. This takes a while.

I find this very doubtful. If you actually create so many controls
that it has a noticeable (seconds) delay, you are likely to run out of
HWND handles anyway (what was the limit, 64k?). Win32 API (and, by
extension, WinForms) simply isn't designed to handle thousands of
controls active at once. If you have a scrollable or expandable
surface of some sort, then you should really consider virtualizing the
controls and creating them as needed - after all, even if you create
several thousand textboxes, what would the user do with it?

Anyway, it would help if you elaborate on what are you using that many
controls for, and what exactly does the "UI manipulation" involve.

As for Peter's suggestion, I think you misunderstand him. It's not a
way to "move form from one thread to another" - you cannot do that.
What you can do, however, is:

1) Fork a new thread and make it STA.
2) Create a "Loading" form in it and display that.
3) Meanwhile on the Main thread, do your UI init.
4) Once UI init is done, send a message over to the "Loading" form to
close itself (Control.Invoke should help).
 
M

Morten Wennevik [C# MVP]

Joe said:
I read over the SetApartmentState but it doesn't seem to help. Is there
anything else that can be done?

Hi Joe,

This is fairly simple using a Splash type screen

in Program.cs Main

Splash.ShowSplash();

if (InitialCheck)
{
MyApplication myApp = new myApp();
myApp.Visible = false;
Application.Run(myApp);
}
else
{
Splash.CloseSplash();
}

Note that Splash exposes a singleton pattern which makes it possible for
MyApplication to call Splash.CloseSplash when it is good and ready to become
visible. This won't prevent lags in initial rendering though.

public static void ShowSplash()
{
if (_splash != null)
return;

_thread = new Thread(new ThreadStart(Splash.StartSplash));
_thread.IsBackground = true;
_thread.SetApartmentState(ApartmentState.STA);
_thread.Start();
}

public static void CloseSplash()
{
if (_splash != null && !_splash.IsDisposed)
{
_splash.Invoke(new MethodInvoker(_splash.Close));
}
_thread = null;
_splash = null;
}

Attach a timer to display a clock and expose a static method to show stages
as the real application progresses loading.
 
J

Joe

OK I get it now. I don't know why I just didn't think of a splash screen. We
already have one for our application we just need to change the message when
someone opens a file.

To answer your question about the controls - lets use chart for an example.
We have to create a chart themselves (which is quick). Next we need to
populate the charts. This means iterating through our data. This cannot be
done on a second thread. We also have other controls which get populated
with certain data and fields. Those controls cannot be populated on another
thread either.

I did look through all the code and tried to make the change before posting
anything but didn't have any luck.

Thanks for all the input everyone.
-Joe
 
J

Joe

I problem with this method is I cannot set the splash screen to the main
window so if the user minimizes the application the splash screen goes with
it.

Keep in mind this is not a startup splash screen. It is more of a progress
screen for when some one opens a file.

-Joe
 
P

Pavel Minaev

I problem with this method is I cannot set the splash screen to the main
window so if the user minimizes the application the splash screen goes with
it.

Keep in mind this is not a startup splash screen. It is more of a progress
screen for when some one opens a file.

Try hand-coding your progress dialog the way Peter described (and I
clarified), then.
 
P

Pavel Minaev

To answer your question about the controls - lets use chart for an example.
We have to create a chart themselves (which is quick). Next we need to
populate the charts. This means iterating through our data. This cannot be
done on a second thread.

That makes me wonder... iterating through a collection of several
thousand elements is, by itself, a very fast operation (you can
measure it yourself). Drawing the chart for that data is another
story, but it should still be fast enough for a quality
implementation. Are you sure you're simply "iterating through your
data" when drawing the chart, and it is not, for example, a deferred
LINQ query (or worse, LINQ to SQL), which actually runs as your chart
is drawn? Have you tried caching the data in memory before starting to
render the chart?

I'm also curious as to what chart control you're actually using. Maybe
that will be a clue. Even if not, I'll add it to my suspect list :)
 
J

Joe

Let's set aside trying to figure out what my application is doing for now.
Just take my word for it that this can not be done in another thread.

Try this:
Create a WinForms app with 2 forms. The main and a second one. The second
one will be the wait screen.
In the wait form create the ShowSplash and CloseSplash methods anyway you
want.
In the main form put a button - Let's call it Wait and Count.
In the OnClick for the button due the following:
System.Threading.Thread thread = new System.Threading.Thread(new
System.Threading.ThreadStart(FormWait.ShowForm));
thread.IsBackground = true;
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();

for (long i = 0; i < 10000000000; i++)
{

}

FormWait.CloseForm();

Now run the app and click the button. While the loop is being done minimize
the main form. You'll see that the wait form stays up and the main form
minimizes. I want them to stay in sync. I want the same behavior I would get
by doing FormWait.ShowDialog(MainForm);

I appoligize if I'm not getting something very obvious. Getting over worked
doesn't help with simple understanding sometimes.

Does this make sense?

Thanks,
Joe

I problem with this method is I cannot set the splash screen to the main
window so if the user minimizes the application the splash screen goes
with
it.

Keep in mind this is not a startup splash screen. It is more of a progress
screen for when some one opens a file.

Try hand-coding your progress dialog the way Peter described (and I
clarified), then.
 
Z

Zhi-Xin Ye [MSFT]

Hi Joe,

Thank you for using Microsoft Managed Newsgroup Service, I'm Zhi-Xin Ye,
it's my pleasure to work with you on this issue.

As I understand, you want to display a waiting message while the UI thread
is busying computing. However, it's not recommend to execute time-consuming
operations in the UI thread, as it will freeze the UI.

The recommended way is to use the BackgroundWorker component, you can
iterate through your data in the BackgroundWorker.DoWork event, and call
the BackgroundWorker.ReportProgress() method to report and send data to the
UI whenever neccessary. Once the BackgroundWorker.ReportProgress() method
is called, the BackgroundWorker.ProgressChanged event fires, then you can
update your UI (e.g. using the data passed from ReportProgress() method to
paint the chart) in the ProgressChanged event handler. Meanwhile, you can
display a label or a ProgressBar to show the progress.

A sample for your information:

public partial class Form1 : Form
{
BackgroundWorker worker;

public Form1()
{

InitializeComponent();

worker = new BackgroundWorker();

worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new
ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

//By default the progress reporting is disabled, we have to
enable it.
worker.WorkerReportsProgress = true;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
// do you data loading here,
// whenever you want to update the UI, call the ReportProgress
method
// and transfer the data you want need.
worker.ReportProgress(10, dataNeededInUI);
}

void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
this.Label1.Text = "Complete!";
}

void worker_ProgressChanged(object sender, ProgressChangedEventArgs
e)
{
// This event fires whenever you call theReportProgress method


// Update the UI here with the data sent from the
BackgroundWorker here.

//Display the progress
this.Label1.Text = "Please waiting....";
}

private void button1_Click(object sender, EventArgs e)
{
if (!worker.IsBusy)
worker.RunWorkerAsync();
}
}


For more information about the BackgroundWorker, you can refer to this
document:

BackgroundWorker Class
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundwork
er.aspx

If anything is unclear or you have any concerns, please feel free to let me
know, I will be happy of assistance.

Have a nice day!

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Z

Zhi-Xin Ye [MSFT]

Hi Joe,

How about this issue now? Do our suggestions make sense to you? If you
still need any help or have any concern, please feel free to feedback,
thanks.

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top