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.
Related
So I am creating an application that will have a splash screen that loads resources, and when complete will load the Main form of the application. In times past I've just let the splash screen own the thread and stay hidden, but I'm toying with the idea of actually letting my Main form be the thread owner, but I'm having trouble transitioning between forms. My current approach is
void main(String[] args)
{
ApplicationContext appContext = new ApplicationContext(new SplashScreen());
appContext.ThreadExit += appContext_ThreadExit;
Application.Run(appContext);
}
place
private void appContext_ThreadExit(object sender, EventArgs e)
{
Application.Run(new MainForm());
}
This give me the error that you can't start a new message loop on the thread. So how do I go about properly executing this transition? Or am I already using the best method by allowing the SplashScreen to own the thread?
Here is what I came up with, but if anyone has a more elegant or proper way to do it feel free to let me know.
bool isLoading = true;
MainForm.OnLoad()
-> creates and runs SplashScreen
MainForm.OnShow()
-> if isLoading is true, re-hide
SplashScreen.LoadingComplete
-> event to signal completion of loading events
MainForm.SplashScreenLoadingComplete
-> handler sets isLoading to false
-> calls Show() this time form will show
I handle the loading in the SplashScreen with a background worker
Is there some equivalent of wpf's Application.MainWindow in compact framework?
The issue i have is that i run the SplashScreen, it does some work, and i need to close it and open the login page.
I try to just close the form and open another one, but it closes the entire application, as the SplashScreen is the one I pass to the Application.Run() method
I don't want to hide the form, because it causes problems when I want to hide my entire application and call another application from my code(the splash screen seems to be still there even though i do call the hide method), so i need to close the splash screen and open the login screen
You have a few options.
Don't pass your splash screen to Application.Run. Send in your main form, then have it create the instance of the splash and handle showing and hiding it. Really that screen (or any view for that matter) shouldn't be doing any "work" anyway - the work should be being done elsewhere and the UI should only react by displaying status, progress, validating user input etc.
Use two sequential calls to Application.Run:
i.e.
Application.Run(new SplashScreen());
Application.Run(new MainForm());
Set: App.xaml (property) -> Build Action -> "Page"
Set in App.xaml.cs Main():
[System.STAThreadAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public static void Main()
{
SplashScreen ss = new SplashScreen();
ss.showDialog();
App app = new App();
app.InitializeComponent();
app.Run();
}
Set App.xaml:
<Application
//something
StartupUri="MainWindow.xaml">
//something
</Application>
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 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();
I'm really new to Windows Forms programming and not quite sure what's the right way to go about programming.
This is my confusion.
I have a single form:
public partial class ReconcilerConsoleWindow : Form
{
public ReconcilerConsoleWindow()
{
InitializeComponent();
SetLogText("Started");
}
public void SetLogText(String text)
{
string logInfo = DateTime.Now.TimeOfDay.ToString() + ": " + text + Environment.NewLine;
tbx_Log.AppendText(logInfo);
}
}
And in my Program.cs class I have the following code:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ReconcilerConsoleWindow window = new ReconcilerConsoleWindow();
Application.Run(window);
if (CallSomeMethod() == true)
{
window.SetLogText("True");
}
}
}
Now, once the window has been displayed by the Application.Run command, the program halts at that point. How can I do further processing while the window is up?
The above is just an example. My purpose is to read an XMl file and display a datagridview. Subsequently, I watch the XMl file for changes and everytime a change is made, I want to refresh the datagridview. However, once the console pops up, how can i continue with my program and make changes to the information displayed on the form on the fly?
Processing that occurs after Application.Run is usually triggered in the form's Load event handler. You can easily create a Load method in Visual Studio by double clicking any open space on the form.
This would look something like this.
private void ReconcilerConsoleWindow_Load(object sender, EventArgs e)
{
if (CallSomeMethod())
{
this.SetLogText("True");
}
}
The reason this is (as stated in several other answers) is that the main thread (the one that called Application.Run(window)) is now taken up with operating the Message Pump for the form. You can continue running things on that thread through messaging, using the form's or forms' events. Or you can start a new thread. This could be done in the main method, before you call Application.Run(window), but most people would do this in Form_Load or the form constructor, to ensure the form is set up, etc. Once Application.Run returns, all forms are now closed.
Application.Run starts the Windows event handling loop. That loop won't finish til your form closes, at which time anything you do to it won't matter anyway.
If you want to do something with your form, do it in the form's Load event handler.
Program.cs is not meant to have business rules, it should only call your Form and display it. All datagrid loading/refreshing/editing should be done at your Forms. You should be using the Events defined on Forms class, like: OnLoad, OnUnload, OnClose and many others etc.
You are missing the concept. In a Windows Forms Application, your Main Thread is responsible for running the Form.
You can always use more Threads, but in Windows Forms I would recommend using a BackgroundWorker Component for parallel Tasks:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Or a Timer:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
Once Application.Run(window) is called, you'll want to handle subsequent things within the application window itself.
In the code view of the form, find the following (or add it)
private void ReconcilerConsoleWindow_Load(object sender, EventArgs e)
{
//this is where you do things :)
if (CallSomeMethod() == true)
{
window.SetLogText("True");
}
}