Splash Screen waiting until thread finishes - c#

I still have a problem with the splash screen. I don't want to use the property SC.TopMost=true.
Now my application scenario is as follows:
in progeram.cs:
[STAThread]
static void Main()
{
new SplashScreen(_tempAL);// where _tempAL is an arrayList
Application.Run(new Form1(_tempAL));
}
in SplashScreen class:
public SplashScreen(ArrayList _Data)
{
DisplaySplash()
}
private void DisplaySplash()
{
this.Show();
this.TopMost = true;
this.CenterToScreen();
this.SetTopLevel(true);
_allServerNarrators = new string[10];
for (int i = 0; i < _allServerNarrators.Length; i++)
_allServerNarrators[i] = null;
GetFromServer();
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
}
private void GetFromServer()
{
_serverNarrators = new ArrayList();
string _file = "Suras.serverNar";
if (!Directory.Exists("c:\\ASGAQuraan"))
Directory.CreateDirectory("c:\\ASGAQuraan");
while (counter < 4 && _serverFiles == null)
{
if (Download("c:\\ASGAQuraan", _ftpServerIP, _file))
{
StreamReader _strReader = new StreamReader
("c:\\ASGAQuraan\\"+_file,System.Text.Encoding.Default);
string _line = _strReader.ReadLine();
string _word;
while (true)
{
while (_line != null)
{
_word = _line.Substring(0, _line.IndexOf("*"));
int _narId = Convert.ToInt32(_word);
_line = _line.Substring(2);
int k = 0;
_serverNarratorNode = new ArrayList();
while (true)
{
int ind = _line.IndexOf("*");
if (ind > 0 && ind < _line.Length)
{
string str = _line.Substring(0, (ind));
if (k == 0)
{
_allServerNarrators[_narId] = str;
_serverNarratorNode.Add(str);
}
else
{
_serverNarratorNode.Add(str);
}
_line = _line.Substring(ind + 1);
k++;
}
else
{
_line = null;
break;
}
}
_serverNarrators.Add(_serverNarratorNode);
_serverFiles = "added";
}
_line = _strReader.ReadLine();
if (_line == null)
{
break;
}
}
}
else
counter++;
}
}
What I want is something in the splash screen class which waits until the thread finishes.
For more details, please tell me what I need to tell you.

Same question, same answer:
The .NET framework has excellent built-in support for splash screens. Start a new WF project, Project + Add Reference, select Microsoft.VisualBasic. Add a new form, call it frmSplash. Open Project.cs and make it look like this:
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
}
class MyApp : WindowsFormsApplicationBase {
protected override void OnCreateSplashScreen() {
this.SplashScreen = new frmSplash();
}
protected override void OnCreateMainForm() {
// Do your time consuming stuff here...
//...
System.Threading.Thread.Sleep(3000);
// Then create the main form, the splash screen will close automatically
this.MainForm = new Form1();
}
}
}

You've entered dangerous territory by creating UI prior to your call to Application.Run(). Application.Run is essentially your program's message pump. By displaying the UI before you start the application's message pump, you make typical UI interaction effectively impossible on the premature UI. For a splash screen this may not seem relevant, but it will matter if (e.g.) there's a request to make the splash screen disappear if it's clicked on, or you want to use a BackgroundWorker.
These can be worked around by creating a message pump in your splash screen (by making it modal via a call to ShowDialog() instead of Show()), but that's treating the symptom when treating the problem really isn't that difficult.
I'd strongly encourage nobugz's answer in this case. The framework provides the support you need. While features in the Microsoft.VisualBasic namespace aren't always very discoverable to C# programmers, they can be a real timesaver and lifesaver for cases like this.
Good luck!

Following across 2 threads is a bit confusing, but I'm going to take a stab and say this...
I don't fully understand your design here, but if the issue is that when you launch a second application the splash screen form turns white... It's most likely due to the fact that splash screen is busy executing all of that code in GetFromServer(). So busy that it has no time to re-paint itself.
To remedy this problem I would suggest that you use the BackGroundWorker component to execute the GetFromServer method. This will run that method in a separate thread and leave the form's thread free to re-paint itself.

Unfortunately I don't have enough reputation to comment on someones answer yet. :( This is meant to be the answer to Colonel Panics comment on Hans Passants answer.
His problem was that a MessageBox shown from new FormMain(args)will be shown behind the splash screen. The key is to invoke the MessageBox from the thread the splash screen runs in:
splashScreen.Invoke(new Action(() => {
MessageBox.Show(splashScreen, "the message");
}));
Where splashScreen is a reference to the splash screen object that has been created in OnCreateSplashScreen and obviously has to be given to the new Form1 object.

You really should give more details about your problem. I could be completely wrong, but I'm going to take a shot in the dark. From what I'm imagining is going on and you want, you want the splash screen to show, do some processing in another thread, then the splash screen to go away when finished.
To do this, you're going to want to move the GetFromServer() call to a BackgroundWorker. Then move the
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
code to the BackgroundWorker_RunWorkerCompleted event handler.
To use the BackgroundWorker:
1) Initialize the BackGroundWorker
BackgroundWorker myWorker = new BackgroundWorker();
2) Add event handlers
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
//put the work you want done in this one
myWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
//this gets fired when the work is finished
3) Add code to the event handlers.
4) Call myWorker.RunWorkerAsync() to start working.
As a separate note, you don't seem to be doing anything with the ArrayList that you're passing to the splash screen's constructor. Is this intended?

Related

C# Splash Screen Thread freeze [duplicate]

I still have a problem with the splash screen. I don't want to use the property SC.TopMost=true.
Now my application scenario is as follows:
in progeram.cs:
[STAThread]
static void Main()
{
new SplashScreen(_tempAL);// where _tempAL is an arrayList
Application.Run(new Form1(_tempAL));
}
in SplashScreen class:
public SplashScreen(ArrayList _Data)
{
DisplaySplash()
}
private void DisplaySplash()
{
this.Show();
this.TopMost = true;
this.CenterToScreen();
this.SetTopLevel(true);
_allServerNarrators = new string[10];
for (int i = 0; i < _allServerNarrators.Length; i++)
_allServerNarrators[i] = null;
GetFromServer();
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
}
private void GetFromServer()
{
_serverNarrators = new ArrayList();
string _file = "Suras.serverNar";
if (!Directory.Exists("c:\\ASGAQuraan"))
Directory.CreateDirectory("c:\\ASGAQuraan");
while (counter < 4 && _serverFiles == null)
{
if (Download("c:\\ASGAQuraan", _ftpServerIP, _file))
{
StreamReader _strReader = new StreamReader
("c:\\ASGAQuraan\\"+_file,System.Text.Encoding.Default);
string _line = _strReader.ReadLine();
string _word;
while (true)
{
while (_line != null)
{
_word = _line.Substring(0, _line.IndexOf("*"));
int _narId = Convert.ToInt32(_word);
_line = _line.Substring(2);
int k = 0;
_serverNarratorNode = new ArrayList();
while (true)
{
int ind = _line.IndexOf("*");
if (ind > 0 && ind < _line.Length)
{
string str = _line.Substring(0, (ind));
if (k == 0)
{
_allServerNarrators[_narId] = str;
_serverNarratorNode.Add(str);
}
else
{
_serverNarratorNode.Add(str);
}
_line = _line.Substring(ind + 1);
k++;
}
else
{
_line = null;
break;
}
}
_serverNarrators.Add(_serverNarratorNode);
_serverFiles = "added";
}
_line = _strReader.ReadLine();
if (_line == null)
{
break;
}
}
}
else
counter++;
}
}
What I want is something in the splash screen class which waits until the thread finishes.
For more details, please tell me what I need to tell you.
Same question, same answer:
The .NET framework has excellent built-in support for splash screens. Start a new WF project, Project + Add Reference, select Microsoft.VisualBasic. Add a new form, call it frmSplash. Open Project.cs and make it look like this:
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
}
class MyApp : WindowsFormsApplicationBase {
protected override void OnCreateSplashScreen() {
this.SplashScreen = new frmSplash();
}
protected override void OnCreateMainForm() {
// Do your time consuming stuff here...
//...
System.Threading.Thread.Sleep(3000);
// Then create the main form, the splash screen will close automatically
this.MainForm = new Form1();
}
}
}
You've entered dangerous territory by creating UI prior to your call to Application.Run(). Application.Run is essentially your program's message pump. By displaying the UI before you start the application's message pump, you make typical UI interaction effectively impossible on the premature UI. For a splash screen this may not seem relevant, but it will matter if (e.g.) there's a request to make the splash screen disappear if it's clicked on, or you want to use a BackgroundWorker.
These can be worked around by creating a message pump in your splash screen (by making it modal via a call to ShowDialog() instead of Show()), but that's treating the symptom when treating the problem really isn't that difficult.
I'd strongly encourage nobugz's answer in this case. The framework provides the support you need. While features in the Microsoft.VisualBasic namespace aren't always very discoverable to C# programmers, they can be a real timesaver and lifesaver for cases like this.
Good luck!
Following across 2 threads is a bit confusing, but I'm going to take a stab and say this...
I don't fully understand your design here, but if the issue is that when you launch a second application the splash screen form turns white... It's most likely due to the fact that splash screen is busy executing all of that code in GetFromServer(). So busy that it has no time to re-paint itself.
To remedy this problem I would suggest that you use the BackGroundWorker component to execute the GetFromServer method. This will run that method in a separate thread and leave the form's thread free to re-paint itself.
Unfortunately I don't have enough reputation to comment on someones answer yet. :( This is meant to be the answer to Colonel Panics comment on Hans Passants answer.
His problem was that a MessageBox shown from new FormMain(args)will be shown behind the splash screen. The key is to invoke the MessageBox from the thread the splash screen runs in:
splashScreen.Invoke(new Action(() => {
MessageBox.Show(splashScreen, "the message");
}));
Where splashScreen is a reference to the splash screen object that has been created in OnCreateSplashScreen and obviously has to be given to the new Form1 object.
You really should give more details about your problem. I could be completely wrong, but I'm going to take a shot in the dark. From what I'm imagining is going on and you want, you want the splash screen to show, do some processing in another thread, then the splash screen to go away when finished.
To do this, you're going to want to move the GetFromServer() call to a BackgroundWorker. Then move the
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
code to the BackgroundWorker_RunWorkerCompleted event handler.
To use the BackgroundWorker:
1) Initialize the BackGroundWorker
BackgroundWorker myWorker = new BackgroundWorker();
2) Add event handlers
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
//put the work you want done in this one
myWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
//this gets fired when the work is finished
3) Add code to the event handlers.
4) Call myWorker.RunWorkerAsync() to start working.
As a separate note, you don't seem to be doing anything with the ArrayList that you're passing to the splash screen's constructor. Is this intended?

Managing the dialog form in C#

On click of a button I have this code with is should show a dialog on top of the current form and display text, wait for one second, change the text and then finally close it:
Form p = new Form();
p.ShowDialog();
p.Text = "Start.";
Thread.Sleep(1000);
p.Text = "Counting.";
Thread.Sleep(1000);
p.Text = "End.";
Thread.Sleep(1000);
p.Close();
However once it executes p.ShowDialog(); it stops the code until the form p is closed and it doesn't work as I intended it to. Can I get some guidance on this? Not necessarily the solution, but at least maybe some keywords I could google on?
UPDATE: due to the difficulties I am facing trying to access business logic, which is irrelevant to the problem, I am delaying providing the working example. Stay tuned and sorry :)
SOLUTION: what I did is in fact used Show() instead of ShowDialog(). Since i was impossible to access form from business logic, BackgroundWorker came in handy and was being used between them. I cannot share any code or the layout of the project structure, but in conclusion, the accepted answer's main statement was the key to the solution :)
That is the point of ShowDialog(). It creates a modal form and does not return control to the calling function until you are done. If it doesn't need to be modal, then use .Show(). If it does need to be modal, then put code in the Form Load method to update the text as needed.
http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx
taken from the link above:
When this method is called, the code following it is not executed until after the dialog box is closed.
if you want to form to display whatever it is you want to display you should write the code inside the the form itself, do that in an eventhandler of the form show event.
As you have found, ShowDialog is a blocking method that does not return until the dialog is closed. Your code to change the text and handle the delay needs to be within the dialogue itself.
However, it's worth noting the next problem that you'll find: if you call Thread.Sleep(1000) from the UI thread, your application will become unresponsive for 1 second at a time. This is probably not what you're aiming for! I'd suggest you look into the Timer or BackgroundWorker classes to handle this more smoothly.
Check this out:
public partial class Form2 : Form
{
delegate void SetTextCallback(string text);
delegate void CloseFormCallback();
public Form2()
{
InitializeComponent();
new Thread(DoMagic).Start();
}
public void DoMagic()
{
this.SetText("Start.");
Thread.Sleep(1000);
this.SetText("Counting.");
Thread.Sleep(1000);
this.SetText("End");
Thread.Sleep(1000);
this.CloseForm();
}
private void CloseForm()
{
if (this.InvokeRequired)
{
CloseFormCallback c = new CloseFormCallback(CloseForm);
this.Invoke(c);
}
else
{
this.Close();
}
}
private void SetText(string text)
{
if (this.label1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.label1.Text = text;
}
}
}

C# NofityIcon balloon tip doesn't always go away within specified time

I use a NotifyIcon in a rather simple fashion.
public class Popup
{
...
private static NotifyIcon ni;
static Popup()
{
ni = new NotifyIcon();
ni.Icon = SystemIcons.Information;
}
public Popup(string nexusKey)
{
...
}
public void make(string text)
{
try
{
...
}
catch
{
ni.Visible = true;
ni.ShowBalloonTip(1000, "Thats the title", text, ToolTipIcon.Info);
}
}
}
Problem is, it seems like the "stay alive" timer doesn't get started if I am focusing different windows than the one hosting the process that display the balloon. Any ideas on how to make sure the balloon goes away after 1 second no matter what ?
Part of the reason for this behaviour is that the timer used in ShowBalloonToolTip was designed to only run when the OS detects user input. Thus if you are just waiting for the balloon to disappear and not actually doing anything then it will never timeout.
I believe that the reasoning was that if you left your PC and came back an hour later then you wouldn't miss any notifications.
There is a way around it, and that is to run a separate timer that toggles the icon visibility.
For example:
private void ShowBalloonWindow(int timeout)
{
if (timeout <= 0)
return;
int timeoutCount = 0;
trayIcon.ShowBalloonTip(timeout);
while (timeoutCount < timeout)
{
Thread.Sleep(1);
timeoutCount++;
}
trayIcon.Visible = false;
trayIcon.Visible = true;
}
edit
Ah yes - I cobbled that together without thinking about how you were using it.
If you wish to run this asynchronously then I'd suggest that you place the timer within a worker thread that Invokes a method that toggles the trayIcon.Visible property on completion.

Program hangs after leaving screen saver or locking computer

Our program works fine, until someone locks the computer or the screen-saver pops up (but not ctrl+alt+delete). Once the computer is unlocked/the screen saver is closed, the application stops drawing everything except the title bar, and stops responding to input - it displays a mostly-white window which can't be moved or closed.
(Example of application freezing - the mountains are from my desktop background)
If we let it sit for about 5~10 minutes, it comes back to life, and doesn't hang again (even after locking the computer/screen saver popup) until the application is restarted.
It's difficult to debug, because it doesn't happen when the program is started from Visual Studio, only when the .exe is manually opened.
It only happens when the splash-screen is shown - if I remove the code to show the splash-screen, it stops happening. We need the splash-screen, however.
I've tried every suggestion on this page; the only one this doesn't happen with is using Microsoft.VisualBasic.WindowsFormsApplicationBase, but that causes all sorts of other problems.
Information about this on the Internet appears to be scarce - has anyone run into a similar problem before?
Here is the relevant code:
//Multiple programs use this login form, all have the same issue
public partial class LoginForm<TMainForm>
where TMainForm : Form, new()
{
private readonly Action _showLoadingForm;
public LoginForm(Action showLoadingForm)
{
...
_showLoadingForm = showLoadingForm;
}
private void btnLogin_Click(object sender, EventArgs e)
{
...
this.Hide();
ShowLoadingForm(); //Problem goes away when commenting-out this line
new TMainForm().ShowDialog();
this.Close();
}
private void ShowLoadingForm()
{
Thread loadingFormThread = new Thread(o => _showLoadingForm());
loadingFormThread.IsBackground = true;
loadingFormThread.SetApartmentState(ApartmentState.STA);
loadingFormThread.Start();
}
}
Here is an example of one of the _showLoadingForm actions used in one of the programs:
public static bool _showSplash = true;
public static void ShowSplashScreen()
{
//Ick, DoEvents! But we were having problems with CloseSplashScreen being called
//before ShowSplashScreen - this hack was found at
//https://stackoverflow.com/questions/48916/multi-threaded-splash-screen-in-c/48946#48946
using(SplashForm splashForm = new SplashForm())
{
splashForm.Show();
while(_showSplash)
Application.DoEvents();
splashForm.Close();
}
}
//Called in MainForm_Load()
public static void CloseSplashScreen()
{
_showSplash = false;
}
Splash Screen Issues
The DoEvents thing is very undesirable and doesn't necessarily accomplish what you think it does. DoEvents tell the CLR to attend to the windows message loop (for the splash screen), but doesn't necessarily offer up any processing time to other threads. Thread.Sleep() will offer other threads a chance to process, but won't necessarily allow the windows message loop for your splash screen to continue pumping messages. So you really need both if you must use a loop, but in a minute I'm going to recommend getting away from this loop altogether. In addition to that loop issue, I don't see any explicit way the splash thread is being cleaned up. You need some kind of Thread.Join() or Thread.Abort() happening somewhere.
Instead of using a Application.DoEvents() loop, I like to use a ManualResetEvent to synchronize the splash forms start up with the calling thread. That way the ShowSplash() method doesn't return until the splash is shown. Anytime after that we are obviously ok to close it down as we know it was finished being shown.
Here's a thread with a few good examples:.NET Multi-threaded Splash Screens in C#
Here's how I modified my favorite example, that #AdamNosfinger posted, to include a ManualResetEvent to synchronize the ShowSplash method with the splash screen thread:
public partial class FormSplash : Form
{
private static Thread _splashThread;
private static FormSplash _splashForm;
// This is used to make sure you can't call SplashScreenClose before the SplashScreenOpen has finished showing the splash initially.
static ManualResetEvent SplashScreenLoaded;
public FormSplash()
{
InitializeComponent();
// Signal out ManualResetEvent so we know the Splash form is good to go.
SplashScreenLoaded.Set();
}
/// <summary>
/// Show the Splash Screen (Loading...)
/// </summary>
public static void ShowSplash()
{
if (_splashThread == null)
{
// Setup our manual reset event to syncronize the splash screen thread and our main application thread.
SplashScreenLoaded = new ManualResetEvent(false);
// show the form in a new thread
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
// Wait for the splash screen thread to let us know its ok for the app to keep going.
// This next line will not return until the SplashScreen is loaded.
SplashScreenLoaded.WaitOne();
SplashScreenLoaded.Close();
SplashScreenLoaded = null;
}
}
// called by the thread
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new FormSplash();
// create a new message pump on this thread (started from ShowSplash)
Application.Run(_splashForm);
}
/// <summary>
/// Close the splash (Loading...) screen
/// </summary>
public static void CloseSplash()
{
// need to call on the thread that launched this splash
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
Main Form Issues
It looks as though you are launching your mainform from your login window using ShowDialog and then closing the login form. Have I understood correctly? This is not good if so. ShowDialog is intended for child windows of your application and wants to have an owner window, if you don't specify an owner form in the method arguments the currently active window is assumed to be the owner. See MSDN
So your main form is assuming the login form is its parent, but you close the login form shortly after showing the main form. So I'm not sure what state the application is left in at that point. You should consider using a standard Form.Show() method instead and simply adjusting the Form properties to appear like a dialog if this is the desired outcome (ex: BorderStyle, MaximizeBox, MinimizeBox, ControlBox, TopMost).
IMPORTANT EDIT: Ok I'm human, I messed up and forgot ShowDialog was a blocking method. While that does negate the owner handle issue, I still recommend not using ShowDialog for your main application form unless you can provide a significant justification for it that is not appearance or threading related (as those should be fixed with other techniques). The advice is still sound, despite the misstep on my part.
Possible Painting Issues
You did not specify which controls you were using or if you were doing any custom painting in your application. But you need to keep in mind some windows handles will be forcibly closed when you lock the computer. For example if you have some custom painted controls and are caching fonts, brushes or other GDI resources you need to have some try { ... } catch { ... } blocks in your code that dispose of and then rebuild the cached GDI resources when an exception is raised during painting. I've run into this before where I was custom painting a list box and caching some GDI objects. If you have any custom painting code anywhere in your app, including in the splash screen, please double check all GDI objects are nicely disposed/cleaned up.
After adding a few lines of code to the code snippets above, I could compile a working program. However, I could not reproduce the problem (Windows 7 Starter). I tried locking the computer, and starting the screen saver, too. I did this while the splash screen was active, and in other situations, but the main window always remained responsive. I think there must be something else going on here, probably during the initialization of the main window.
Here is the code, maybe it helps the others figure out the problem.
using System;
using System.Threading;
using System.Windows.Forms;
public class MainForm : Form
{
//Here is an example of one of the _showLoadingForm actions used in one of the programs:
public static bool _showSplash = true;
public static void ShowSplashScreen()
{
//Ick, DoEvents! But we were having problems with CloseSplashScreen being called
//before ShowSplashScreen - this hack was found at
//http://stackoverflow.com/questions/48916/multi-threaded-splash-screen-in-c/48946#48946
using(SplashForm splashForm = new SplashForm())
{
splashForm.Show();
while(_showSplash)
Application.DoEvents();
splashForm.Close();
}
}
//Called in MainForm_Load()
public static void CloseSplashScreen()
{
_showSplash = false;
}
public MainForm()
{
Text = "MainForm";
Load += delegate(object sender, EventArgs e)
{
Thread.Sleep(3000);
CloseSplashScreen();
};
}
}
//Multiple programs use this login form, all have the same issue
public class LoginForm<TMainForm> : Form where TMainForm : Form, new()
{
private readonly Action _showLoadingForm;
public LoginForm(Action showLoadingForm)
{
Text = "LoginForm";
Button btnLogin = new Button();
btnLogin.Text = "Login";
btnLogin.Click += btnLogin_Click;
Controls.Add(btnLogin);
//...
_showLoadingForm = showLoadingForm;
}
private void btnLogin_Click(object sender, EventArgs e)
{
//...
this.Hide();
ShowLoadingForm(); //Problem goes away when commenting-out this line
new TMainForm().ShowDialog();
this.Close();
}
private void ShowLoadingForm()
{
Thread loadingFormThread = new Thread(o => _showLoadingForm());
loadingFormThread.IsBackground = true;
loadingFormThread.SetApartmentState(ApartmentState.STA);
loadingFormThread.Start();
}
}
public class SplashForm : Form
{
public SplashForm()
{
Text = "SplashForm";
}
}
public class Program
{
public static void Main()
{
var loginForm = new LoginForm<MainForm>(MainForm.ShowSplashScreen);
loginForm.Visible = true;
Application.Run(loginForm);
}
}
Several years later (with the code no longer in front of me), I'll add an answer for anyone else who experiences this problem.
The issue turned out to be exactly as Hans Passant had guessed. The problem was that, due to some incredibly obscure and innocuous bugs in the .Net framework, InvokeRequired can sometimes return false when it should return true, causing code that should run on the GUI thread to run in the background (which, due to some more obscure and innocuous bugs, causes the behavior I was seeing).
The solution is to not rely on InvokeRequired, using a hack similar to this:
void Main()
{
Thread.Current.Name = "GuiThread";
...
}
bool IsGuiThread()
{
return Thread.Current.Name == "GuiThread";
}
//Later, call IsGuiThread() to determine if GUI code is being run on GUI thread
This solution, as well as an extremely in-depth look at the causes of the issue, was found here.
since there is no working example
can you try removing Application.DoEvents(); and inserting a thread.sleep?
Application.DoEvents(); let say can be very evil.
From the quick scan I did of your code, it looks like the key to your problem might be using
Application.Run(_splashForm);
Ideally you would use that inside a thread, but maybe it would work in conjunction with your DoEvents too. Sorry if you are doing that and I just missed it...
In our application we had some similar problems with the splash screen. We wanted to have a splash screen with an animated gif (don't blame on me, it was a management decision). That only works correctly, when the splashScreen has its own message loop. Because I think the DoEvents is the key to your problem, I show you, how we solved it. Hopefully it will help you to solve your problem!
We're going to show the splash screen in that way:
// AnimatedClockSplashScreen is a special form from us, it can be any other!
// Our form is set to be TopMost
splashScreen = new AnimatedClockSplashScreen();
Task.Factory.StartNew(() => Application.Run(splashScreen));
The splash screen is a simple containing the animated gif of a clock. It doesn't have any loop, so it doesn't steel any time.
When the splash needs to be closed, we do it in that way:
if (splashScreen != null)
{
if (splashScreen.IsHandleCreated)
{
try
{
splashScreen.Invoke(new MethodInvoker(() => splashScreen.Close()));
}
catch (InvalidOperationException)
{
}
}
splashScreen.Dispose();
splashScreen = null;
}
remove this line, you don't need it, You are forcing it to a single thread when the default is mta. Take the default.
loadingFormThread.SetApartmentState(ApartmentState.STA);
change the following:
using(SplashForm splashForm = new SplashForm())
{
splashForm.Show();
while(_showSplash)
Application.DoEvents();
splashForm.Close();
}
to:
SplashForm splashForm = new SplashForm())
splashForm.Show();
Change this:
public static void CloseSplashScreen()
{
_showSplash = false;
}
to this:
public static void CloseSplashScreen()
{
splashForm.Close();
}
Here's a shot in the dark: when we idle, we also ask the thread to go to sleep. I'm not sure that this will help, but it's worth a shot:
while(_showSplash) {
System.Threading.Thread.Sleep(500);
Application.DoEvents();
}
Have you tried using a WaitHandle for showing the form in the thread?
Something like:
EventWaitHandle _waitHandle = new AutoResetEvent(false);
public static void ShowSplashScreen()
{
using(SplashForm splashForm = new SplashForm())
{
splashForm.Show();
_waitHandle.WaitOne();
splashForm.Close();
}
}
//Called in MainForm_Load()
public static void CloseSplashScreen()
{
_waitHandle.Set();
}
I think your problem is because you are using Form.ShowDialog, not Application.Run. ShowDialog runs a restricted message loop that runs on top of the main message loop and ignores some windows messages.
Something like this should work:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
Application.Run( new MainForm() );
}
}
public partial class MainForm: Form
{
FormSplash dlg = null;
void ShowSplashScreen()
{
var t = new Thread( () =>
{
using ( dlg = new FormSplash() ) dlg.ShowDialog();
}
);
t.SetApartmentState( ApartmentState.STA );
t.IsBackground = true;
t.Start();
}
void CloseSplashScreen()
{
dlg.Invoke( ( MethodInvoker ) ( () => dlg.Close() ) );
}
public MainForm()
{
ShowSplashScreen();
InitializeComponent();
Thread.Sleep( 3000 ); // simulate big form
CloseSplashScreen();
}
}

Making a winforms 2.0 Splash Screen

What is the easiest way to fire a splash screen (Which disappears on its own) in a C# / .NET 2.0 winforms app? It looks like the VisualBasic assembly (which can be called from C# nonetheless) has a way to do this, but are there any simple examples?
Thanks
There's a detailed tutorial on Code Project which puts the splash screen on its own thread so the main app can get on with loading up.
Easiest way would be to create a form and allow it to kill itself after some time it is shown. But, things gets more complicated if you want this form to be able to display some application loading progress while application is initializing, and disappear for example 3 seconds after application is really ready for use.
Idea would include putting the splash screen on completely different thread from the main application. It's thread function should go like that:
static void ThreadFunc()
{
_splash = new Splash();
_splash.Show();
while (!_shouldClose)
{
Application.DoEvents();
Thread.Sleep(100);
if (new Random().Next(1000) < 10)
{
_splash.Invoke(new MethodInvoker(_splash.RandomizeText));
}
}
for (int n = 0; n < 18; n++)
{
Application.DoEvents();
Thread.Sleep(60);
}
if (_splash != null)
{
_splash.Close();
_splash = null;
}
}
Then, you can use this to show and hide it:
static public void ShowSplash()
{
_shouldClose = false;
Thread t = new Thread(ThreadFunc);
t.Priority = ThreadPriority.Lowest;
t.Start();
}
internal static void RemoveSplash()
{
_shouldClose = true;
}

Categories