I have a splash screen for my C# database application that is called via the Shown event. The splash screen contains some information that is preprocessed when the main form's constructor is called, hence why I'm using the Shown event, because that information should be available.
However, when the splash screen is shown, the main form is whited out, and the menu bar, bottom menu bar, and even the gray background are all white and invisible. It looks like the program is hanging, but after the 5 second delay I have built in, the banner goes away and the program is shown normally. Also, on the banner, I have labels that are not shown when the splash screen displays...
Here is my code, some reasoning behind why it isn't working would help greatly.
SPLASH SCREEN CODE :
public partial class StartupBanner : Form
{
public StartupBanner(int missingNum, int expiredNum)
{
InitializeComponent();
missingLabel.Text = missingNum.ToString() + " MISSING POLICIES";
expiredLabel.Text = expiredNum.ToString() + " EXPIRED POLICIES";
}
}
CALLING CODE :
private void MainForm_Shown(object sender, EventArgs e)
{
StartupBanner startup = new StartupBanner(missingPoliciesNum, expiredPoliciesNum);
startup.MdiParent = this;
startup.Show();
Thread.Sleep(5000);
startup.Close();
}
Using startup.ShowDialog() shows the correct label information on the splash screen, but that locks up the application, and I need the splash to go away after about 5 seconds, which is why it's a splash. ;)
First run the splash screen with ShowDialog() instead of Show(), so the splashscreen locks the main form and do not lock the main thread:
private void MainForm_Shown(object sender, EventArgs e)
{
StartupBanner startup = new StartupBanner(missingPoliciesNum, expiredPoliciesNum);
startup.ShowDialog();
}
In the splash screen form you should define a timer that closes the form after 5 seconds:
public partial class StartupBanner : Form
{
private System.Windows.Forms.Timer _closeTimer = new Timer();
public StartupBanner(int missingNum, int expiredNum)
{
this.InitializeComponent();
missingLabel.Text = missingNum.ToString() + " MISSING POLICIES";
expiredLabel.Text = expiredNum.ToString() + " EXPIRED POLICIES";
this._closeTimer = new System.Windows.Forms.Timer();
this._closeTimer.Interval = 5000;
this._closeTimer.Tick += new EventHandler(this._closeTimer_Tick);
this._closeTimer.Start();
}
private void _closeTimer_Tick(object sender, EventArgs e)
{
System.Windows.Forms.Timer timer = (System.Windows.Forms.Timer)sender;
timer.Stop();
this.Close();
}
}
EDIT: Thread.Sleep() locks the whole thread e.g. each action for the forms, so that they cannot handle any message like clicks or button presses. They do not run in background, so it is better to use a timer that can close the form in background.
FIX: removed startup.MdiParent = this; line
It is a feature of Windows, designed to help the user cope with unresponsive programs. When you display the splash screen, Windows sends your main form a message to tell it that it is no longer the active window. This normally causes it to redraw the window caption, using the color for inactive windows. That same message also fires the Form.Deactivated event.
But that doesn't work anymore, your main thread is busy executing code and not going idle to 'pump the message loop'. Windows notices this, the message isn't getting delivered. After a couple of seconds, it replaces your window with the 'ghost window'. It has the same size and border as your main window but with no content, just a white background. And a caption that reads "Not responding". Enough for the user to know that trying to use the window isn't going to work.
Avoid this by using a real splash screen, support for it is already built into .NET.
In your code, MainForm_Shown wait 'Thread.Sleep(5000);', so it got white because the main thread sleep and couldn't get any message. The StartupBanner Form have some reason, too. I suggest you use Thread to avoid sub function or sub thread intercepting main thread and make MainForm whited out.
There is inbuild support for splashscreens in .Net
The best way and using the API is
SplashScreen splash = new SplashScreen("splashscreen.jpg");
splash.Show(false);
splash.Close(TimeSpan.FromMilliseconds(2));
InitializeComponent();
Related
How to display an intermediate screen in the transition from screen 1 to screen 2
Screen 2 contains a table with a database that takes some time to display. Switching between screen 1 and screen 2 The software disappears until Screen 2 opens. How can I post a message to the user "Please wait ..."
this my code:
this.Hide();
Form C = new Main();
C.ShowDialog();
this.Show();
i work on C# , WinForm
thanks
You can use Thread. Start your Thread before ShowDialog and run below-shown method within that thread. To close that thread you need to use Thread Form's Shown Event. So that you can close thread after form successfully shown to the user. It is mandatory to close your thread.
private static LoadingForm loadForm;
static private void ShowForm()
{
loadForm = new LoadingForm();
Application.Run(loadForm);
}
This loadForm object should have your loading image in the background-image property of form.
I am showing minimal code so that you can do rest of the task by yourself. It's good if you do something by yourself. Beware of how to handle cross-thread exception whenever using Thread.
Hope this makes help.
I'm working on a custom splash-screen (since the standard one doesn't fit my needs). But there is one option I'd like to have from it - auto-close. But to implement it, I need to understand how the common SplashScreen selects the moment to close.
So, is there any sort of event to message the splash screen, to tell it that it should be closed? What event does the common splash-screen use, at least?
The WPF SplashScreen class uses a very simple trick, it calls Dispatcher.BeginInvoke().
The expectation is that the UI thread is grinding away getting the program initialized and is therefore not dispatching anything. It is "hung". Not forever of course, as soon as it is done, it re-enters the dispatcher loop and now the BeginInvoked method gets a chance, the ShowCallback() method runs. Poorly named, should be "CloseCallback" :) A 0.3 second fade covers up any additional delay in getting the main window to render.
In general, calling Dispatcher.BeginInvoke() on the UI thread looks like a weird hack but is very useful. An excellent way to solve gritty re-entrancy problems.
Very simple, not the only way to do it. The main window's Load event could be a useful trigger.
Instead of having an image file with Build Action set to Splash Screen, you can have more control over the splash screen by creating and showing it yourself in the Application's OnStartup event handler. The show method of SplashScreen has a parameter to stop it closing automatically and then you can tell it when to close using the Close method:
Firstly remove the StartupUri tag from App.xaml:
<Application x:Class="Splash_Screen.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
Change the Build Action of your image file to Resource
Then create and show the splash screen in the OnStartup event handler:
public partial class App : Application
{
private const int MINIMUM_SPLASH_TIME = 1500; // Miliseconds
private const int SPLASH_FADE_TIME = 500; // Miliseconds
protected override void OnStartup(StartupEventArgs e)
{
// Step 1 - Load the splash screen
SplashScreen splash = new SplashScreen("splash.png");
splash.Show(false, true);
// Step 2 - Start a stop watch
Stopwatch timer = new Stopwatch();
timer.Start();
// Step 3 - Load your windows but don't show it yet
base.OnStartup(e);
MainWindow main = new MainWindow();
// Step 4 - Make sure that the splash screen lasts at least two seconds
timer.Stop();
int remainingTimeToShowSplash = MINIMUM_SPLASH_TIME - (int)timer.ElapsedMilliseconds;
if (remainingTimeToShowSplash > 0)
Thread.Sleep(remainingTimeToShowSplash);
// Step 5 - show the page
splash.Close(TimeSpan.FromMilliseconds(SPLASH_FADE_TIME));
main.Show();
}
}
Here are the basic events I want to happen when my WPF application starts. This is very similar to how Word starts on my machine.
Display busy cursor.
Perform basic initialization. This takes a couple of seconds and needs to be done before splash screen is displayed.
Display splash screen. This splash screen displays progress into more in-depth initialization and can take awhile (caches information from database).
Display default cursor. Since splash screen is displaying progress now, there's no need to display a busy cursor.
Once splash screen progress is complete, display main window.
Close splash screen.
Everything works fine except for the displaying of the busy cursor prior to the splash screen being displayed. When I execute the application through a shortcut, the wait cursor flashes, but soon goes back to the default. I've tried different ways to set the Cursor but none work, but I think the problem is that I'm not in a control/window--I'm doing it from within App.xaml.cs. And, the properties I'm setting seem to be Windows Forms properties. Here is an excerpt from my code in App.xaml.cs.
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
System.Windows.Forms.Application.UseWaitCursor = true;
//System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
//System.Windows.Forms.Application.DoEvents();
Initialize();
SplashWindow splash = new SplashWindow();
splash.Show();
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
// Right now I'm showing main window right after splash screen but I will eventually wait until splash screen closes.
MainWindow main = new MainWindow();
main.Show();
}
This should work
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
Use System.Windows.Input not System.Windows.Forms.
If you have a task that takes a significant amount of time, and it is running on a non-GUI thread, (which is a good idea) you can use this code to change the application cursor:
Application.Current.Dispatcher.Invoke(() =>
{
Mouse.OverrideCursor = Cursors.Wait;
});
When the busy process completes, use this:
Application.Current.Dispatcher.Invoke(() =>
{
Mouse.OverrideCursor = null;
});
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
InitializeComponent();
...
Mouse.OverrideCursor = null;
I'm assuming the Initialize() is the part that you want your busy cursor to appear for, yes?
If so, try the following approach:
In your MainWindow.xaml, on the <Window> element, set the following properties: Visibility="Hidden" and Cursor="Wait".
In your MainWindow.xaml.cs, move the initialization code out of the constructor and into a public Initialize() method, so that any code that depends on the Initialize() call doesn't get executed. Make sure the end of your Initialize() method sets the Visiblity property to Visible and resets the Cursor as well.
Update the code snippet posted above to something like the following:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow main = new MainWindow();
main.Show(); // this should set the cursor how you want it
Initialize();
SplashWindow splash = new SplashWindow();
splash.Show();
main.Initialize(); // now invoke the Initialize method you created
// Right now I'm showing main window right after splash screen but I will eventually wait until splash screen closes.
}
For me it worked using a mix of the stuff stated above:
class MyForm : System.Windows.Window {}
class Test{
MyForm myForm;
void ShowWaitCurserInMyForm(){
//before kicking off the stuff I'm waiting for:
System.Windows.Forms.Application.UseWaitCursor = true; // disables all Input from the mouse
myForm.Cursor = System.Windows.Input.Cursors.Wait; // actually displays a wait Cursor
// do time intensive stuff here, if we wait for an event, following stuff belongs in its handler
System.Windows.Forms.Application.UseWaitCursor = false; // reenables all Input from the mouse
myForm.Cursor = null; // reset the Cursor visually
}
}
I am using a splash screen for a c# which runs on startup and checks the app license.
I show the splash from the main form like this:
public partial class Form1 : Form
{
static bool stopThreads = false;
static bool gridChanged = false;
public Form1()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(DoSplash));
th.Start();
th.Join();
}
private void DoSplash()
{
Splash sp = new Splash();
sp.ShowDialog();
}
Now from the splash form I am trying exit the application when the license is invalid, but it only exits the splash and it enters the main form.
I tried exiting with :
Enviroment.Exit();
Application.Exit();
Form1 f = new Form1();
this.Close();
But none closes the main form, only the splash.
How can I close the entire app from the splash form class?
Try Application.ExitThread()
Yes, these calls only cause the thread to exit. You created a new thread. There's little point to be gracious about it in this case, Environment.Exit(1) will get the job done. The huff-and-puff version is Control.BeginInvoke() to run code on the main UI thread. You'll need a reference to Form1 to make that call.
Btw, you'll also have a big problem with SystemEvents, they run on the wrong thread because the very first window you created was created on thread other than the main UI thread. The most typical mishap is a deadlock when you lock and unlock the work station. You'll need to wait until at least one window is created on the UI thread. Form1's OnLoad() method override or Load event would be a good place to start the splash. Or just use the built-in support for splash screens.
You could use Application.Exit() or Environment.Exit().
These probably aren't the "cleanest" way to shut down your app, but if you're just bailing at the splash screen, it's unlikely it'll cause any issues.
Edit: If you want to quit without showing the splash screen at all if the licence is invalid, you should check the licence before showing the splash screen, and just exit before then.
Never introduce multithreading in application unless absolutely necessary.
As Sir Walter put it,
Else thou shalt enter into a world of pain.
Moreover, any UI interactions, such as displaying a window or working with controls, must be done on main thread only.
If you want to do something while the form is on the screen, call Show instead of modal ShowDialog so execution does not get blocked.
Application.Exit ()
will do nicely if you call it on the main thread, as you should.
If you want to show splash screen before main form is shown, you should not do it in main form's InitializeComponent. Instead, change code in Program.cs to show splash screen first:
Application.Run (new SplashScreenForm ());
Somewhere in SplashScreenForm (I don't know why you need it at all, honestly) you should check for license, and if it's fine, close the window, create MainForm instance and call its ShowDialog. If it's bad—just close the window, and since it was the last form, application would stop.
I have an application.
First I display a splash screen, a form, and this splash would call another form.
Problem: When the splash form is displayed, if I then open another application on the top of the splash, and then minimize this newly opened application window, the splash screen becomes white. How do I avoid this? I want my splash to be displayed clearly and not affected by any application.
You need to display the splash screen in a different thread - currently your new form loading code is blocking the splash screen's UI thread.
Start a new thread, and on that thread create your splash screen and call Application.Run(splash). That will start a new message pump on that thread. You'll then need to make your main UI thread call back to the splash screen's UI thread (e.g. with Control.Invoke/BeginInvoke) when it's ready, so the splash screen can close itself.
The important thing is to make sure that you don't try to modify a UI control from the wrong thread - only use the one the control was created on.
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();
}
}
}
I had a similar issue you might want to check out. The Stack Overflow answer I got worked perfectly for me - you may want to take a look.