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();
}
}
}
}
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 have a console application, that launches a form application (from another class) in a different thread.
But then, I wan't to access the richTextBox1 component from my main class, in the main thread and that throws an error wich says that I'm trying to access the component from another thread.
My code:
(Form application)
public partial class ChatGui : Form
{
public static RichTextBox textBox;
public ChatGui()
{
InitializeComponent();
richTextBox1.ReadOnly = true;
richTextBox1.BackColor = SystemColors.Window;
}
public void WriteLine(string line)
{
richTextBox1.Text += line+"\r\n";
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
WriteLine("[You]: "+textBox1.Text);
NetworkManager.SendPacket("rchat_msg " + textBox1.Text.Replace(" ", "%20"));
textBox1.Text = "";
e.Handled = true;
}
}
public void Exit()
{
Application.Exit();
}
private void ChatGui_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
}
The main console application
public static void StartRemoteChat()
{
RemoteChat = true;
Program.ChatGui = new ChatGui();
new Thread(new ThreadStart(() =>
{
Application.Run(Program.ChatGui);
while (RemoteChat)
{
// ...
}
})).Start();
}
So, how can I access the richTextBox1 component from my main thread (I want to change some variables of the component) without this error happening ?
The control is owned by the thread that creates it.
In your case the thread that you start owns the form because it paints it so its going to be your forms ui thread.
However when you try to use another thread to make changes to the control, it will throw an exception just like you said.
The way around this is to invoke the thread that created it to come and make the change that you want like this:
richTextBox1.BeginInvoke(new Action(()=>
{
// make changes to control here.
}));
one that can help you is the "CheckForIllegalCrossThreadCalls = false;" but used here(at the beginning):
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
the advice is to use each call to "RichTextBox1" the invoke method.
this method allows to interact with elements created by other threads.
example:
richTextBox1.Invoke(new Action(() => richTextBox1.Text = "hello word"));
"CheckForIllegalCrossThreadCalls = false" is a solution that has enough dangers if it is used in complex programs.
I hope I was helpful.
Simple call invoke to invoke the method from a new thread:
if (InvokeRequired)
{
Invoke(new Action(**METHODHERE**));
return;
}
and to pass parameters:
if (InvokeRequired)
{
Invoke(new Action<string>(**METHODHERE**) **parameter**);
return;
}
Good read: https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
try setting following property.
RichTextBox.CheckForIllegalCrossThreadCalls=false;
this may help you.
This is how the demo windows form app looks like:
And this is the implemented code:
public partial class HornMorphoWindow : Form
{
private static dynamic _morpho;
private static string _analyzeWord;
private static Logger logger = LogManager.GetCurrentClassLogger();
public delegate void StatusDelegate();
public HornMorphoWindow()
{
InitializeComponent();
}
private void HornMorphoWindow_Load(object sender, EventArgs e)
{
var splashScreen = new Splash(); // This is Splash Form
splashScreen.Show();
Application.DoEvents(); // Force the splash screen to be shown
Task.Factory.StartNew(LoadLibrary).Wait(); // Wait for the library to load
splashScreen.Close();
}
// When a button clicked
private void analyze_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(amharicInput.Text)) return;
_analyzeWord = amharicInput.Text; // amharicInput is TextBox
analyze.Enabled = false; // analyze is a button
Task.Factory.StartNew(AnalyzeWord);
}
private static void LoadLibrary()
{
logger.Info("Loaidng Library.....");
using (Py.GIL())
{
_morpho = Py.Import("l3");
_morpho.load_lang("am");
}
logger.Info("Library Loaded Sucessfully!");
}
private void AnalyzeWord()
{
logger.Info("Word Analyzation Started. Word: " + _analyzeWord);
using (Py.GIL())
{
_analyzeWord = _morpho.anal_word("am", _analyzeWord, Py.kw("nbest", 1));
}
logger.Info("Word Analyzation Ended. Result:\n " + _analyzeWord);
try
{
this.Invoke(new StatusDelegate(UpdateStatus));
}
catch
{
// Some problem occurred
}
}
private void copyButton_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(result.Text)) return;
Clipboard.SetText(result.Text, TextDataFormat.UnicodeText);
}
private void UpdateStatus()
{
result.Text = _analyzeWord; // result is a label
copyButton.Visible = true; // copyButton shows up when result is successfull
analyze.Enabled = true;
}
}
I asked this question because, I call C# function which use using(Py.GIL()) block and is executed with a new thread as shown on the above code. It works for the first round(sometimes it does not), and for the next round, it stops on the using block and the application stays the same with out showing any result or exception.
The application works if I removed the using(Py.GIL()) block, and do other stuff for the sake of testing. For example, Change the result label text to something else.
What am I doing wrong?
UPDATED
The problem is not on LoadLibrary function. It is on AnalyzeWord function. On LoadLibrary it successfully executes it but not on AnalyzeWord function.
I followed the suggestion by #Damien and came up with this solution and it worked.
private void HornMorphoWindow_Load(object sender, EventArgs e)
{
var splashScreen = new Splash(); // This is Splash Form
splashScreen.Show();
Application.DoEvents(); // Force the splash screen to be shown
// The newly added line and the solution for the problem
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
//********************************
Task.Factory.StartNew(LoadLibrary).Wait(); // Wait for the library to load
splashScreen.Close();
}
PythonEngine.BeginAllowThreads() must be initialized on the Main thread or else it does not work.
The main thread will hold the GIL after initialization until you
explicitly release it by calling PythonEngine.BeginAllowThreads() from
the main thread (not from your background thread)
I have a Main form, which is running a synchronous operation(thus freezing the form).
Before that starts to happen I call my function showWaitWindow().
private void showWaitWindow()
{
Wait x = new Wait();
x.Show(this); //"this" is allowing the form to later centralize itself to the parent
}
This is where it is exactly happening:
if (result)
{
System.Threading.Thread t = new System.Threading.Thread(new
System.Threading.ThreadStart(showWaitWindow));
t.Start();
}
else
{
return;
}
propertyGrid1.SelectedObject = z.bg_getAllPlugins(); //Heavy synchronous call
//This should be closing the form, which is not happening.
for (int index = Application.OpenForms.Count; index >= 0; index--)
{
if (Application.OpenForms[index].Name == "Wait")
{
MessageBox.Show("found");
Application.OpenForms[index].Close();
}
}
I've tried this without threading as well, which didn't work as well. Also, because it's trying to centralize to the parent, while being created in another thread, it throws an exception "tried to access in different thread that it was created in" rephrasing.
How do I approach that?
I would suggest using a BackgroundWorker -- available in the WinForms toolbox.
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker1;
public Form1()
{
InitializeComponent();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//perform lengthy operation in here.
}
}
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().