Hey fellas.
I have an application that is designed to be run across a network. This means that the initial run of this application can take a while. So I've been putting together a splash screen to pretty the process up some.
It uses threading to show the form via a static method. I'm still something of a threading novice, so when I got errors I was a bit confused as to what and why.
Turns out my code is perfectly fine when I run it outside the Visual Studio debugger. But when I run it from inside the debugger, I get the exception:
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
Here's my class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace MyApp
{
public partial class SplashScreen : Form
{
private static double OPACITY_INCREMENT = .05;
private static SplashScreen _splashForm;
private static SplashScreen SplashForm
{
get { return SplashScreen._splashForm; }
set { SplashScreen._splashForm = value; }
}
private static void ShowForm()
{
SplashScreen.SplashForm = new SplashScreen();
Application.Run(SplashScreen.SplashForm);
}
internal static void CloseForm()
{
if (SplashScreen.SplashForm != null &&
SplashScreen.SplashForm.IsDisposed == false)
{
// Make it start going away.
OPACITY_INCREMENT = -OPACITY_INCREMENT;
}
SplashScreen.SplashThread = null;
SplashScreen.SplashForm = null;
}
private static Thread _splashThread;
private static Thread SplashThread
{
get { return SplashScreen._splashThread; }
set { SplashScreen._splashThread = value; }
}
internal static void ShowSplashScreen()
{
if (SplashScreen.SplashForm != null)
{
return;
}
SplashScreen.SplashThread = new Thread(new ThreadStart(SplashScreen.ShowForm));
SplashScreen.SplashThread.IsBackground = true;
SplashScreen.SplashThread.SetApartmentState(ApartmentState.STA);
SplashScreen.SplashThread.Start();
}
public SplashScreen()
{
InitializeComponent();
this.timerFade.Start();
this.ClientSize = this.BackgroundImage.Size;
}
private void SplashScreen_Load(object sender, EventArgs e)
{
}
private void timerFade_Tick(object sender, EventArgs e)
{
if (OPACITY_INCREMENT > 0)
{
if (this.Opacity < 1)
this.Opacity += OPACITY_INCREMENT;
}
else
{
if (this.Opacity > 0)
this.Opacity += OPACITY_INCREMENT;
else
{
this.Invoke(new MethodInvoker(this.TryClose));
}
}
}
private void TryClose()
{
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(this.TryClose));
}
this.Close();
}
}
}
I'm calling the splash screen from inside the Program.cs Main method.
namespace CIMA
{
static class Program
{
// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen.ShowSplashScreen();
// Code omitted for brevity.
SplashScreen.CloseForm();
// Code omitted for brevity.
}
}
}
I would like to be able to call SplashScreen.CloseForm() from inside one of my other forms, but I haven't got quite so far as trying that out yet. I'm confused as to what the Debugger is up to.
Am I missing something? Or do I have to disable the splash screen if it is being run in the debugger?
If so, what's a nice way of doing this? I want to avoid resorting to Compilation Symbols if possible, because I hate keeping track of them.