I am working with Windows forms. I have set up a splash screen to appear when opening the program like this.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.FormBorderStyle = FormBorderStyle.None;
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(15);
if (progressBar1.Value == 30) timer1.Stop();
}
It has a progress bar and a timer. I got the idea from another post here. This is working OK, the splash screen is appearing as it should. On the windows form to appear after this I have:
Thread t = new Thread(new ThreadStart(splash));
t.Start();
Thread.Sleep(2000);
InitializeComponent();
t.Abort();
The problem is as follows. When I open the exe, the splash screens appear for about 2 secs as it should, and then when it goes away, the next form appears a second and then goes behind every other window I have opened.
Do you know how can I fix it?
Update 1:
public void splash()
{
System.Windows.Forms.Application.Run(new Form2());
}
This is my splash screen
Final Update:
I got it to work this way.
Splash splash = new Splash();
Instalador instalador = new Instalador();
splash.Show();
Thread.Sleep(2 * 1000);
splash.Close();
Application.Run(instalador);
This on the main. I do not know how OK is this, but it works.
To answer your specific question, you might get away with just adding this.Activate() after InitializeComponent
Thread t = new Thread(new ThreadStart(splash));
t.Start();
Thread.Sleep(2000);
InitializeComponent();
this.Activate();
t.Abort();
However, at the very least, implement handshaking that shuts down the splash screen nicely rather than just aborting the thread.
Also, depending on exactly what you're doing in your splash screen thread, there may be a multitude of subtle issues - e.g. the Z-ordering issue you're currently having.
Typically, I launch my splash screens from Main() before Application.Run({main form}) and also call Application.Run({splashScreen}) from it's thread handler. Application.Run has thread affinity so the splash screen will get its own ApplicationContext which will properly hook Closing/Closed events to shutdown the thread and raise ThreadExit just by invoking or beginInvoking SplashScreen.Close() (typically from MainForm_Shown event handler). It also has other minor benefits such as Application.OpenForms containing the splash screen.
Taking this route has pretty much eliminated my bugs and frustrations with splash screens.
-Begin Edit-
A more thorough example:
Start up the second thread and ApplicationContext in Main()
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread t = new Thread(new ThreadStart(splash));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
Application.Run(new Form1());
}
private static void splash()
{
Application.Run(new Form2());
}
}
Splash screen with a SetProgress method added so you can update it externally if you wish.
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
timer1.Start();
}
public void SetProgress()
{
if (this.InvokeRequired) //Should have been handled by SplashScreenHandler but check just in case.
this.BeginInvoke(new Action(SetProgress));
progressBar1.Increment(15);
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(15);
if (progressBar1.Value == 30) timer1.Stop();
}
}
Main form: Notice nothing in constructor and the Shown event handler shuts down the splash screen(s) without having to carry in a reference (if you're going to instead rely on the timer for status updates.)
public partial class Form1 : Form
{
public Form1()
{
//Simulate a long init
Thread.Sleep(2000);
InitializeComponent();
}
private void Form1_Shown(object sender, EventArgs e)
{
foreach (var splashScreen in Application.OpenForms.OfType<Form2>())
{
splashScreen.BeginInvoke( (Action) delegate () { splashScreen.Close(); });
}
}
}
-End Edit-
After long time struggle i got the solution.
Use the code into Login Form (Form_Load) this.Activate();
Example:
private void login_Load(object sender, EventArgs e)
{
this.Activate();
}
I had the same issue and after stuggling for a very long time I ended up doing the following in the Load event of the main form after closing the splash screen:
//Manually activate frmMain as it gets back in the Z-order by Windows when splash screen closes.
NativeMethods.SetForegroundWindow(this.Handle);
this.Activate();
Where "SetForegroundWindow" is defined as follows:
/// <summary>
/// Sets foreground window
/// </summary>
/// <param name="hWnd"></param>
/// <returns></returns>
[DllImport("user32.dll")]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
That being said we probably need to see more of your code.
Related
I need to show splash screen on my application start for few seconds. Does anybody know how to implement this?
Will be much appreciate for the help.
First, create your splash screen as a borderless, immovable form with your image on it, set to initially display at the center of the screen, colored the way you want. All of this can be set from within the designer; specifically, you want to:
Set the form's ControlBox, MaximizeBox, MinimizeBox and ShowIcon properties to "False"
Set the StartPosition property to "CenterScreen"
Set the FormBorderStyle property to "None"
Set the form's MinimumSize and MaximumSize to be the same as its initial Size.
Then, you need to decide where to show it and where to dismiss it. These two tasks need to occur on opposite sides of the main startup logic of your program. This could be in your application's main() routine, or possibly in your main application form's Load handler; wherever you're creating large expensive objects, reading settings from the hard drive, and generally taking a long time to do stuff behind the scenes before the main application screen displays.
Then, all you have to do is create an instance of your form, Show() it, and keep a reference to it while you do your startup initialization. Once your main form has loaded, Close() it.
If your splash screen will have an animated image on it, the window will need to be "double-buffered" as well, and you will need to be absolutely sure that all initialization logic happens outside the GUI thread (meaning you cannot have your main loading logic in the mainform's Load handler; you'll have to create a BackgroundWorker or some other threaded routine.
Here are some guideline steps...
Create a borderless form (this will be your splash screen)
On application start, start a timer (with a few seconds interval)
Show your Splash Form
On Timer.Tick event, stop timer and close Splash form - then show your main application form
Give this a go and if you get stuck then come back and ask more specific questions relating to your problems
simple and easy solution to create splash screen
open new form use name "SPLASH"
change background image whatever you want
select progress bar
select timer
now set timer tick in timer:
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(1);
if (progressBar1.Value == 100) timer1.Stop();
}
add new form use name "FORM-1"and use following command in FORM 1.
note: Splash form works before opening your form1
add this library
using System.Threading;
create function
public void splash()
{
Application.Run(new splash());
}
use following command in initialization like below.
public partial class login : Form
{
public login()
{
Thread t = new Thread(new ThreadStart(splash));
t.Start();
Thread.Sleep(15625);
InitializeComponent();
enter code here
t.Abort();
}
}
http://solutions.musanitech.com/c-create-splash-screen/
I wanted a splash screen that would display until the main program form was ready to be displayed, so timers etc were no use to me. I also wanted to keep it as simple as possible.
My application starts with (abbreviated):
static void Main()
{
Splash frmSplash = new Splash();
frmSplash.Show();
Application.Run(new ReportExplorer(frmSplash));
}
Then, ReportExplorer has the following:
public ReportExplorer(Splash frmSplash)
{
this.frmSplash = frmSplash;
InitializeComponent();
}
Finally, after all the initialisation is complete:
if (frmSplash != null)
{
frmSplash.Close();
frmSplash = null;
}
Maybe I'm missing something, but this seems a lot easier than mucking about with threads and timers.
create splash
private void timer1_Tick(object sender, EventArgs e)
{
counter++;
progressBar1.Value = counter *5;
// label2.Text = (5*counter).ToString();
if (counter ==20)
{
timer1.Stop();
this.Close();
}
}
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.GradientInactiveCaption;
this.ClientSize = new System.Drawing.Size(397, 283);
this.ControlBox = false;
this.Controls.Add(this.label2);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.label1);
this.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Splash";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.ResumeLayout(false);
this.PerformLayout();
Then in your application
sp = new Splash();
sp.ShowDialog();
The other answers here cover this well, but it is worth knowing that there is built in functionality for splash screens in Visual Studio: If you open the project properties for the windows form app and look at the Application tab, there is a "Splash screen:" option at the bottom. You simply pick which form in your app you want to display as the splash screen and it will take care of showing it when the app starts and hiding it once your main form is displayed.
You still need to set up your form as described above (with the correct borders, positioning, sizing etc.)
None of the other answers gave me exactly what I was looking for. Read on for my solution to the problem.
I want a splash screen to fade in from 0% opacity to 100% opacity while things boot up, with a minimum display time of 2000ms (to allow the full fade in effect to show). Once everything is ready, I want the splash screen to display for a further 500ms while the main screen displays behind the splash screen. Then I want the splash screen to go away, leaving the main screen running.
Note that I use the MVP pattern for winforms. If you don't use MVP, you will need to simplify the below example a little.
Long story short, you need to create an AppContext class that inherits from ApplicationContext. I have put this in my Program.cs as below:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
}
public class AppContext : ApplicationContext
{
private IMainPresenter _mainPresenter;
private bool _ready;
public AppContext()
{
_ready = false;
using (ISplashPresenter splashPresenter = new SplashPresenter(new SplashView()))
{
Stopwatch sw = Stopwatch.StartNew();
_mainPresenter = new MainPresenter(new MainView());
_mainPresenter.Closed += MainPresenter_Closed;
new Thread(() =>
{
// !!! Do work here !!!
if (sw.ElapsedMilliseconds < 2000)
Thread.Sleep(2000 - (int)sw.ElapsedMilliseconds);
_ready = true;
})
.Start();
while (!_ready)
{
Application.DoEvents();
Thread.Sleep(1);
}
_mainPresenter.Show();
_ready = false;
new Thread(() =>
{
Thread.Sleep(500);
_ready = true;
})
.Start();
while (!_ready)
{
Application.DoEvents();
Thread.Sleep(1);
}
}
}
private void MainPresenter_Closed(object sender, EventArgs e)
{
ExitThread();
}
}
There are several implementation specific details that I haven't gone into here, such as ISplashPresenter implementing IDisposable and exactly how the fade in is managed; if enough people request it I will edit this answer to include a complete example.
First you should create a form with or without Border (border-less is preferred for these things)
public class SplashForm : Form
{
Form _Parent;
BackgroundWorker worker;
public SplashForm(Form parent)
{
InitializeComponent();
BackgroundWorker worker = new BackgroundWorker();
this.worker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.worker _DoWork);
backgroundWorker1.RunWorkerAsync();
_Parent = parent;
}
private void worker _DoWork(object sender, DoWorkEventArgs e)
{
Thread.sleep(500);
this.hide();
_Parent.show();
}
}
At Main you should use that
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
}
Maybe a bit late to answer but i would like to share my way.
I found an easy way with threads in the main program for a winform application.
Lets say you have your form "splashscreen" with an animation, and your "main" which has all your application code.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread mythread;
mythread = new Thread(new ThreadStart(ThreadLoop));
mythread.Start();
Application.Run(new MainForm(mythread));
}
public static void ThreadLoop()
{
Application.Run(new SplashScreenForm());
}
In your main form in the constructor:
public MainForm(Thread splashscreenthread)
{
InitializeComponent();
//add your constructor code
splashscreenthread.Abort();
}
This way the splashscreen will last just the time for your main form to load.
Your splashcreen form should have his own way to animate/display information.
In my project my splashscreen start a new thread, and every x milliseconds it changes his main picture to another which is a slightly different gear, giving the illusion of a rotation.
example of my splashscreen:
int status = 0;
private bool IsRunning = false;
public Form1()
{
InitializeComponent();
StartAnimation();
}
public void StartAnimation()
{
backgroundWorker1.WorkerReportsProgress = false;
backgroundWorker1.WorkerSupportsCancellation = true;
IsRunning = true;
backgroundWorker1.RunWorkerAsync();
}
public void StopAnimation()
{
backgroundWorker1.CancelAsync();
}
delegate void UpdatingThreadAnimation();
public void UpdateAnimationFromThread()
{
try
{
if (label1.InvokeRequired == false)
{
UpdateAnimation();
}
else
{
UpdatingThreadAnimation d = new UpdatingThreadAnimation(UpdateAnimationFromThread);
this.Invoke(d, new object[] { });
}
}
catch(Exception e)
{
}
}
private void UpdateAnimation()
{
if(status ==0)
{
// mypicture.image = image1
}else if(status ==1)
{
// mypicture.image = image2
}
//doing as much as needed
status++;
if(status>1) //change here if you have more image, the idea is to set a cycle of images
{
status = 0;
}
this.Refresh();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (IsRunning == true)
{
System.Threading.Thread.Sleep(100);
UpdateAnimationFromThread();
}
}
Hope this will help some people.
Sorry if i have made some mistakes. English is not my first language.
Here is the easiest way of creating a splash screen:
First of all, add the following line of code before the namespace in Form1.cs code:
using System.Threading;
Now, follow the following steps:
Add a new form in you application
Name this new form as FormSplashScreen
In the BackgroundImage property, choose an image from one of your folders
Add a progressBar
In the Dock property, set it as Bottom
In MarksAnimationSpeed property, set as 50
In your main form, named as Form1.cs by default, create the following method:
private void StartSplashScreen()
{
Application.Run(new Forms.FormSplashScreen());
}
In the constructor method of Form1.cs, add the following code:
public Form1()
{
Thread t = new Thread(new ThreadStart(StartSplashScreen));
t.Start();
Thread.Sleep(5000);
InitializeComponent();//This code is automatically generated by Visual Studio
t.Abort();
}
Now, just run the application, it is going to work perfectly.
Here's my 2023 take on a 2011 question.
Over time, I've done this many times in many ways. The approach that currently use:
Force the main form Handle creation so that the message that creates the splash can be posted into the main form's message queue using BeginInvoke. This allows the main form ctor to return. Ordinarily the handle (the native hWnd) doesn't come into existence until it's shown. Therefore, it needs to be coerced while it's still hidden.
Override the SetVisibleCore() preventing the main window from becoming visible until the Splash has finished processing.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
Debug.Assert(!IsHandleCreated, "Expecting handle is not yet created.");
// Ordinarily we don't get the handle until
// window is shown. But we want it now.
_ = Handle;
Debug.Assert(IsHandleCreated, "Expecting handle exists.");
// Call BeginInvoke on the new handle so as not to block the CTor.
BeginInvoke(new Action(()=> execSplashFlow()));
}
protected override void SetVisibleCore(bool value) =>
base.SetVisibleCore(value && _initialized);
bool _initialized = false;
private void execSplashFlow()
{
using (var splash = new SplashForm())
{
splash.ShowDialog();
}
_initialized= true;
WindowState = FormWindowState.Maximized;
Show();
}
}
Splash Example
The async initialization can be performed in the Splash class itself or it can fire events causing the main app to do things. Either way, when it closes itself the main form will set the _initialized bool to true and it is now capable of becoming visible.
public partial class SplashForm : Form
{
public SplashForm()
{
InitializeComponent();
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.None;
}
protected async override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible)
{
labelProgress.Text = "Updating installation...";
progressBar.Value = 5;
await Task.Delay(1000);
progressBar.Value = 25;
// SIMULATED background task like making an API call or loading a
// database (long-running task that doesn't require the UI thread).
labelProgress.Text = "Loading avatars...";
await Task.Delay(1000);
labelProgress.Text = "Fetching game history...";
progressBar.Value = 50;
await Task.Delay(1000);
labelProgress.Text = "Initializing scenario...";
progressBar.Value = 75;
await Task.Delay(1000);
labelProgress.Text = "Success!";
progressBar.Value = 100;
await Task.Delay(1000);
DialogResult= DialogResult.OK;
}
}
}
Try this code
public partial class ssplashscreen : Form
{
public ssplashscreen()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(1);
if (progressBar1.Value == 100)
{
timer1.Stop();
this.Hide();
Form frm = new login();
frm.Show();
}
}
}
Try This:
namespace SplashScreen
{
public partial class frmSplashScreen : Form
{
public frmSplashScreen()
{
InitializeComponent();
}
public int LeftTime { get; set; }
private void frmSplashScreen_Load(object sender, EventArgs e)
{
LeftTime = 20;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (LeftTime > 0)
{
LeftTime--;
}
else
{
timer1.Stop();
new frmHomeScreen().Show();
this.Hide();
}
}
}
}
I am new to c# and I am using windows forms.
I have 3 Forms:
SpalshScreenForm with progressBar and Timer
MainForm
ResultForm
What I am trying to do is: I want to write if statement in SplashScrreenForm which checks if Example.txt file exists or not while progressBar is loading, and if the file exists I want to open MainForm and if it doesn't exist I want to open ResultForm.
As shown in code I run the program the SpalshScreenForm opens and after that it closes and then MainForm opens. The issue is that ResultForm never opens and it seems that the if statement does not get executed.
My question is : how can I open SplashScreenForm and check if the files exists, if the file exists I want to open MainForm and if it doesn't exist I want to open ResultForm. I am happy to listen to any new suggestions/ideas. Please help me thank you.
Program.cs Code:
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());
}
}
MainForm Code:
public MainForm()
{
Thread t = new Thread(new ThreadStart(Splash_Screen));
t.Start();
Thread.Sleep(3000);
InitializeComponent();
t.Abort();
}
public void Splash_Screen()
{
Application.Run(new SpalshScreenForm());
}
SplashScreenForm Code:
public SplashScreenForm()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(1);
if(progressBar1.Value==100)
{
if (!File.Exists(#"E:\Example.txt"))
{
ResultForm _ResultForm= new _ResultForm();
_ResultForm.ShowDialog();
}
timer1.Stop();
}
}
In Program.cs, change the last line to
Application.Run(new SplashScreenForm());
To make sure the Splash Screen is the startup project.
Next, change the if statement that checks for the existence of Example.txt in the SplashScreenForm code to
if (!File.Exists(#"E:\Example.txt"))
{
ResultForm _ResultForm= new _ResultForm();
_ResultForm.ShowDialog();
}
else
{
MainForm _MainForm = new _MainForm();
_MainForm.Show();
}
This ensures that either the ResultForm or the MainForm is shown at when the splash screen disappears.
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();
}
}
I have a windows form application that needs to load a bunch of things before loading the Main window. I thought this would justify a ProgressBar, so I thought I display another form that contains the ProgressBar Control using the constructor of my main form.
It all works fine but if I try to put the text in a Label on the intro form its content won't show until the main form is loaded. Is here a way to avoid this other than loading the intro window first?
Warning: this post contains elements of self promotion ;o)
I would probably use a splash form in this case. I wrote a blog post a while ago (triggered by this SO Q&A) about a thread-safe splash form that could be used together will long-running main form initializations.
In short the approach is to using ShowDialog, but to create and display the form on a separate thread so it doesn't block the main thread. The form contains a status message label (could of course be extended with a progressbar as well). Then there is a static class that provides thread-safe methods for displaying, updating and closing the splash form.
Condensed code samples (for commented code samples, check the blog post):
using System;
using System.Windows.Forms;
public interface ISplashForm
{
IAsyncResult BeginInvoke(Delegate method);
DialogResult ShowDialog();
void Close();
void SetStatusText(string text);
}
using System.Windows.Forms;
public partial class SplashForm : Form, ISplashForm
{
public SplashForm()
{
InitializeComponent();
}
public void SetStatusText(string text)
{
_statusText.Text = text;
}
}
using System;
using System.Windows.Forms;
using System.Threading;
public static class SplashUtility<T> where T : ISplashForm
{
private static T _splash = default(T);
public static void Show()
{
ThreadPool.QueueUserWorkItem((WaitCallback)delegate
{
_splash = Activator.CreateInstance<T>();
_splash.ShowDialog();
});
}
public static void Close()
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.Close(); });
}
}
public static void SetStatusText(string text)
{
if (_splash != null)
{
_splash.BeginInvoke((MethodInvoker)delegate { _splash.SetStatusText(text); });
}
}
}
Example of usage:
SplashUtility<SplashForm>.Show();
SplashUtility<SplashForm>.SetStatusText("Working really hard...");
SplashUtility<SplashForm>.Close();
There sure is. It's called a BackgroundWorker.
Here is a code snippet from Figo Fei with slight modification for explanation purposes:
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Maximum = 100;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// This would be the load process, where you put your Load methods into.
// You would report progress as something loads.
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i); //run in back thread
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) //call back method
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) //call back method
{
progressBar1.Value = progressBar1.Maximum;
}
Hope this helps you.
You can show your SplashForm from either the main program or the MainForm constructor, that doesn't really matter. What you are seeing is that as long as your Loading process isn't completed, no messages are processed and hence no Screen updates are happening. The ProgressBar is an exception, it runs it's own thread for precisely this reason.
The short solution is to do a SplashForm.Update() after changing the Label. A little more involved would be to start a separate Thread with a MessagePump (Application.Run). Here is a SO question with some more leads.
The problem is most likely because there is not a running message loop at the time you are attempting to display the progress bar form. There should be a line of code that looks something like the following in the entry point of your application.
Application.Run(new Form1());
The call to Application.Run will start the message loop, but do you see how the Form1 constructor is executed before the message loop is running? And since your progress bar logic is in that constructor then there is no mechanism running that can dispatch the form's painting messages.
I think the best approach is to load a splash screen first and kick off a worker thread (you could use BackgroundWorker for that) that does the time consuming work. The progress bar will live on the splash screen form and you will update that periodically. Once the work is complete then you can close the splash screen and load the main form.
I'm using a Splash Screen from Here. I love how simple it is. But the problem with it is that the splash screen doesn't go away until I click on it. When run within the IDE it works fine. Any ideas? I'd attach the code here but its not inserting properly for some reason.
private System.Windows.Forms.Timer timer1;
//private Splash sp=null;
public Form1()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(DoSplash));
//th.ApartmentState = ApartmentState.STA;
//th.IsBackground=true;
th.Start();
Thread.Sleep(3000);
th.Abort();
Thread.Sleep(1000);
}
private void DoSplash()
{
Splash sp = new Splash();
sp.ShowDialog();
}
private void timer1_Tick(object sender, System.EventArgs e)
{
// sp.Close();
}
First of all, the way the splash screen on that page is done, using Thread.Abort, is not the right way to do things.
Never call Thread.Abort, unless you're in the process of shutting down the AppDomain the thread lives in.
Let me reiterate that for emphasis. The only time you should call Thread.Abort is when you know enough about Thread.Abort and how it behaves to know that you should never call it.
Take a look at this other question on StackOverflow: Multi-Threaded splash screen in c#?.
If you want to keep your existing solution, a possible better way would be to drop a timer into the splash screen form, set its timer to the time you want the splash screen to stay on screen, and call Close in its Tick event handler.
In the same venue, I would simply fire off that original thread, and remove the other lines.
In other words, from the first code block on that page, I would keep these two lines:
Thread th = new Thread(new ThreadStart(DoSplash));
th.Start();
Couple that with that timer on the form that makes the form self-closing, and you're in way better shape than trying to get to grips with Thread.Abort.
Which you should not call.
The DoSplash should probably call sp.Show() instead of sp.ShowDialog()
But calling Sleep(x000) form your main thread to show a splash screen isn't very efficient, and Thread.Abort() should be reserved for better use too.
A better approach is to close your SplashForm with a Timer and set a minimum delay. On startup you can Show() and Update() the SplahForm and continue to load stuff. The timer event won't fire until the MainForm is finished initializing and enters the EventLoop. No threads needed, and the MainForm doesn't have to be involved either.
Just to give you the main ingredients of a MessageLoop driven SplashForm:
public partial class SplashForm : Form
{
// default stuff
public static void Splash()
{
var s = new SplashForm();
s.Show();
s.Update();// force paint
}
private void SplashForm_Load(object sender, EventArgs e)
{
Timer t = new Timer();
t.Interval = 1; // wait for EventLoop
t.Tick += GoAway;
t.Enabled = true;
}
private void GoAway(object sender, EventArgs e)
{
this.Close();
}
}
and then, in Program.cs,
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashForm.Splash();
Application.Run(new MainForm());
That's an ugly implementation. You should check out this SO thread. That describes how to use the VisualBasic.NET namespace from C# and the OnCreateSplashScreen method to do a much cleaner splash screen implementation.