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.
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 want to implement a help-form into my app, which can get the focus, even if a dialog is shown. At the moment i dispose the actual instance of my help if it can't get focused, but i dont think thats the right way. So i want to ask, if theres a option to show a form, seperated from the logic of my main-application.
Things i tried:
calling as a AppDomain (MSDN)
putting the help into seperate app and call it as a process
In both ways, the help(-form) can't get the focus back, when a dialog was called.
I dont want to use the help provided with C#, because i need to show the help(-pages) inside the application.
Thanks
PS: I'm using .Net 2.0.
You can do this by creating an STA thread and using Application.Run() to display the form from that separate thread. Application.Run() will create a separate Message Pump for the other form; this is what keeps it separate.
If you do that, you have to be VERY CAREFUL when communicating between the forms. You will need to use Control.Invoke() or some other inter-thread mechanism to call UI-changing methods on the second form from the first form (and vice-versa).
But if you do this, then the first form can be showing a modal dialog, and the second form will still be focusable.
Note that the second window may be behind the first window because there will be no way to specify the relative Z-order between them.
Showing the second form can be done like this:
private static void ShowIndependentForm()
{
Thread thread = new Thread(ShowIndependentFormImpl);
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}
private static void ShowIndependentFormImpl()
{
Application.Run(new Form2());
}
You can just call ShowIndependentForm() where appropriate; probably from the main form after you have created it, but my test code in Main() looks like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ShowIndependentForm();
Application.Run(new Form1());
}
Important
Because the second form has its own message pump, closing the first form will NOT close the program unless you set Thread.IsBackground to true. If you don't, you will have to explicitly close the second form (via calling a method in the second form using Control.Invoke() or some other way) when the first form closes if you want the program to close automatically.
I am working on a C# WinForms app, and I want it to be able to pop open a non-modal dialog which will get redrawn and be interactive at the same time as the main window/form.
The main window is using a SerialPort and displaying data transfer counts, which are continually increasing via a SerialDataReceivedEventHandler.
I can use ShowDialog() which seems to work in a modal fashion, but the main window data counters freeze while the dialog is in use, and I think eventually the serial buffers are overrun.
I think I want to use Show(), but if I do this the dialog appears on screen half-drawn, then is not drawn or interactive any more (gets trashed if I drag another window across it). It stays onscreen until I close the main app window.
Perhaps I should be starting another thread or, likely, am just doing something wrong. I don't usually do C# or Windows programming (maybe you can tell.)
Edit after comments (thanks, commenters):
I think maybe most things are getting run under whatever thread the serial receive event handler gets called under. When starting up my app I create a class to handle the serial, which includes:
com.DataReceived += new SerialDataReceivedEventHandler(SerialRxHandler);
The only code that I have written to care about threads is some functions to update the counters and log listbox, which I found had to be wrapped with some InvokeRequired to stop me getting complaints about thread switching:
delegate void SetCountDelegate(TextBox tb, int count);
internal void SetCount(TextBox tb, int count) {
// thread switch magic
if (InvokeRequired) {
BeginInvoke(new SetCountDelegate(SetCount), new object[] { tb, count });
} else {
tb.Text = String.Format("{0}", count);
}
}
So maybe I shouldn't be trying to Show() a form on this thread. Another InvokeRequired block, or should I be doing things completely differently?
Suppose I'll answer myself..
Prompted by commenters and a bit of thinking, I did an Invoke() to switch to the UI thread before trying to create and Show() my child dialog. That worked.
I have encountered an odd issue with the way I am showing a splash form, that causes an InvalidAsynchronousStateException to be thrown.
First of all, here is the code for Main{} where I start the splash form:
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.Start();
Application.Run(new MainForm());
}
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
Application.Run(splash);
}
}
I am using .NET2.0, with Win XP.
During some testing where the app was left running for may hours, I noticed that the number of exceptions would occasionally increase by one or two.
(Numbers obtained by PerfMon, viewing '# of Exceps Thrown' counter.) These exceptions seem to be caught and swallowed by the runtime, because they do
not ripple up and cause anything to go wrong in the app itself. At least nothing that I can determine anyway.
I have discovered that the exception is thrown when the UserPreferenceChanged event is fired by the system. Since finding this out, I can generate the exception
at will by changing the background picture or screen saver, etc.
I am not explicitly subscribing to this event myself anywhere in code, but I understand (via the power of Google) that all top level controls and forms subscribe
to this event automatically.
I still have not determined why this event is being fired in the first place, as it appears to happen while the app is running over night, but I guess that is another mystery to be solved.
Now, if I stop the splash form thread from running, the exception disappears. Run the thread, it comes back. So, it appears that something is not unsubscribing from the event, and this is causing the subsequent exception perhaps?
Interestingly, if I substitute my splash form with a default, out of the box Form, the problem still remains:
static void ShowSplash()
{
using (Form splash = new Form())
{
Application.Run(splash);
}
}
While this form is being displayed, any UserPreferenceChanged events do not cause any exceptions. As soon as the form is closed, and the thread exits, exceptions will be thrown.
Further research has lead me to this Microsoft article, that contains the following comment:
Common causes are a splash screens
created on a secondary UI thread or
any controls created on worker
threads.
Hmm, guilty as charged by the looks of it. Note that my app is not freezing or doing anything untoward though.
At the moment, this is more of a curiosity than anything else, but I am conecerned that there may be some hidden nasties here waiting to bite in the future.
To me, it looks like the form or the message pump started by Application.Run is not cleaning up properly when it terminates.
Any thoughts?
Yes, you are running afoul with the SystemEvents class. That class creates a hidden window that listens to system events. Particularly the UserPreferenceChanged event, a lot of controls use that event to know when they need to repaint themselves because the system colors were changed.
Problem is, the initialization code that creates the window is very sensitive to the apartment state of the thread that calls it. Which in your case is wrong, you didn't call Thread.SetApartmentState() to switch to STA. That's very important for threads that display a UI.
Beware that your workaround isn't actually a fix, the system events will be raised on the wrong thread. Your splash thread instead of the UI thread of your program. You'll still get random and extremely hard to diagnose failure when an actual system event gets fired. Most infamously when the user locks the workstation, the program deadlocks when it is unlocked again.
I think calling Thread.SetApartmentState() should fix your problem. Not 100% sure, these UI thread interactions are very difficult to analyze and I haven't gotten this wrong yet. Note that .NET already has very solid support for splash screens. It definitely gets details like this right.
I was able to simulate your problem and I can offer a work around, but there might be a better option out there since this was the first time I had come across this.
One option to avoid the exception is to not actuall close the splash screen, but rather just hide it. Something like this
public partial class SplashForm : Form
{
public SplashForm()
{
InitializeComponent();
}
// Not shown here, this is wired to the FormClosing event!!!
private void SplashForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
this.Hide();
}
}
Then it will be important that you make the thread that you run the splash screen on a background thread to ensure that the application is not kept alive by the splash screen thread. So your code would look something like this
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.IsBackground = true;
splash.Start();
Application.Run(new MainForm());
}
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
Application.Run(splash);
}
}
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.