I have a C# application which runs into trouble when it comes to multi-threads / backgroundworkers when I'm using a splash screen before I load the main window.
my code look something like this:
[STAThread]
private static void Main()
{
.. do some stuff
ShowSplash(); // where i show a splash screen and load some stuff
...
As the last step of ShowSplash, I do the following:
new MyCabApplication<MyMainWorkItem, MDIParentForm>().Run(); -- where i load the form through cab.
The problem is that when I do that I get the following exception:
Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead
Any idea what can I do?
Here is my showsplash function:
private static DialogResult ShowSplash(AutoResetEvent controller)
{
// create and register splash screen
splashScreen = new PointSplashScreen();
Application.Run(splashScreen);
return DialogResult.OK;
}
Two solutions:
Instead of using Application.Run, just create a new instance of the form and then call ShowDialog. Move new MyCabApplication<MyMainWorkItem, MDIParentForm>().Run(); outside of the Splash Screen after the call to ShowDialog(). You can check properties of the Splash screen if this code should not always be run.
Instead of using Application.Run(Form), use Application.Run(ApplicationContext). You will need to create a new ApplicationContext and move your code there.
Solution 1 is easier.
It sounds like MyCabApplication extends Application. The Run method starts a WinForm application by starting a message loop that handles window messages.
Because you are already showing UI, there is already a message loop running, so you cannot start another. To get your main form to show up, make a new instance of it and call Show():
var form = new MainForm();
form.Show();
Related
I have an odd question which I haven't been able to find a solution to.
Basically, I have a console application. It takes commands, which I determine actions from. All of the actions involve opening a form and displaying something.
Currently, I'm able to enter a command, it correctly determines the action and pops up a form and everything is okay.
Unfortunately, in doing so, the console in the background behind the form essentially freezes (I've realised it just isn't updating the UI anymore) - so if I try and type another command while there's a form open, nothing will happen until I close the form.
I've been looking into creating a new thread for the form to run in which I guess is the path I need to take - but I can't figure out how to do that while not causing errors (e.g. that pesky illegal access from a separate thread error).
Can anyone help?
Should I cave and just set up a form from the get-go and make my commands be entered via a textbox on a form? (seems annoying, but at least I then have a form that can invoke other things)
You can open forms from a console application in another thread like this:
var t = new Thread(() => {
var f = new Form();
f.ShowDialog();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
I am trying to create a simple c# application (my first attempt at c# so please be kind). I've created a form with a textbox to capture an "auth code", which is then validated and then a webclient fetches an xml file passing this auth code in to the request. The data sent back is parsed e.c.t.
What i want to do is once the xml comes back and ive done my checks to valid it is all fine. I want to close the first form and load up a second form where i will programmatically add the form components needed to display the xml data in a pretty format.
My problem is that im unable to get the second form to stay open (im no doubt invoking the second form in the wrong manner). Here's what i have:
// close current form
this.Close();
//open new form
xmlViewForm xmlView = new xmlViewForm();
xmlView.Show();
I'm sure you've spotted the mistake im making by now. but just to state the obvious for the sake of completeness, it closes the first form, opens the second, and then immediately the program exits (the second form flashes up for a second obviously).
I've tried a few things but none of them work (including using Application.Run(new xmlViewForm()); instead of instantiating the class and using the show() method. Obviously you know that doesn't work, and now i do too, although i dont understand c# even remotely enough to work out why.
Thanks for any help :)
The first thing that came to mind is that you are closing the form that you opened by calling Application.Run(new MyForm()) or something similar. This form has special significance; it is the "main form" of the application, and when closed, it signals to the application that the user wants to close the entire program, no matter how many other windows are open.
There are two possible fixes. First, and easiest, is simply to Hide() the form you don't want visible instead of calling Close() on it. Though invisible, it's still running, so the application doesn't close.
The second solution is to define a custom "application context" that should be run instead of the "default context" that is created by specifying a main form to watch. You do this by deriving a custom class from System.Windows.Forms.ApplicationContext. With this context specified, you can use it to control termination of the application based on something other than closure of the main form. Example code that launches two "main forms" and keeps track of whether both are still active can be found at the MSDN page for the class. You can do something similar by specifying Load and Close handlers for the main form, then passing them to the child form when the main form instantiates it, thus keeping a count of "open" forms, and closing out the full application when that number is zero. Just make sure the child form loads before closing the main form by calling childForm.Show() before this.Close().
You can not open the second form after closing the main form.
Do this:
//open new form
xmlViewForm xmlView = new xmlViewForm();
xmlView.Show();
// hide current form
this.Hide();
Main form can not be closed because it's the parent form. The child form will never show up if you close the main form.
Or change the xmlViewForm to main form by editing Program.cs file
Application.Run(new XmlViewForm());
Then you can easily call the other form first at the time of loading and close it as you please:
private void XmlViewForm_Load(o, s)
{
// hide current form, and this will remain hidden until the other form is done with it's work
this.Hide();
//open the other form
TheOtherForm _theOtherForm = new TheOtherForm();
_theOtherForm.Show();
}
private void TheOtherForm_Closed(o, s)
{
// show current form
this.Show;
}
In my application it takes some time to loan my initial screen(1-2 mins). Since it has so many controls to be filled by Database.
So I need to have a splash screen, it will load(probably with a progress bar) and stays while main form loads. Means in background I need to load main form (better with out showing)
Only main window finishes loading, it notifies to splash, the splash will go off and main will be visible.
I tried to achieve above with several way but no success.
Any one can help me ?
look at this Splash screen
and this Splash screen class
FormSplash splash = new FormSplash();
this.BeginInvoke(
new MethodInvoker(
() =>
{
splash.Show();
}
)
);
// main form code here
// at end of loading code
splash.Close();
The above code belongs in Form_Load of the main form.
There are a couple of good approaches discussed at Show a splash screen at once (in fact to me this and the other question appear to be duplicates).
The best way and using the API is
SplashScreen splash = new SplashScreen("splashscreen.jpg");
splash.Show(false);
splash.Close(TimeSpan.FromMilliseconds(2));
InitializeComponent();
I everyone. I currently have a problem with my focus when using a splash screen. I am using VS2008, with .NET framework 2.0. Also, I have linked my project with the VisualBasic.dll since I use the ApplicationServices to manage my single instance app and splash screen.
Here is a code snippet simplified of what I tried debugging.
namespace MyProject
{
public class Bootstrap
{
/// <summary>
/// Main entry point of the application. It creates a default
/// Configuration bean and then creates and show the MDI
/// Container.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// Creates a new App that manages the Single Instance background work
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
App myApp = new App();
myApp.Run(args);
}
}
public class App : WindowsFormsApplicationBase
{
public App()
: base()
{
// Make this a single-instance application
this.IsSingleInstance = true;
this.EnableVisualStyles = true;
// There are some other things available in the VB application model, for
// instance the shutdown style:
this.ShutdownStyle = Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses;
// Add StartupNextInstance handler
this.StartupNextInstance += new StartupNextInstanceEventHandler(this.SIApp_StartupNextInstance);
}
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new MyMainForm();
this.SplashScreen.StartPosition = FormStartPosition.CenterScreen;
}
protected override void OnCreateMainForm()
{
// Do your initialization here
//...
System.Threading.Thread.Sleep(5000); // Test
// Then create the main form, the splash screen will automatically close
this.MainForm = new Form1();
}
/// <summary>
/// This is called for additional instances. The application model will call this
/// function, and terminate the additional instance when this returns.
/// </summary>
/// <param name="eventArgs"></param>
protected void SIApp_StartupNextInstance(object sender,
StartupNextInstanceEventArgs eventArgs)
{
// Copy the arguments to a string array
string[] args = new string[eventArgs.CommandLine.Count];
eventArgs.CommandLine.CopyTo(args, 0);
// Create an argument array for the Invoke method
object[] parameters = new object[2];
parameters[0] = this.MainForm;
parameters[1] = args;
// Need to use invoke to b/c this is being called from another thread.
this.MainForm.Invoke(new MyMainForm.ProcessParametersDelegate(
((MyMainForm)this.MainForm).ProcessParameters),
parameters);
}
}
}
Now, what happens is that, when I start the application, the Splash Screen shows as expected, but when it is destroyed, it does not return the focus to the main form (Form1 in the test). The MainForm simply flashes orange in the taskbar. If I launch the application from the IDE (VS2008), focus works just fine. I am using XP Pro. Also, the main form is not on top of every other windows. If I comment out the OnCreateSplashScreen() method, the application gets focus normally.
To test normal execution, I am using the VS Command Prompt to launch my application. I am using the Release version of my project.
Any ideas?
Edit:
I also handle the StartUpNextInstance event to send my command-line arguments to my main form. For test purposes, it was removed here.
Edit: Added a bit more code. Now you have the full extent of my bootstrap.
The question is not in detail.
1) What the is the relationship between the SplashScreen and the main form of the application?
2) How does SplashScreen automatically close?
I'm sure the problem here is that the main form had already started loading up in the background while SplashScreen is yet to close. Due to bad timing, the main form loads up in the background and the SplashScreen unloads... hence the flash in the taskbar.
Make them appear in serial controlled manner. There are many ways. I cannot suggest exactly how since hardly any detail has been provided. Like what is VB doing in the project, how many threads are working, what are the custom forms used here.. etc.
EDIT:
Algorithm to implement Splash screen (IMHO) :)
1) Create a custom form - splash screen
2) Run it on a separate thread. Implement it's behaviour as you like.
3) In your splash screen form, write a handler to capture a custom unload event handler which closes the splash screen form.
4) Now, back in the main thread, create you main app form. Set its Visible property to false.
5) Write even handler of the main form's Load event. In this handler, fire an event to splash screen to unload. Then, make the main form visible.
You need to call the Activate() method on your main form in order to display it properly.
In some of my implementations of splash forms on a different thread, I've seen this situation as well.
usually if I call this.Activate(); as my first line of code in the form load it tends to work as expected.
putting this after hiding the splash usually doesnt work correctly.
other methods that can help.
this.BringToFront();
this.TopMost = true; //setting this to true and back to false sometimes can help
What if you set the focus explicitly in your Form1 Load event?
For some obscure reason, focus seems to be working correctly. I am going to try and incorporate more of my normal execution of my main program into the MyMainForm and try to find what really causes the focus change to fail. I will keep you all posted.
try calling form1.show() after this.MainForm = new Form1(); It worked for me.
I had the same issue and wanted easy solution for this.
I tried above but no luck, but ideas from above useful information.
I tried my trick and it works for me.
This is my Programs.cs code which works fine:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainWindow = new frmMain();
splashScreen.DisplaySplashScreen(mainWindow, 10000);
**mainWindow.TopMost = true;**
Application.Run(mainWindow);
}
}
Notice the mainWindow.TopMost = true;
Ok that was working enough but I did not want the mainWindow to stay on top.
So in Shown event i added this.TopMost = false;
private void frmMain_Shown(object sender, EventArgs e)
{
this.TopMost = false;
}
Now my application is working as I wanted it to work.
Note: Just posted to help any other user who face the same issue what I faced.
Happy Coding :)
in you main form's load event add
this.Activate();
I am showing a splash form by starting a new thread immediately before running my main form.
In the method that is run by this thread, I am using Application.Run as shown in Option 1 below. Is this a correct way of doing this, or are there problems waiting for me becaue I have called Application.Run twice? An alternative is Option 2, also shown below where I call .ShowDialog() to display the form.
The splash form itself closes after a specified time, controlled within the form itself, and both options appear to work well.
So my question is: Which is preferred - Option 1 or Option 2? If you could give specific reasons for one or the other that would be great.
Thanks.
Snippet of Main:
// Run splash screen thread.
Thread splash = new Thread(new ThreadStart(ShowSplash));
splash.Start();
// Run main application.
Application.Run(new MainForm());
Show splash form option 1:
static void ShowSplash()
{
Application.Run(new SplashForm());
}
Show splash form option 2:
static void ShowSplash()
{
using (SplashForm splash = new SplashForm())
{
splash.ShowDialog();
}
}
Option 2 will probably run into trouble because then you are using the same Mesageloop as the MainForm but from another thread.
Option 1 is fine.
I realize that this may be an unusual viewpoint but have you considered not using a Splash screen and instead showing the information on the 'welcome page' or 'help > about' screen instead?
There are a handful of reasons to do this:
Unless you get into multi-threading, a Splash Screen may not repaint properly if some alert/msgbox pops up over the top of it, negating the benefit of the splash screen entirely.
Splash screens that show 'you have plugins x, y and z' installed can't really tell this until that information has been loaded up. By the time this info is loaded, your app is ready to go, so you'll either close the splash screen or it'll be in the way of the user.
If I look away and miss the splash screen, I'll miss whatever information you're telling me. If 'License expires in 3 days' is part of that information, and today is Friday, that means I won't realise that on Monday, I can't use the app. Obscure, but I've seen it.