how to properly start and kill a form thread [duplicate] - c#

I want a splash screen to show while the application is loading. I have a form with a system tray control tied to it. I want the splash screen to display while this form loads, which takes a bit of time since it's accessing a web service API to populate some drop-downs. I also want to do some basic testing for dependencies before loading (that is, the web service is available, the configuration file is readable). As each phase of the startup goes, I want to update the splash screen with progress.
I have been reading a lot on threading, but I am getting lost on where this should be controlled from (the main() method?). I am also missing how Application.Run() works, is this where the threads for this should be created from? Now, if the form with the system tray control is the "living" form, should the splash come from there? Wouldn't it not load until the form is completed anyway?
I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :)

The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.
Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:
private void Form1_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
Thread.Sleep(3000); // Emulate hardwork
done = true;
Show();
}

Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.
Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
Override the "OnCreateSplashScreen" method like so:
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashForm();
this.SplashScreen.TopMost = true;
}
Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.
This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.
At the end of the day, it's all IL and bytecode anyway, so why not use it?

After looking all over Google and SO for solutions, this is my favorite:
http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen
FormSplash.cs:
public partial class FormSplash : Form
{
private static Thread _splashThread;
private static FormSplash _splashForm;
public FormSplash() {
InitializeComponent();
}
/// <summary>
/// Show the Splash Screen (Loading...)
/// </summary>
public static void ShowSplash()
{
if (_splashThread == null)
{
// show the form in a new thread
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// 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();
}
}
Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// splash screen, which is terminated in FormMain
FormSplash.ShowSplash();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// this is probably where your heavy lifting is:
Application.Run(new FormMain());
}
}
FormMain.cs
...
public FormMain()
{
InitializeComponent();
// bunch of database access, form loading, etc
// this is where you could do the heavy lifting of "loading" the app
PullDataFromDatabase();
DoLoadingWork();
// ready to go, now close the splash
FormSplash.CloseSplash();
}
I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.

This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.
Here is what I ultimately pieced together:
App.XAML:
<Application Startup="ApplicationStart" …
App.XAML.cs:
void ApplicationStart(object sender, StartupEventArgs e)
{
var thread = new Thread(() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
// call synchronous configuration process
// and declare/get reference to "main form"
thread.Abort();
mainForm.Show();
mainForm.Activate();
}

I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.
Quoting MSDN:
Activating a form brings it to the
front if this is the active
application, or it flashes the window
caption if this is not the active
application. The form must be visible
for this method to have any effect.
If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.

I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:
The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.
Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.
Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.

One simple way is the use something like this as main():
<STAThread()> Public Shared Sub Main()
splash = New frmSplash
splash.Show()
' Your startup code goes here...
UpdateSplashAndLogMessage("Startup part 1 done...")
' ... and more as needed...
splash.Hide()
Application.Run(myMainForm)
End Sub
When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:
Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().
If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.

I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest
Yet Another Splash Screen in C#

private void MainForm_Load(object sender, EventArgs e)
{
FormSplash splash = new FormSplash();
splash.Show();
splash.Update();
System.Threading.Thread.Sleep(3000);
splash.Hide();
}
I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.

I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.
private void formMain_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem(delegate
{
using (formSplash splashForm = new formSplash())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
}, null);
Thread.Sleep(2000);
done = true;
Show();
}

I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.
Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.
But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.
class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
this.SplashScreen = new Splash();
}
protected override void OnCreateMainForm()
{
this.MainForm = new Form1();
}
}
and
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
void Form1_Shown(object sender, EventArgs e)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Program.watch.Stop();
this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}
Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.

Actually mutlithreading here is not necessary.
Let your business logic generate an event whenever you want to update splash screen.
Then let your form update the splash screen accordingly in the method hooked to eventhandler.
To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.
This way you can have nice changing splash screen without any multithreading headache.
Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:
private void SomethingChanged(object sender, MyEventArgs e)
{
formSplash.Update(e);
Application.DoEvents(); //this will update any animation
}

Related

How to change NotifyIcon.Text in a running WinForm via command-line-arguments?

This is a tray-icon-only Windows Forms application. I'm trying to use argument to control something and change the text on the form for showing the status information.
But I found when I use argument to call it during it's running, the things I want to change are null (NotifyIcon() and MenuItem()), seems it ran a different application when I using arguments. I also tried Invoke() but there is no this definition in NotifyIcon().
Here is the code I wrote:
static void Main(string[] args)
{
if (args.Length > 0)
{
Arg_Call(args[0]);
}
if (new Mutex(true, "{XXX}").WaitOne(TimeSpan.Zero, true))
{
Init_Tray();
Application.Run();
}
}
private static NotifyIcon trayicon;
private static void Init_Tray()
{
trayicon = new NotifyIcon() { Icon = new Icon(#"D:\projects\Icon.ico"), Text = "Waiting", Visible = true };
trayicon.Visible = true;
Application.Run();
}
private static void Arg_Call(string args)
{
trayicon.Invoke((MethodInvoker)delegate {
trayicon.Text = "OK";
}); //from: https://stackoverflow.com/a/661662/8199423
}
Where am I wrong? How to and what is the best way to change the NotifyIcon.Text property in the running form via command-line-arguments?
I am sorry I was unable to adequately explain why your question is a duplicate of the existing "single-instance-application" questions. I will try to reiterate the train of thought here:
You wrote "How to and what is the best way to change the texts in the running form via command-line-arguments?"
Your requirement involves a currently-running process, which is presenting the NotifyIcon in the task tray, and the desire to use the command-line to modify that currently-running process's state.
It is a simple fact that when you type anything on the command line, it starts a whole new process. That process is necessarily different from the process that is already running, and which is presenting the NotifyIcon in the task tray.
Putting all of the above together, we have the conclusion that you want a new process that you start on the command line to interact with an existing process. And the simplest way to achieve that goal is to use the built-in single-instance-application support found in .NET. This is because the support for single-instance-applications includes automatic passing of the new command line arguments to the previous running program. Hence, the duplicate.
As I mentioned earlier, you should try to develop the skill to generalize and see how seemingly new problems are really just old problems in disguise and which you or someone else already knows how to solve.
In the same way that all problem solving can be summarized as "break the large problem down into smaller problems, repeat as necessary until all of the smaller problems are problems you already know how to solve", programming is very often not a matter of solving new problems, but rather of recognizing how your current problem is really a problem you already know how to solve.
All that said, I have the impression that you're still having difficulty figuring out how to apply that information to your specific scenario. So, perhaps this is an opportunity to illustrate the validity of the philosophy I espouse, by showing you how your seemingly different problem really is the problem I claim it is. :)
So, let's start with your original scenario. I am not using the code you posted, because it's mostly code that isn't needed. It seemed simpler to me to start from scratch. To do that, I wrote a little TrayManager class that encapsulates the actual NotifyIcon part of the functionality:
class TrayManager : IDisposable
{
private readonly NotifyIcon _notifyIcon;
public TrayManager()
{
_notifyIcon = new NotifyIcon
{
ContextMenu = new ContextMenu(new[]
{
new MenuItem("Exit", ContextMenu_Exit)
}),
Icon = Resources.TrayIcon,
Text = "Initial value",
Visible = true
};
}
public void Dispose()
{
Dispose(true);
}
public void SetToolTipText(string text)
{
_notifyIcon.Text = text;
}
protected virtual void Dispose(bool disposing)
{
_notifyIcon.Visible = false;
}
private void ContextMenu_Exit(object sender, EventArgs e)
{
Application.ExitThread();
}
~TrayManager()
{
Dispose(false);
}
}
The above hard-codes the context menu for the icon. Of course, it a real-world program, you'd probably want to decouple the menu from the above class, for greater flexibility.
The simplest way to use the above would look something like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (TrayManager trayManager = new TrayManager())
{
Application.Run();
}
}
}
So, how do we modify the above so that when you run the program again, you can change the Text property of the NotifyIcon with the command-line arguments you type? That's where the single-instance application comes in. As seen in the duplicate I marked earlier, What is the correct way to create a single-instance application?, one of the simplest ways to accomplish this is to use the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase class, which has built right in support for single-instance applications and a mechanism for delivering new command line arguments to the existing process.
The one little draw-back is that this class was designed for Winforms programs, with the assumption that there will be a main form. To use it will require creating a Form instance. For a program without the need for an actual form, this means creating a Form instance that is never shown, and making sure that it's never shown does require a little bit of finagling. Specifically:
class TrayOnlyApplication : WindowsFormsApplicationBase
{
public TrayOnlyApplication()
{
IsSingleInstance = true;
MainForm = new Form { ShowInTaskbar = false, WindowState = FormWindowState.Minimized };
// Default behavior for single-instance is to activate main form
// of original instance when second instance is run, which will show
// the window (i.e. reset Visible to true) and restore the window
// (i.e. reset WindowState to Normal). For a tray-only program,
// we need to force the dummy window to stay invisible.
MainForm.VisibleChanged += (s, e) => MainForm.Visible = false;
MainForm.Resize += (s, e) => MainForm.WindowState = FormWindowState.Minimized;
}
}
The only thing in the above that gives us the single-instance application behavior we want is the setting of IsSingleInstance = true;. Everything else is there just to satisfy the requirement that some Form object is present as the MainForm, without actually showing that object on the screen.
Having added the above class to the project, we can now "connect the dots". The new Program class looks like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (TrayManager trayManager = new TrayManager())
{
TrayOnlyApplication app = new TrayOnlyApplication();
app.StartupNextInstance += (s, e) => trayManager
.SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
app.Run(args);
}
}
}
You'll note two changes:
In addition to the TrayManager, which handles the NotifyIcon, we now also create the TrayOnlyApplication object, subscribing to its StartupNextInstance event so that we can receive the command line arguments given to any new instance, and use that to set the Text property of the NotifyIcon object (by passing that to the method created specifically for that purpose).
Instead of using Application.Run() to run the require message-pump loop to handle window messages, we use the Run() method our TrayOnlyApplication class inherited from the WindowsFormsApplicationBase class. Either of these methods handle message pumping while the program is running, and return control to the caller when the Application.ExitThread() method is called, so both approaches to message pumping work with the code in the TrayManager.
Now, the above example is simply a slight modification of the original version that didn't enforce single-instance operation. You might notice that it has the arguable deficiency that it always creates the tray icon, whether or not it's the first instance to run. Subsequent instances will run, create the tray icon, then immediately dismiss the icon and exit.
The WindowsFormsApplicationBase provides a mechanism to avoid this, the Startup event. While the StartupNextInstance event is raised in any instance of the application that is run when an instance already is running, the Startup event is raised only when no other instance is already running. I.e. in the instance where you actually want to do things, like show the tray icon.
We can take advantage of that event to defer creation of the NotifyIcon until we know whether we actually need it or not:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TrayManager trayManager = null;
TrayOnlyApplication app = new TrayOnlyApplication();
// Startup is raised only when no other instance of the
// program is already running.
app.Startup += (s, e) => trayManager = new TrayManager();
// StartNextInstance is run when the program if a
// previously -run instance is still running.
app.StartupNextInstance += (s, e) => trayManager
.SetToolTipText(e.CommandLine.Count > 0 ? e.CommandLine[0] : "<no value given>");
try
{
app.Run(args);
}
finally
{
trayManager?.Dispose();
}
}
}
Note that here, we need to write the try/finally explicitly instead of using the using statement, because the using statement requires initialization of the variable when it's declared, and we want to defer initialization until later, or never, depending on which instance is being run.
(Unfortunately, there's no way to defer creation of the dummy window in the TrayOnlyApplication class, since it's required just to call the Run() method, which requires a valid Form object be already set, and the determination as to which instance is being run happens in that call, not before.)
And that's all there is to it. The above shows, clearly I hope, exactly how the single-instance application techniques available to you directly solve the problem you are asking for help with. By providing a mechanism for a newly-run instance of your program to communicate the command line arguments passed to it, to the already-running instance of the same program, that newly-run instance can cause the already-running instance to perform whatever work it needs to (such as changing the tool-tip text for the tray icon, for example).
Naturally, any similar mechanism will achieve the same result. The only important thing is to have the newly-run instance detect an existing instance, and communicate with it. It just happens that the WindowsFormsApplicationBase class provides that functionality pre-made. There are lots of other ways to do the same thing, each with their own pros and cons.

Can a C# Form application run without Application.Run?

I have a library that I originally created in Java, but I want to rewrite it (and update it) in C#. I've found IKVM, and I might have to go that route. But my first choice would be to rewrite the library in C#.
The library is a teaching tool reminiscent of turtle graphics or Karel the Robot. So I need to be able to open a non-modal window while still sending commands from the Main method.
To mimic the original, I would like to use code similar to what is shown below to start the program since that would reinforce the typical way that objects are declared and instantiated. As the name implies, MyCustomForm would be a subclass of Form.
MyCustomForm myform = new MyCustomForm(...);
I've searched through the site, but haven't found any other question similar to mine.
Is this possible in C#?
Yes, there are several ways. The easiest would be to call Application.DoEvents periodically. Here's a short example of how you might write it.
static class Program {
static bool formClosed = false;
[STAThread]
static void Main() {
MyCustomForm form = new MyCustomForm();
form.Show();
form.FormClosed += Form_FormClosed;
while(!formClosed) {
Application.DoEvents();
Thread.Sleep(10);
}
}
private static void Form_FormClosed(object sender, FormClosedEventArgs e) {
formClosed = true; }
}
There exists much better solutions than this example, though. The term you want to Google for is "C# game loop" despite that your application is not a game.
No, it is not possible.
Application.Run() starts the event pump which allows your application to respond to events. This includes events that come from the user (e.g. clicks, drags, maximize, close) and events that come from the O/S or your own program (such a request to repaint the window, respond to timers, shut down when the O/S is being shut own). All windows forms applications require this sort of event pump in order to provide typical functionality.
Yes, just use ShowDialog() on your form, simple like that, then when your form closes call Application.Exit(). There is no need for Application.DoEvents();
In my case I'm using this to allow user to login again with another user.
So I can close my main form e then show user login form again

Form closing itself

I'm new to C# (switching from Java) and i'm having a bit of trouble understanding GUI in C#
Ill just paste the Code i have and let that explain the most part.
Main Class:
Frame frame;
keepRunning = true;
public GraphicsComponent()
{
frame = new Frame();
frame.Show();
}
public void run()
{
while (keepRunning)
{
Console.WriteLine("Running");
}
}
public static void Main()
{
GraphicsComponent gameComponent = new GraphicsComponent();
gameComponent.run();
}
using the frame.Show() method, it appears and disappears for a quick moment. but the "running" loop is run.
The other way i have seen is another method called ShowDialoge(), that one keeps the window open but blocks the rest of the code from running until it is closed.
The way i want to use it is basically how GUIs work in Java. Where i can reference an object (and it's components) as needed in the rest of my code.
Thanks heaps!
Edit: There is alot of confusion, so here is my Frame class:
public partial class Frame : Form
{
public Frame()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
A normal way to kick off a winforms application is by file->new winforms project. WHen you do this you get "Program.cs", which contains your Main method - the entry point into your application. From there you have code like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigForm());
}
In this example I kick off a new "ConfigForm"" that is a Form, and contains in it's ctor some code that looks like this:
public ConfigForm()
{
InitializeComponent();
// Other init work here
}
I create the form by right-clicking in the project and adding a new form.
You might want to look into the Application.Run method, see here. You can specify a "Main Form" which is going to be the main form that runs for the duration of the application, and when closed ends the application, or you can specify other options if you want. Perhaps the simplest solution is to start a main form, and since the form is live for the duration of the application, you can kick off other long-running code inside that form. If it's code that you want to be executing all the time performing some sort of background operation then you might want to fire it off in it's own thread. However, threading is hard so if you're new to c# / .net I'd try to stay away from threading unless you need it.

Run multiple UI Threads

Skip to the bottom for the question; this is just some extra info
I am using a component (GeckoFX) to render some websites, well fine, yet it can only be used in a Windows Form; as it has to bind to a WinForms object that can be drawn. Because all the WinForms are running in the same thread, I can only use one GeckoFX instance at a time; so I decided to create a 'worker class' in the form of a WinForm, and add all the logic in there. The form doesn't require to communicate with the main form.
Now I can fire up 10 windows, and they will eventually work, but every new form will wait before all the other forms have handled all their GeckoFX events, as you cannot use multiple instances on one thread. Furthermore, the browser has to be on a UIThread. So:
Is it possible to create multiple UI Threads (one for each form)?
I have seen someone doing it ([edit: removed 'bad' link]), yet no one ever got his code samples working. The guy who got it working originally used some form of custom message pumping to do this kind of things, but I have no clue how to achieve something like that.
I don't think that what you ask is really what you want but creating a message pump per thread is easy, you just have to call Application.Run once per thread.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Thread t1 = new Thread(Main_);
Thread t2 = new Thread(Main_);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
static void Main_()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Use Application.DoEvent().
or
Create multiply threading forms:
Thread form2Thread;
Form2 form2;
private void Form1_Load(object sender, EventArgs e)
{
form2Thread = new Thread(RunForm2);
form2Thread.SetApartmentState(ApartmentState.STA);
form2Thread.Name = "Form2 Thread"; // looks nice in Output window
form2Thread.Start();
}
public void RunForm2()
{
form2 = new Form2();
Application.Run(form2);
}
GeckoFx doesn't require a form.
GeckoWebBrowser wb = new GeckoWebBrowser();
wb.CreateControl(); //<-- the magic lays here!
wb.DocumentCompleted += delegate{ MessageBox.Show(wb.DocumentTitle); };
wb.Navigate("http://mysite.com");
Seems like it is possible.
I took backgrounder, opened TestApp, and created a new Form1 on thread/message pump #2:
private void button2_Click(object sender, EventArgs e) {
helper.Background(() => {
Form1 form2 = new Form1();
form2.Show();
});
}
The second window responds to mouse clicks etc.
Haven't actually verified if everything looks right, the freebie Visual Studio Express Edition I'm using is missing the "Threads" debug window, ahem. So I'm a bit in the dark. It seems to work, though. Let me know :-).

Multi-threaded splash screen in C#?

I want a splash screen to show while the application is loading. I have a form with a system tray control tied to it. I want the splash screen to display while this form loads, which takes a bit of time since it's accessing a web service API to populate some drop-downs. I also want to do some basic testing for dependencies before loading (that is, the web service is available, the configuration file is readable). As each phase of the startup goes, I want to update the splash screen with progress.
I have been reading a lot on threading, but I am getting lost on where this should be controlled from (the main() method?). I am also missing how Application.Run() works, is this where the threads for this should be created from? Now, if the form with the system tray control is the "living" form, should the splash come from there? Wouldn't it not load until the form is completed anyway?
I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :)
The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.
Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:
private void Form1_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
Thread.Sleep(3000); // Emulate hardwork
done = true;
Show();
}
Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.
Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
Override the "OnCreateSplashScreen" method like so:
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashForm();
this.SplashScreen.TopMost = true;
}
Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.
This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.
At the end of the day, it's all IL and bytecode anyway, so why not use it?
After looking all over Google and SO for solutions, this is my favorite:
http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen
FormSplash.cs:
public partial class FormSplash : Form
{
private static Thread _splashThread;
private static FormSplash _splashForm;
public FormSplash() {
InitializeComponent();
}
/// <summary>
/// Show the Splash Screen (Loading...)
/// </summary>
public static void ShowSplash()
{
if (_splashThread == null)
{
// show the form in a new thread
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// 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();
}
}
Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// splash screen, which is terminated in FormMain
FormSplash.ShowSplash();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// this is probably where your heavy lifting is:
Application.Run(new FormMain());
}
}
FormMain.cs
...
public FormMain()
{
InitializeComponent();
// bunch of database access, form loading, etc
// this is where you could do the heavy lifting of "loading" the app
PullDataFromDatabase();
DoLoadingWork();
// ready to go, now close the splash
FormSplash.CloseSplash();
}
I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.
This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.
Here is what I ultimately pieced together:
App.XAML:
<Application Startup="ApplicationStart" …
App.XAML.cs:
void ApplicationStart(object sender, StartupEventArgs e)
{
var thread = new Thread(() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
// call synchronous configuration process
// and declare/get reference to "main form"
thread.Abort();
mainForm.Show();
mainForm.Activate();
}
I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.
Quoting MSDN:
Activating a form brings it to the
front if this is the active
application, or it flashes the window
caption if this is not the active
application. The form must be visible
for this method to have any effect.
If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.
I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:
The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.
Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.
Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.
One simple way is the use something like this as main():
<STAThread()> Public Shared Sub Main()
splash = New frmSplash
splash.Show()
' Your startup code goes here...
UpdateSplashAndLogMessage("Startup part 1 done...")
' ... and more as needed...
splash.Hide()
Application.Run(myMainForm)
End Sub
When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:
Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().
If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.
I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest
Yet Another Splash Screen in C#
private void MainForm_Load(object sender, EventArgs e)
{
FormSplash splash = new FormSplash();
splash.Show();
splash.Update();
System.Threading.Thread.Sleep(3000);
splash.Hide();
}
I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.
I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.
private void formMain_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem(delegate
{
using (formSplash splashForm = new formSplash())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
}, null);
Thread.Sleep(2000);
done = true;
Show();
}
I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.
Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.
But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.
class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
this.SplashScreen = new Splash();
}
protected override void OnCreateMainForm()
{
this.MainForm = new Form1();
}
}
and
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
void Form1_Shown(object sender, EventArgs e)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Program.watch.Stop();
this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}
Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.
Actually mutlithreading here is not necessary.
Let your business logic generate an event whenever you want to update splash screen.
Then let your form update the splash screen accordingly in the method hooked to eventhandler.
To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.
This way you can have nice changing splash screen without any multithreading headache.
Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:
private void SomethingChanged(object sender, MyEventArgs e)
{
formSplash.Update(e);
Application.DoEvents(); //this will update any animation
}

Categories

Resources