views:

1263

answers:

3

Please have a look at the following code:

var splashForm = new SplashForm();
m_Thread = new Thread( () => System.Windows.Forms.Application.Run( splashForm ) )
m_Thread.Start();

// Do some initialization
// ...

// the following method just invokes `Close()` on the right thread
splashForm.Shutdown();

// Loop until the thread is no longer alive
// ...

System.Windows.Forms.Application.Run( mainForm );

It looks as if all works fine: first I see the splashscreen, later the mainform gets started. But somehow I get strange errors, like: graphical elements (a endless ProgressBar) are not showing up correctly.
EDIT: I have two progressbars, one on the splashscreen, on on the mainform. They both show the same (wrong) behaviour in endlessmode: no progress, just the pure background./EDIT
In my opinion this is due to the call of Application.Run() on different threads. This errors can be eliminated by calling any function/property of the mainForm before starting the splashscreen - like, for instance

mainForm.Text = mainForm.Text;

Can anyone please confirm that this code can cause problems - or that it should behave alright and I have to look for the error somewhere else?
I already looked for splashscreen implementations and I know that it can be done differently. But I am interested in understanding this implementation and its possible problems. Thanks!

+2  A: 

I would create the form on the thread where the Application.Run() executes.

SplashForm splashForm = null;
m_Thread = new Thread(delegate { splashForm = new SplashForm(); System.Windows.Forms.Application.Run(splashForm); });
m_Thread.Start();

But what really needs to be done is access it via the InvokeRequired and BeginInvoke technique. Check here.

kek444
No, I thought so too but I think the tread-affinity is set by the Run method.
Henk Holterman
Well, that's only half of it. The main part is actually the InvokeRequired part as the linked article suggests.
kek444
Yes, for any communication between the 2 threads - as always.
Henk Holterman
I use RevokeInquired already ... that is the reason why I have the Shutdown() - it invokes just the Close()-method.
tanascius
+2  A: 

The thread on which your SplashForm displays needs to have a Windows message pump in order to process messages that each window/control produces consumes. To do that you need to make the thread an STA thread. Try calling SetApartmentState before starting the thread

Kevin Jones
I am running a SplashForm on a MTA thread just fine. Do you have any references about MessageLoop and STA/MTA requirements?
Henk Holterman
I set the ApartmentState by using m_Thread.TrySetApartmentState( ApartmentState.STA ); Unfortunately it does not help.
tanascius
This is the best I could find with a short search: http://blogs.msdn.com/jfoscoding/archive/2005/04/07/406341.aspx
Kevin Jones
+1, Probably a good point. I wasn't running anything COM related. Might try though.
Henk Holterman
A: 

OMG, I found the answer:
Application.EnableVisualStyles(); was called in the ctor if my mainForm (WHY?). It has to be called before any controls are created. Moving it to the static Main() did the trick. The visual styles are required for endless (ProgressBarStyle.Marquee) progressbars.
Now this splashscreen solution works as it should.

tanascius
this might have worked for you. But, I would have done it a bit differently. I assume you want to show the splashscreen only during loading. Then why create a message pump for the thread on which splash screen is created. Instead just call it on a separate thread by calling Show and when you are done, just call close. Now, create message pump by calling Application.Run(mainForm)
P.K
Yes, you might be right. The additonal call to Application.Run() can be probably skipped, I'll check this.
tanascius