I am using the following code to apply a delay in my form load method before it becomes visible (after a splash screen is shown). I have defined my form load as async:
private async void MainForm_Load(object sender, EventArgs e)
And this is my delay function at the end of MainForm_Load:
Task startTimer = Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
});
await startTimer;
splash.Close();
this.Visible = true;
But my delay function is not working, my splash screen is immediately closed and my form becomes visible. What am I doing wrong?
Update:
Here is the code for my form load. But again I have removed some blocks from it so I won't give you the headache:
private async void MainForm_Load(object sender, EventArgs e)
{
this.Visible = false;
// Check license
// Load two user controls
Splash splash = new Splash();
splash.Show();
RefreshPostbagFolder();
InitiateGeneralSettings();
InitiateRunSelectFile();
InitiateRunSelectManualCampaignType();
InitiateImageList();
RefreshManageTab();
RefreshProgramLog();
RefreshServiceLog();
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
//await startTimer;
splash.Close();
this.Visible = true;
this.BringToFront();
}
This is interesting, and I currently don't have an explanation for it. I'm sure there's more to how WinForms works than simply setting these properties. But this basic example does replicate the issue:
private async void Form1_Load(object sender, EventArgs e)
{
this.Hide();
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
this.Show();
}
Stepping through the code in the debugger, what's actually happening isn't that the rest of the code isn't being awaited, but that the Hide() isn't doing what we think. The form isn't actually displayed until after Form1_Load executes. Since it executes asynchronously, the internal components which serve to display the form are able to execute during that await. But that's too late to hide the form.
This shows promise:
private async void Form1_Load(object sender, EventArgs e)
{
BeginInvoke(new MethodInvoker(delegate
{
this.Hide();
}));
await Task.Factory.StartNew(() => { Thread.Sleep(5000); });
this.Show();
}
What this does is invoke Hide() after the components have shown the form. However, the form is still shown for a fraction of a second. So it's not ideal.
It's been a long time since I've done anything with WinForms (and even then I didn't do much), so I'm not sure how else to load a form without it being shown. But this at least gets to the issue of why it's not "awaiting" in your case. It is awaiting, but during that await is when the form is initially shown. So hiding the form before that point has no effect.
Your main form gets automatically shown and activated inside Application.Run here, note applicationContext.MainForm.Visible = true.
It becomes visible as soon as the execution point returns from Form.Load event handler to the code which fired the event (in your case, that's where it hits the await inside MainForm_Load), so the asynchronous part doesn't affect its visibility.
To avoid flickering, you can initially show the form as minimized and without the taskbar icon, like this:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form {
WindowState = FormWindowState.Minimized,
ShowInTaskbar = false };
form.Shown += delegate
{
Debug.Print("form.Shown");
};
form.Load += async delegate
{
Debug.Print("form.Load");
var splashForm = new Form { Text = "Splash!" };
splashForm.ShowInTaskbar = false;
splashForm.Show();
await Task.Delay(5000);
splashForm.Hide();
form.WindowState = FormWindowState.Normal;
form.ShowInTaskbar = true;
form.Show();
};
Application.Run(form);
}
}
}
Instead of modifying Program.Main, you can initially set Form.WindowState and Form.ShowInTaskbar in the VS form designer or your MainForm's constructor.
On a side note, use Task.Delay instead of Thread.Sleep wrapped with Task.Run or Task.Factory.StartNew.
Related
I am in the process of creating a POS system using C# and WinForms.
I am using a form with some text and an image to indicate when a long running process is performed, like sales printing and DB update after the sale. But when I do that, only the AjaxLoader form is showing and it's not calling the update functions below it.
This is my code.
public void completeSale()//invoked on Sell button
{
loader = new AjaxLoader();//this is a form
loader.label1.Text = "Printing...";
ThreadStart threadStart = new ThreadStart(Execution);
Thread thread = new Thread(threadStart);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private void Execution()
{
this.Invoke((MethodInvoker)delegate { loader.ShowDialog(this); });
Application.DoEvents();
update_sale("Sold");//method not getting called at all
this.Invoke((MethodInvoker)delegate { loader.Dispose(); });
}
This is my Ajax loader form that I need to display, that is supposed to block my POS form. So upon finishing the printing (doing background task) I need to close the loader.
The problem is that the lines
Application.DoEvents();
update_sale("Sold");//method not getting called at all
is never reached.
What am I doing wrong?
The .ShowDialog() on a form is a blocking call, so your code will wait until the form that is shown as dialog is .Closed()
I would also recommend using using async Task as this makes working with Threads much much easier!
I've changed your code to show this.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await completeSale();
}
AjaxLoader loader = null;
public async Task completeSale()//invoked on Sell button
{
//for info, this is how I set up AjaxLoader form properties in the designer.
loader = new AjaxLoader();
loader.label1.Text = "Printing...";
loader.TopMost = true;
loader.WindowState = FormWindowState.Normal;
loader.StartPosition = FormStartPosition.CenterParent;
loader.ShowInTaskbar = false;
loader.ControlBox = false;
loader.FormBorderStyle = FormBorderStyle.None;
//loader.PointToClient(this.DesktopLocation);
await Execution();
}
private async Task Execution()
{
if (loader.InvokeRequired)
this.Invoke((MethodInvoker)delegate { loader.Show(this); });
else
loader.Show(this);
//Application.DoEvents();
await update_sale("Sold");
if (loader.InvokeRequired)
this.Invoke((MethodInvoker)delegate { loader.Close(); });
else
loader.Close();
}
private async Task update_sale(string v)
{
//long running process like printing etc..
await Task.Delay(3000);
}
}
this will do something like this:
On the AjaxLoader form I added a progress bar that is set to style = Marquee
I'm trying to update a status message while waiting for a web response to be returned. The call posts files to a server and sometimes it can take 30+ seconds.
I want to update the message (windows form textbox text) if the call is taking longer than expected. If the call has been waiting for 15 seconds, update the message to "This is taking awhile but should complete soon."
I've tried:
async fire and forget
timer using invoke
task.run
both tasks as async, awaiting the web calling Tasks
Background Worker using dowork and progress work
Nothing seems to work. Is it even possible to update the main thread while a task has a thread locked up?
I'm testing with simple calls:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
Task.Delay(1000).Wait();
this.Invoke((MethodInvoker)delegate
{
this.box.Text += '.';
this.box.Update();
});
}
}
private void MakeCall()
{
worker.RunWorkerAsync();
using (WebClient client = new WebClient())
{
//Just runs Task.Delay(10000) then returns "Complete"
var res = client.DownloadString("https://localhost:44343/api/TestDelay");
MessageBox.Show(res);
}
worker.CancelAsync();
}
private void button_Click(object sender, EventArgs e)
{
MakeCall();
}
I think I see your problem. You are downloading on the UI thread without ever getting off of it, so the background worker can never get on it either.
Try this code out:
EDIT: Using two Tasks rather than a background worker
private void MakeCall()
{
// it'd be a good idea to disable the button here
ManualResetEventSlim waiter = new ManualResetEventSlim(false);
Task.Run(async () =>
{
while(!waiter.IsSet)
{
await Task.Delay(1000);
this.Invoke((MethodInvoker)delegate
{
this.box.Text += '.';
this.box.Update();
});
}
});
Task.Run(() =>
{
using (WebClient client = new WebClient())
{
var res = client.DownloadString("https://localhost:44343/api/TestDelay");
MessageBox.Show(res);
}
waiter.Set();
// hop back on the UI thread and re-enable your button here
});
}
private void button_Click(object sender, EventArgs e)
{
MakeCall();
}
I'm providing an additional answer here because although the answer by outbred worked in my test, it didnt work in my original program because the form needed to remain open, locking the main form, run one of the defined tasks, then close automatically. That meant the async fire and forget method wasn't an option.
What I did was overloaded the forms ShowDialog method to take an async action, trigger it, show the dialog, then close the dialog on complete.
This method works perfectly, locking the parent and allowing the background worker to update the text.
internal DialogResult ShowDialog(Action action) => ShowDialog(async () => await Task.Run(action));
internal DialogResult ShowDialog(Func<Task> action)
{
action.Invoke().ContinueWith(task => this.DialogResult = DialogResult.OK);
return this.ShowDialog();
}
Then you can call it using one of the following ways:
using (Form1 form = new Form1())
form.ShowDialog(form.MakeCall);
using (Form1 form = new Form1())
form.ShowDialog(() => { form.MakeCall("HelloWorld");} );
using (Form1 form = new Form1())
form.ShowDialog(async () => { await form.MakeCallAsync("HelloWorld");} );
It will display the form as a dialog (locking the parent), run the task to completion, then close the form.
the Form.ShowDialog() method causes the code to be halted until the newly called form is closed. I need the code to continue running after the ShowDialog() method is called. I googled and read about using backgroundworker? But that is the first time i have heard of that and never used it before.
Form2 form2this = new Form2();
form2this.ShowDialog();
MessageBox.Show("Something");
This code gets executed after clicking a button, how can i still call ShowDialog to prevent the user from interacting with the main form but still allowing the main form to continue with its work?
Sorry if its been discussed but everything i found seems extremely difficult to perform such a simple task. I am actually surprised its not included in the SHowDialog method. for instance ShowDialog().Continue would be cool.
If you just want the code to continue on instead of blocking until the popup is closed consider using Show instead of ShowDialog.
If you have some action that you want to have the parent form doing while the child form is up, then yes, it could be appropriate to use a BackgroundWorker (or just manually starting a new Thread/Task). It would be helpful to know more about what that task is though. If you need to interact with the main form, or the child form, then that seems like trouble to me; if you just need to do some background task with no UI interaction then this is the right line of thought.
Another possibility is that what you want to do really just should be something done in the child form, rather than the parent form.
As long as you do asynchronous operations during the time that the modal dialog is opened, you can do it as simply as shown below, assuming button1_Click() is the event handler for a button.
private async void button1_Click(object sender, EventArgs e)
{
// create and display modal form
Form2 modalForm = new Form2();
BeginInvoke((Action)(() => modalForm.ShowDialog()));
// do your async background operation
await DoSomethingAsync();
// close the modal form
modalForm.Close();
}
private async Task DoSomethingAsync()
{
// example of some async operation....could be anything
await Task.Delay(10000);
}
I found that when I used the solution that suggested to use Show(), I could end up in cases where the dialog I wanted to be modal would end up behind the main form, after switching back and forth between apps. That never happens when I use the solution above.
This is my way, so ugly but i have no better idea.
private void AppUiMain_Shown(object sender, EventArgs e)
{
var loading = new AppUiLoading();
loading.Shown += (o, args) =>
{
bool isLoading = true;
loading.Top = (int)(loading.Top * 1.16);
Application.DoEvents();//refresh ui
EventHandler ehr = null;
EventHandler ehe = null;
ehr = (ss, ee) =>
{
App.Instance.Ready -= ehr;
App.Instance.Error -= ehe;
isLoading = false;
};
ehe = (ss, ee) =>
{
loading.Text = "Error";
loading.ShowAbortButton("Error occur");
};
App.Instance.Error += ehe;
App.Instance.Ready += ehr;
InitApp();
//HACK: find a better way to `refresh' main form
Application.DoEvents();
this.Height++;
this.Height--;
//HACK: find a better way to keep message looping on ShowDialog
while (isLoading)
Application.DoEvents();
loading.Close();
};
loading.ShowDialog(this);
}
To continue code execution without closing modal dialog WindowsFormsSynchronizationContext.Current.Post(-=> {"Your code"}, null); can be used. Here you can find more detail -
http://newapputil.blogspot.in/2015/05/continue-executing-code-after-calling.html
Run an async call to show modal. Here an example in wpf:
private Window waitView;
/// <summary>
/// Closes a displayed WaitView from code.
/// </summary>
public void CloseWaitView()
{
if(waitView != null)
{
// Work on the gui Thread of waitView.
waitView.Dispatcher.Invoke(new Action(() => close()));
}
}
/// <summary>
/// Closes a displayed WaitView and releases waitView-Instance.
/// </summary>
private void close()
{
waitView.Close();
waitView = null;
}
/// <summary>
/// Showes a modal WaitView (Window).
/// </summary>
public void ShowWaitView()
{
// instance a new WaitViewWindow --> your Window extends Window-Class
waitView = new WaitViewWindow();
// prepare a operation to call it async --> your ShowDialog-call
var asyncCall = new Action(() => waitView.Dispatcher.Invoke(
new Action(() => waitView.ShowDialog())
));
// call the operation async
// Argument 1 ar:
// ar means IAsyncResult (what should be done, when come back from ShowDialog -->
// remove view memory with set waitView to null or ... dispose
// the second argument is an custom parameter you can set to use in ar.AsyncState
asyncCall.BeginInvoke(ar => waitView = null, null);
// all from here is done during ShowDialog ...
}
I suppose next solution for async ShowDialog:
public bool DialogResultAsync
{
get;
private set;
}
public async Task<bool> ShowDialogAsync()
{
var cts = new CancellationTokenSource();
// Attach token cancellation on form closing.
Closed += (object sender, EventArgs e) =>
{
cts.Cancel();
};
Show(); // Show message without GUI freezing.
try
{
// await for user button click.
await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (TaskCanceledException)
{ }
}
public void ButtonOkClick()
{
DialogResultAsync = true;
Close();
}
public void ButtonCancelClick()
{
DialogResultAsync = false;
Close();
}
And in main form you must use this code:
public async void ShowDialogAsyncSample()
{
var msg = new Message();
if (await msg.ShowDialogAsync())
{
// Now you can use DialogResultAsync as you need.
System.Diagnostics.Debug.Write(msg.DialogResultAsync);
}
}
I need to show splash screen on my application start for few seconds. Does anybody know how to implement this?
Will be much appreciate for the help.
First, create your splash screen as a borderless, immovable form with your image on it, set to initially display at the center of the screen, colored the way you want. All of this can be set from within the designer; specifically, you want to:
Set the form's ControlBox, MaximizeBox, MinimizeBox and ShowIcon properties to "False"
Set the StartPosition property to "CenterScreen"
Set the FormBorderStyle property to "None"
Set the form's MinimumSize and MaximumSize to be the same as its initial Size.
Then, you need to decide where to show it and where to dismiss it. These two tasks need to occur on opposite sides of the main startup logic of your program. This could be in your application's main() routine, or possibly in your main application form's Load handler; wherever you're creating large expensive objects, reading settings from the hard drive, and generally taking a long time to do stuff behind the scenes before the main application screen displays.
Then, all you have to do is create an instance of your form, Show() it, and keep a reference to it while you do your startup initialization. Once your main form has loaded, Close() it.
If your splash screen will have an animated image on it, the window will need to be "double-buffered" as well, and you will need to be absolutely sure that all initialization logic happens outside the GUI thread (meaning you cannot have your main loading logic in the mainform's Load handler; you'll have to create a BackgroundWorker or some other threaded routine.
Here are some guideline steps...
Create a borderless form (this will be your splash screen)
On application start, start a timer (with a few seconds interval)
Show your Splash Form
On Timer.Tick event, stop timer and close Splash form - then show your main application form
Give this a go and if you get stuck then come back and ask more specific questions relating to your problems
simple and easy solution to create splash screen
open new form use name "SPLASH"
change background image whatever you want
select progress bar
select timer
now set timer tick in timer:
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(1);
if (progressBar1.Value == 100) timer1.Stop();
}
add new form use name "FORM-1"and use following command in FORM 1.
note: Splash form works before opening your form1
add this library
using System.Threading;
create function
public void splash()
{
Application.Run(new splash());
}
use following command in initialization like below.
public partial class login : Form
{
public login()
{
Thread t = new Thread(new ThreadStart(splash));
t.Start();
Thread.Sleep(15625);
InitializeComponent();
enter code here
t.Abort();
}
}
http://solutions.musanitech.com/c-create-splash-screen/
I wanted a splash screen that would display until the main program form was ready to be displayed, so timers etc were no use to me. I also wanted to keep it as simple as possible.
My application starts with (abbreviated):
static void Main()
{
Splash frmSplash = new Splash();
frmSplash.Show();
Application.Run(new ReportExplorer(frmSplash));
}
Then, ReportExplorer has the following:
public ReportExplorer(Splash frmSplash)
{
this.frmSplash = frmSplash;
InitializeComponent();
}
Finally, after all the initialisation is complete:
if (frmSplash != null)
{
frmSplash.Close();
frmSplash = null;
}
Maybe I'm missing something, but this seems a lot easier than mucking about with threads and timers.
create splash
private void timer1_Tick(object sender, EventArgs e)
{
counter++;
progressBar1.Value = counter *5;
// label2.Text = (5*counter).ToString();
if (counter ==20)
{
timer1.Stop();
this.Close();
}
}
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.GradientInactiveCaption;
this.ClientSize = new System.Drawing.Size(397, 283);
this.ControlBox = false;
this.Controls.Add(this.label2);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.label1);
this.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Splash";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.ResumeLayout(false);
this.PerformLayout();
Then in your application
sp = new Splash();
sp.ShowDialog();
The other answers here cover this well, but it is worth knowing that there is built in functionality for splash screens in Visual Studio: If you open the project properties for the windows form app and look at the Application tab, there is a "Splash screen:" option at the bottom. You simply pick which form in your app you want to display as the splash screen and it will take care of showing it when the app starts and hiding it once your main form is displayed.
You still need to set up your form as described above (with the correct borders, positioning, sizing etc.)
None of the other answers gave me exactly what I was looking for. Read on for my solution to the problem.
I want a splash screen to fade in from 0% opacity to 100% opacity while things boot up, with a minimum display time of 2000ms (to allow the full fade in effect to show). Once everything is ready, I want the splash screen to display for a further 500ms while the main screen displays behind the splash screen. Then I want the splash screen to go away, leaving the main screen running.
Note that I use the MVP pattern for winforms. If you don't use MVP, you will need to simplify the below example a little.
Long story short, you need to create an AppContext class that inherits from ApplicationContext. I have put this in my Program.cs as below:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
}
public class AppContext : ApplicationContext
{
private IMainPresenter _mainPresenter;
private bool _ready;
public AppContext()
{
_ready = false;
using (ISplashPresenter splashPresenter = new SplashPresenter(new SplashView()))
{
Stopwatch sw = Stopwatch.StartNew();
_mainPresenter = new MainPresenter(new MainView());
_mainPresenter.Closed += MainPresenter_Closed;
new Thread(() =>
{
// !!! Do work here !!!
if (sw.ElapsedMilliseconds < 2000)
Thread.Sleep(2000 - (int)sw.ElapsedMilliseconds);
_ready = true;
})
.Start();
while (!_ready)
{
Application.DoEvents();
Thread.Sleep(1);
}
_mainPresenter.Show();
_ready = false;
new Thread(() =>
{
Thread.Sleep(500);
_ready = true;
})
.Start();
while (!_ready)
{
Application.DoEvents();
Thread.Sleep(1);
}
}
}
private void MainPresenter_Closed(object sender, EventArgs e)
{
ExitThread();
}
}
There are several implementation specific details that I haven't gone into here, such as ISplashPresenter implementing IDisposable and exactly how the fade in is managed; if enough people request it I will edit this answer to include a complete example.
First you should create a form with or without Border (border-less is preferred for these things)
public class SplashForm : Form
{
Form _Parent;
BackgroundWorker worker;
public SplashForm(Form parent)
{
InitializeComponent();
BackgroundWorker worker = new BackgroundWorker();
this.worker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.worker _DoWork);
backgroundWorker1.RunWorkerAsync();
_Parent = parent;
}
private void worker _DoWork(object sender, DoWorkEventArgs e)
{
Thread.sleep(500);
this.hide();
_Parent.show();
}
}
At Main you should use that
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
}
Maybe a bit late to answer but i would like to share my way.
I found an easy way with threads in the main program for a winform application.
Lets say you have your form "splashscreen" with an animation, and your "main" which has all your application code.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Thread mythread;
mythread = new Thread(new ThreadStart(ThreadLoop));
mythread.Start();
Application.Run(new MainForm(mythread));
}
public static void ThreadLoop()
{
Application.Run(new SplashScreenForm());
}
In your main form in the constructor:
public MainForm(Thread splashscreenthread)
{
InitializeComponent();
//add your constructor code
splashscreenthread.Abort();
}
This way the splashscreen will last just the time for your main form to load.
Your splashcreen form should have his own way to animate/display information.
In my project my splashscreen start a new thread, and every x milliseconds it changes his main picture to another which is a slightly different gear, giving the illusion of a rotation.
example of my splashscreen:
int status = 0;
private bool IsRunning = false;
public Form1()
{
InitializeComponent();
StartAnimation();
}
public void StartAnimation()
{
backgroundWorker1.WorkerReportsProgress = false;
backgroundWorker1.WorkerSupportsCancellation = true;
IsRunning = true;
backgroundWorker1.RunWorkerAsync();
}
public void StopAnimation()
{
backgroundWorker1.CancelAsync();
}
delegate void UpdatingThreadAnimation();
public void UpdateAnimationFromThread()
{
try
{
if (label1.InvokeRequired == false)
{
UpdateAnimation();
}
else
{
UpdatingThreadAnimation d = new UpdatingThreadAnimation(UpdateAnimationFromThread);
this.Invoke(d, new object[] { });
}
}
catch(Exception e)
{
}
}
private void UpdateAnimation()
{
if(status ==0)
{
// mypicture.image = image1
}else if(status ==1)
{
// mypicture.image = image2
}
//doing as much as needed
status++;
if(status>1) //change here if you have more image, the idea is to set a cycle of images
{
status = 0;
}
this.Refresh();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (IsRunning == true)
{
System.Threading.Thread.Sleep(100);
UpdateAnimationFromThread();
}
}
Hope this will help some people.
Sorry if i have made some mistakes. English is not my first language.
Here is the easiest way of creating a splash screen:
First of all, add the following line of code before the namespace in Form1.cs code:
using System.Threading;
Now, follow the following steps:
Add a new form in you application
Name this new form as FormSplashScreen
In the BackgroundImage property, choose an image from one of your folders
Add a progressBar
In the Dock property, set it as Bottom
In MarksAnimationSpeed property, set as 50
In your main form, named as Form1.cs by default, create the following method:
private void StartSplashScreen()
{
Application.Run(new Forms.FormSplashScreen());
}
In the constructor method of Form1.cs, add the following code:
public Form1()
{
Thread t = new Thread(new ThreadStart(StartSplashScreen));
t.Start();
Thread.Sleep(5000);
InitializeComponent();//This code is automatically generated by Visual Studio
t.Abort();
}
Now, just run the application, it is going to work perfectly.
Here's my 2023 take on a 2011 question.
Over time, I've done this many times in many ways. The approach that currently use:
Force the main form Handle creation so that the message that creates the splash can be posted into the main form's message queue using BeginInvoke. This allows the main form ctor to return. Ordinarily the handle (the native hWnd) doesn't come into existence until it's shown. Therefore, it needs to be coerced while it's still hidden.
Override the SetVisibleCore() preventing the main window from becoming visible until the Splash has finished processing.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
Debug.Assert(!IsHandleCreated, "Expecting handle is not yet created.");
// Ordinarily we don't get the handle until
// window is shown. But we want it now.
_ = Handle;
Debug.Assert(IsHandleCreated, "Expecting handle exists.");
// Call BeginInvoke on the new handle so as not to block the CTor.
BeginInvoke(new Action(()=> execSplashFlow()));
}
protected override void SetVisibleCore(bool value) =>
base.SetVisibleCore(value && _initialized);
bool _initialized = false;
private void execSplashFlow()
{
using (var splash = new SplashForm())
{
splash.ShowDialog();
}
_initialized= true;
WindowState = FormWindowState.Maximized;
Show();
}
}
Splash Example
The async initialization can be performed in the Splash class itself or it can fire events causing the main app to do things. Either way, when it closes itself the main form will set the _initialized bool to true and it is now capable of becoming visible.
public partial class SplashForm : Form
{
public SplashForm()
{
InitializeComponent();
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.None;
}
protected async override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible)
{
labelProgress.Text = "Updating installation...";
progressBar.Value = 5;
await Task.Delay(1000);
progressBar.Value = 25;
// SIMULATED background task like making an API call or loading a
// database (long-running task that doesn't require the UI thread).
labelProgress.Text = "Loading avatars...";
await Task.Delay(1000);
labelProgress.Text = "Fetching game history...";
progressBar.Value = 50;
await Task.Delay(1000);
labelProgress.Text = "Initializing scenario...";
progressBar.Value = 75;
await Task.Delay(1000);
labelProgress.Text = "Success!";
progressBar.Value = 100;
await Task.Delay(1000);
DialogResult= DialogResult.OK;
}
}
}
Try this code
public partial class ssplashscreen : Form
{
public ssplashscreen()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(1);
if (progressBar1.Value == 100)
{
timer1.Stop();
this.Hide();
Form frm = new login();
frm.Show();
}
}
}
Try This:
namespace SplashScreen
{
public partial class frmSplashScreen : Form
{
public frmSplashScreen()
{
InitializeComponent();
}
public int LeftTime { get; set; }
private void frmSplashScreen_Load(object sender, EventArgs e)
{
LeftTime = 20;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (LeftTime > 0)
{
LeftTime--;
}
else
{
timer1.Stop();
new frmHomeScreen().Show();
this.Hide();
}
}
}
}
EDIT 2
Okay, based on the advice on the answers below I eliminated my thread approach and now my program looks like this:
program.cs
static void Main(){
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
FrmWWCShell FrmWWCShell = null;
var splash = new FrmSplash();
splash.SplashFormInitialized += delegate
{
FrmWWCShell = new FrmWWCShell();
splash.Close();
};
Application.Run(splash);
Application.Run(FrmWWCShell);
}
And FrmSplash.cs like this:
public partial class FrmSplash : Form
{
public FrmSplash()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
splashTimer.Interval = 1;
splashTimer.Tick +=
delegate { if (SplashFormInitialized != null) SplashFormInitialized(this, EventArgs.Empty); };
splashTimer.Enabled = true;
}
public event EventHandler SplashFormInitialized;
}
The problem is that it doesn't work at all now. The splash screen pops up for a split second, the marque progress bar never even initializes, and then disappears while I wait the 10 secs for the dll's and Main Form to show up while staring at nothing....
Color me severely confused now!
ORIGINAL POST--> for reference
I implemented a App Loading splash screen that operates on a seperate thread while all of the dll's are loading and the form is getting "Painted." That works as expected. What is strange is that now when the Splash form exits it sends my main Form to the back, if there is anything else open(i.e. Outlook). I start the thread in Program.cs,
static class Program
{
public static Thread splashThread;
[STAThread]
static void Main()
{
splashThread = new Thread(doSplash);
splashThread.Start();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FrmWWCShell());
}
private static void doSplash()
{
var splashForm = new FrmSplash();
splashForm.ShowDialog();
}
}
And then I end it once my FrmSearch_Shown event is fired.
private void FrmSearch_Shown(object sender, EventArgs e)
{
Program.splashThread.Abort();
this.Show();
this.BringToFront();
}
I, as you can see, have tried calling a Show() and/or BringToFront() on FrmSearch and it still "jumps" to the back.
What am I missing?
What else can I try?
Am I doing this so horribly ignorant that it is my process that is causing this?
Should I file for early retirement?
Thanks for any insight!
EDIT 1
I tried setting the TopMost Property on my Main Form to TRUE. This keeps my form from hiding but it also keeps the user from looking at any other app. Seems a little narcissistic of me...
First of all, it's very important that UI work is done on the primary application thread. I'm actually kind of surprised that you're not getting more serious errors already by showing the splash screen on a background thread.
Here's a technique I've used:
Use Application.Run on your splash form rather than your "real" form.
In your splash form, have an initialized event:
public event EventHandler SplashFormInitialized
Create a timer that fires in one millisecond, and triggers that event.
Then in your application run method you can load your real form, then close your splash form and do an Application.Run on the real form
var realForm = null;
var splash = new SplashForm();
splash.SplashFormInitialized += delegate {
// As long as you use a system.windows.forms.Timer in the splash form, this
// handler will be called on the UI thread
realForm = new FrmWWCShell();
//do any other init
splash.Close();
}
Application.Run(splash); //will block until the splash form is closed
Application.Run(realForm);
The splash might include:
overrides OnLoad(...)
{
/* Using a timer will let the splash screen load and display itself before
calling this handler
*/
timer.Interval = 1;
timer.Tick += delegate {
if (SplashFormInitialized != null) SplashFormInitialized(this, EventArgs.Empty);
};
timer.Enabled = true;
}
Try calling Application.DoEvents() right after show.
Warning: do not call DoEvents very often, but this is one of those time.
EDIT: Clyde noticed something I did not: you are threading this. Don't run any UI on another thread. Take out the thread, leave in the Application.DoEvents().