I've been having the worst time trying to figure this out. I'm quite confused when it comes to threading.
What I'm trying to do is have a delay of 1 pause in the function and the continue the function until another 1 sec pause comes along and finally finish the function.
public partial class SplashScreen : Form
{
public SplashScreen()
{
InitializeComponent(); // initalize splash screen
DatabaseStatus(); // set database connection
getUserInfo(); // get user information
showInfo(); // show app information on splash screen
System.Threading.Thread wa = new System.Threading.Thread(new System.Threading.ThreadStart(checkUser));
wa.IsBackground = true;
wa.Start();
}
void checkUser()
{
if (RegisteredUser)
{
richTextBox1.Text += "Loading user settings..."; // SHOW THIS TEXT AND WAIT 1 SECOND UNTIL NEXT
System.Threading.Thread.Sleep(1000);
if (DATABASE_CONNECTION)
{
richTextBox1.Text += "Loging on...";
// WAIT AGAIN 1 SEC AND CONTINUE///
LoginCheck login = new LoginCheck(USER_NAME, PASSWORD);
if (login.LOGIN_SUCESS)
{
richTextBox1.Text += "Sucess!";
// SHOW THIS TEXT AND WAIT 1 SEC UNTIL SPLASH SCREEN FADE OUT//
//MessageBox.Show(login.HASH);
opac.Interval = 12;
opac.Start();
opac.Tick += new EventHandler(dec);
}
else
{
MessageBox.Show(login.HASH);
}
}
}
else
{
richTextBox1.Text += "Not user profile found...";
// ask user to register
}
}
}
Where I've placed the comments is where I want the thread to pause and continue...
Anyone have any input?
Thanks
First, you must know when working with WinForms (and also WPF/Silverlight... right?) that you cannot, should not, manipulate UI elements from any other thread except the original that created the form/control.
If you need to do async work, you will need use Invoke or BeginInvoke to transition your UI work back over to the form or control's thread. Also, consider using delegates (MethodInvoker is handy) rather than creating your own threads.
Also, you need to start your async work during or after the Load event, otherwise your logic will start to execute before the form is even displayed (see my example below).
I took your example and put it into a simplified sample of my own.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
RegisteredUser = true;
DATABASE_CONNECTION = true;
}
private void UpdateStatus(string message)
{
BeginInvoke(new MethodInvoker(() => richTextBox1.Text += message));
}
private void CheckUser()
{
if (RegisteredUser)
{
UpdateStatus("Loading user settings..."); // SHOW THIS TEXT AND WAIT 1 SECOND UNTIL NEXT
System.Threading.Thread.Sleep(1000);
if (DATABASE_CONNECTION)
{
UpdateStatus("Logging on...");
//// WAIT AGAIN 1 SEC AND CONTINUE///
//LoginCheck login = new LoginCheck(USER_NAME, PASSWORD);
if (true)//login.LOGIN_SUCESS)
{
UpdateStatus("Success!");
// SHOW THIS TEXT AND WAIT 1 SEC UNTIL SPLASH SCREEN FADE OUT//
//MessageBox.Show(login.HASH);
//opac.Interval = 12;
//opac.Start();
//opac.Tick += new EventHandler(dec);
}
else
{
//MessageBox.Show(login.HASH);
}
}
}
else
{
UpdateStatus("No user profile found.");
// ask user to register
}
}
protected bool DATABASE_CONNECTION { get; set; }
protected bool RegisteredUser { get; set; }
private void Form1_Load(object sender, EventArgs e)
{
var invoker = new MethodInvoker(CheckUser);
invoker.BeginInvoke(null, null);
}
}
As you can see, I use a method such as UpdateStatus to do work on the UI for me, ensuring its done on the UI thread. You can use any number of similar methods to do other stuff in the UI, such as trigger the fading of your form or whatever.
You shouldn't even show message boxes outside the UI thread; have those invoked safely by a similar method (also, for debugging, just Debug.WriteLine to write messages to the debugger rather than pop up message boxes all over).
I would recommend you to do everything in a separate Background worker thread and update the status in the UI. Which makes UI faster and ur application reliable.
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
you need to use Invoke() see this
public partial class SplashScreen : Form
{
bool DATABASE_CONNECTION;
bool RegisteredUser; // if user has been registered
string USER_NAME;
string PASSWORD;
double LIN_x = 0.01;
DialogResult result;
custom con = new custom();
Timer opac = new Timer();
public SplashScreen()
{
InitializeComponent(); // initalize splash screen
DatabaseStatus(); // set database connection
getUserInfo(); // get user information
showInfo(); // show app information on splash screen
}
private void UpdateStatus(string message)
{
BeginInvoke(new MethodInvoker(() => richTextBox1.Text += message + Environment.NewLine));
}
void checkUser()
{
UpdateStatus("Loading user settings...");
if (RegisteredUser)
{
UpdateStatus("User " + USER_NAME + " found." );
if (DATABASE_CONNECTION)
{
UpdateStatus("Logging on...");
LoginCheck login = new LoginCheck(USER_NAME, PASSWORD);
if (login.LOGIN_SUCESS)
{
UpdateStatus("Success! Loading " + con.AppTitle() + "...please wait");
//UpdateStatus(login.HASH); return hash string from web site
fadeSplash(); // begin fade out of form
}
else
{
UpdateStatus("There was an error logging in.");
}
}
else
{
UpdateStatus("No database connection found.");
}
}
else
{
UpdateStatus("No user found");
Reg(); // Registration form
}
}
private void fadeSplash()
{
opac.Interval = 12;
opac.Tick += new EventHandler(dec);
opac.Start();
}
private void dec(object sender, EventArgs e)
{
this.Opacity -= LIN_x;
if (this.Opacity < 0.04)
{
opac.Stop();
this.Hide();
main open = new main(); // start application
open.Show();
}
}
}
Here's the code where the fade method doesn't fire during MethodInvoke
Related
I'm working on a basic audio player and I want to update some GUI elements based on the progression through the song.
Next to my Form I use an AudioPlayer class, which contains a ref on the created Form.
In the playAudio function I want to start a timer, which should call updateCurrTime, when elapsed. (For reference: I'm using NAudio)
The function calling the timer:
public bool playAudio()
{
if (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
waveOutDevice.Pause();
timer.Enabled = false;
return false;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Paused)
{
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
else if(waveOutDevice.PlaybackState == PlaybackState.Stopped)
{
initPlayer(mu_path);
waveOutDevice.Play();
timer.Enabled = true;
return true;
}
return false;
}
And the function to update my Form with:
public void updateCurrTime()
{
while (waveOutDevice.PlaybackState == PlaybackState.Playing)
{
form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
}
}
I defined the timer like this:
timer = new Timer();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 100;
}
and the OnTimedEvent like this:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
self_ref.updateCurrTime();
}
I use a getter/setter structure for the label text:
public int curr_time
{
get { return Convert.ToInt32(this.l_t_curr.Text); }
set { this.l_t_curr.Text = value.ToString() + "s"; }
}
My problem is, that I'm getting an error, because the form is created on another thread. I did my research, but tbh, I didn't understand, how to implement BackGroundWorker or other solutions in my case.
With help of Julo's hint I was able to fix the issue.
public void updateCurrTime()
{
MethodInvoker methodInvokerDelegate = delegate ()
{ form1_ref.l_t_curr.Text = audioFileReader.CurrentTime.TotalSeconds.ToString(); };
//form1_ref.curr_time = (int)audioFileReader.CurrentTime.TotalSeconds;
//This will be true if Current thread is not UI thread.
if (form1_ref.InvokeRequired)
form1_ref.Invoke(methodInvokerDelegate);
else
methodInvokerDelegate();
}
To update GUI from another thread, you need to use Invoke or BeginInvoke.
Example:
private void GuiUpdate(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
GuiUpdate(sender, e);
});
return;
}
// put here GUI updating code
}
Difference between Invoke or BeginInvoke is:
Invoke stops execution of current thread until the called function ends,
when using BeginInvoke the starting thread continues without interruption.
Use Invoke when you need result from the function, or priority update. Otherwise it is better to use BeginInvoke.
Prepared for downvotes but I am really nowhere near getting to grips with the ins and outs of threading with this backgroundworker, but I've managed to just about get a structure for what I want:
public class cls1
{
private FormProgress myProgForm = new FormProgress();
public BackgroundWorker worker = new BackgroundWorker(); // new instance of bkgworker
public void prepare_a_job()
{
worker.WorkerReportsProgress = true; // Allows the worker to report progress
worker.ProgressChanged += worker_ProgressChanged; // Adding handler to update progress
worker.DoWork += job1; // Adding handler for the ACTUAL JOB METHOD
myProgForm.Show(); // Show the prog update form
worker.RunWorkerAsync(); // Start the job, already! Wo lo loo
}
void job1(object sender, EventArgs e) // Do 0 to 100
{
for (int i = 0; i <= 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i); // ReportProgress uses percentages
Thread.Sleep(50);
}
// THIS IS WHERE I'D INSERT ANOTHER METHOD
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if(e.ProgressPercentage == 100) // If the % gets to 100
{
myProgForm.UPDATEME("", true); // then pass true to close progressForm
}
else
{
myProgForm.UPDATEME("Counting\n" + e.ProgressPercentage); // else just update
}
}
}
And on my FormProgress I just have this method:
public void UPDATEME(string MSG, bool finish = false)
{
this.label1.Text = MSG;
this.Refresh();
if (finish) { this.Close(); }
}
Messy, right? But it works (and I've been trying to find/learn this stuff for 24 hours and this is the first thing I even remotely understand.
The issue I'm having with this mess, is calling the UPDATEME() method from any other methods I want to call during the job1 routine - e.g. in reality this won't just be a loop to waste time, it'll be a set of conditions to call a tonne of other methods in various orders.
I tried bunging in a 2nd method into job1 and within that 2nd method call UPDATEME but it's not a thread-safe cross-thread update...
I think it might have something to do with Invoking but then I also read something about MSDN BackgroundWorker was another way to allow thread-safe without invoke and then my head exploded and my brain fell out.
How can I always refer to my ProgressForm.UPDATEME("new progress message") method within any other method in my code?
EDIT:
For instance I'd insert a call to this 2nd method in the job1 call
void myOtherMethod()
{
(worker).ReportProgress(0);
myProgForm.UPDATEME("Doing part 1");
Thread.Sleep(1000);
myProgForm.UPDATEME("Doing part 2");
Thread.Sleep(1000);
myProgForm.UPDATEME("Doing part 3");
Thread.Sleep(1000);
}
How can I always refer to my ProgressForm.UPDATEME("new progress
message") method within any other method in my code?
Like this:
public void UPDATEME(string MSG, bool finish = false)
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(() => this.UPDATEME(MSG, finish)));
}
else
{
this.label1.Text = MSG;
if (finish) { this.Close(); }
}
}
I don't really understand how invoking the method from within itself
gets round the fact the method is called outside the 1st level
thread ...
It is confusing at first as this is a recursive call. The "meat" is that Invoke() runs whatever is inside it on the same thread that created the control (the form itself in this case). When we enter the method the second time (due to recursion) the check returns false and we safely run the else block on the UI thread.
You can actually get rid of the check (and recursion) by always calling Invoke() whether it's needed or not like this:
public void UPDATEME(string MSG, bool finish = false)
{
this.Invoke(new Action(() =>
{
this.label1.Text = MSG;
if (finish) { this.Close(); }
}));
}
Here is an alternate version that still checks if Invoke() is required, but doesn't use recursion (less confusing, but we've now introduced duplicate code):
public void UPDATEME(string MSG, bool finish = false)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
this.label1.Text = MSG;
if (finish) { this.Close(); }
}));
}
else
{
this.label1.Text = MSG;
if (finish) { this.Close(); }
}
}
For those that are "detail oriented", here is an approach/variation (I'm using MethodInvoker instead of Action) showing one way to remove the duplicate code above:
public void UPDATEME(string MSG, bool finish = false)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
this.updater(MSG, finish);
});
}
else
{
this.updater(MSG, finish);
}
}
private void updater(string MSG, bool finish = false) // NOT thread safe, thus the private (don't call directly)
{
this.label1.Text = MSG;
if (finish) { this.Close(); }
}
I process a file line by line reading various events that have a time stamp and data associated with them. I want to be able to show a form while doing processing which I need to interact with and intercept some events by having a button saying interrupt EventX and if it is pressed it will show the event data in a rich text box field when this event is reached sometime in the future. I can then change some of that event data (let's say I simulate some conditions) and when I press "Resume" it should resume processing by raising an event to the intended subscriber for further processing.
So I need an interceptor that will be pass-trough mechanism when a certain form element is pressed and pass that data to the intended subscriber.
I am ok to wait synchronously for modifying data and pressing "Resume"
Thanks
If you want to have a Responsive GUI while doing a long running operation, you need some form of Multitasking. Wich means either async/await or any of the many Multithreading (Thread and BackgroundWorker, mostly) approaches
While pause and resume could be added, doing so usually more work then it is worth. At the very least you run into issues like still held filehandles or race conditions. Often a "cancel" action is way enough/better then a full stop/resume mechanic.
As a Beginner I would advice you to use the BackgroundWorker. It is about as easy as getting into Multitasking via Multithreading can be. I even wrote a example for it a few years back:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Thank you Christofer,
I accepted you answer as you gave me some suggestions how to solve my problem.
You can see bellow how I solved this problem
Rad
//class variable
private SimulatorRunner simulatorRunner;
//Code behind DevicesSimulatorForm form
private void RunSimulator()
{
btnRerun.BackColor = Color.BurlyWood;
ParameterizedThreadStart start = new ParameterizedThreadStart(RunSimulator);
Thread simulatorProcessingThread = new Thread(start);
simulatorProcessingThread.Start(this);
}
//This will run in a separate thread so when accessing controls Invoke is being used.
public void RunSimulator(object form)
{
DevicesSimulatorForm devicesSimulatorForm = (DevicesSimulatorForm) form;
simulatorRunner.Run(devicesSimulatorForm);
devicesSimulatorForm.InvokeEx(formInner =>
{
formInner.btnRerun.BackColor = Color.LightGray;
InitializeFields();
InitializeTextBackBorder();
InitializeButtonControls();
running = false;
});
}
public class SimulatorRunner
{
public void Run(DevicesSimulator form)
{
string buffer = "Some content read from file in a loop that needs to be passed
to a rich text box when a boolean Intercept check box is true
and FormStatusIntercept will return true and with Thread.Sleep(1)
we will have a chance to update the buffer to the new value and by
unchecking Intercept check box we will exit while loop and continue
processing"
while (true)
{
if (FormStatusIntercept(form, ref buffer))
{
Thread.Sleep(1);
}
else
{
publishEventArgs.Buffer = buffer;
break;
}
}
PublishEvent?.Invoke(this, publishEventArgs);
}
}
private bool FormStatusIntercept(DevicesSimulator simulatorForm, ref string buffer)
{
string modifiedBuffer = buffer;
//When btnFormStatus button is pressed it changes FormStatusContinued = true
//which allows continuation of the processing by exiting while loop
if (simulatorForm.FormStatusContinued == true)
{
simulatorForm.InvokeEx(form =>
{
if (form.rtbFormStatus.Text != modifiedBuffer)
{
modifiedBuffer = form.rtbFormStatus.Text;
}
form.FormStatusContinued = false;
form.FormStatusInterceptPending = false;
});
buffer = modifiedBuffer;
return false;
}
else if (simulatorForm.FormStatusIntercept == true)
{
if (simulatorForm.FormStatusInterceptPending == false)
{
//Whith check box pressed (true) we request a stop
//and we enter a while loop with Thread.Sleep(1)
simulatorForm.InvokeEx(form =>
{
form.btnFormStatus.Text = "Continue";
form.rtbFormStatus.Text = modifiedBuffer;
form.FormStatusInterceptPending = true;
});
}
return true;
}
return false;
}
I am quite new to c# programming, I am trying to achieve the following result but failing to do so.
What I expect -:
On a click event of a button, I want to open an applciation via its API, run analysis and then exit the application. While running the application I have a progress bar on the form which should keep going from 0 - 100 till the RunAnalysis() method called through the API gets executed, when it gets executed the progress bar should show as 100% and the application called through should exit
What is happening -:
The RunanAlysis() is being executed and the application exits, the click event of the button gets executed and then the progress bar moves from 0 - 100 which should not happen
What is my attempt
namespace trialapp
{
public partial class Form1 : Form
{
AutoResetEvent obj = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
ETABS2015.cSapModel SapModel;
System.Reflection.Assembly ETABSAssembly;
ETABS2015.cOAPI ETABSObject;
int result = -1;
delegate int MyDelegate();
MyDelegate pointer = null;
private void button1_Click(object sender, EventArgs e)
{
//Use ret to check return values of OAPI calls
int ret;
//Dynamically load ETABS.exe assembly from the program installation folder
string pathToETABS = System.IO.Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES"), "Computers and Structures", "ETABS 2013", "ETABS.exe");
ETABSAssembly = System.Reflection.Assembly.LoadFrom(pathToETABS);
//Create an instance of ETABSObject and get a reference to cOAPI interface
ETABSObject = (ETABS2015.cOAPI)ETABSAssembly.CreateInstance("CSI.ETABS.API.ETABSObject");
//Start ETABS application
ret = ETABSObject.ApplicationStart();
//Get a reference to cSapModel to access all OAPI classes and functions
SapModel = ETABSObject.SapModel;
//Initialize model
ret = SapModel.InitializeNewModel();
//Create steel deck template model
ret = SapModel.File.NewSteelDeck(4, 12, 12, 4, 4, 24, 24);
//Save model
System.IO.Directory.CreateDirectory("C:\\ETABSAPI");
ret = SapModel.File.Save("C:\\ETABSAPI\\example2.edb");
//Run analysis
backgroundWorker1.RunWorkerAsync();
// ret = SapModel.Analyze.RunAnalysis();
obj.WaitOne();
//Close ETABS
ret = ETABSObject.ApplicationExit(false);
//Clean up variables
SapModel = null;
ETABSObject = null;
//Check ret value
if (ret == 0)
{
MessageBox.Show("API script completed succesfully.");
}
else
{
MessageBox.Show("API script FAILED to complete.");
}
}
public void AfterRunAnalysisComplete(IAsyncResult resultHolder)
{
result = pointer.EndInvoke(resultHolder);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
pointer = new MyDelegate(SapModel.Analyze.RunAnalysis);
IAsyncResult flag = pointer.BeginInvoke(new AsyncCallback(AfterRunAnalysisComplete), null);
while (!flag.IsCompleted)
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
backgroundWorker1.ReportProgress(i);
if (i == 100)
{
i = 0;
}
if (flag.IsCompleted)
{
break;
}
}
}
backgroundWorker1.ReportProgress(100);
//obj.Set();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
}
Can any one tell me as to where exactly am I going wrong?
Edit -:
I tried not using WaitOne() and putting the code which followsbackgroundWorker1.RunWorkerAsync(); in the backgroundWorker1_DoWork method, but that is not I want to do as the extent of main project is too much and this will not make sense with the design of classes.
The problem, as I see it, is when you kick off
ret = ETABSObject.ApplicationStart();
it's going to start it in a new thread, which your program isn't going to have access to. I would recommend starting the application in a TaskFactory, then you can use a while to check if the task is still running and update your progress bar.
Maybe something like this:
var etabApp = Task.Factory.Startnew(() => { ETABSObject.ApplicationStart()});
while(etabApp.Status == TaskStatus.Running)
{
//Do something to check the percent complete and update the progress bar
}
I do some checks on form load, but it is locking the form for a period (some thousandths of seconds). For this reason, I want to display a message such as "loading application..", but I have no idea how I do this. I hope this clear! Any help is very appreciated. Thanks in advance.
Ideally what you want to do is to perform your checks on a background thread, so that the UI thread isn't blocked. Have a look at the BackgroundWorker class.
You should hook your checks to the DoWork event of the background worker, and call the BackgroundWorker's RunWorkerAsync() method from the Form_Load event to kick off the background work.
Something like this (note, this is untested):
BackgroundWorker bw = new BackgroundWorker();
public void Form_Load(Object sender, EventArgs e) {
// Show the loading label before we start working...
loadingLabel.Show();
bw.DoWork += (s, e) => {
// Do your checks here
}
bw.RunWorkerCompleted += (s, e) => {
// Hide the loading label when we are done...
this.Invoke(new Action(() => { loadingLabel.Visible = false; }));
};
bw.RunWorkerAsync();
}
You can create another thread to display the loading message.
First you need a bool.
bool loading = true;
Create a thread like:
Thread myThread = new Thread(new ThreadStart(Loading));
myThread .Start();
Then have a method:
private void Loading()
{
while(loading)
{
//Display loading message here.
}
}
When you are done loading whatever just set loading to false and the tread will terminate.
Have a look at BackgroundWorker component. You dont need any threading knowledge at all.
This can be accomplished easily by displaying a separate form executed on another thread. In this form (call it frmSplash) you can put an animated gif or static text. The code you will need is as follows in your main form:
Declare some variables after the class.
public partial class frmMain : Form
{
public Thread th1;
static frmSplash splash;
const int kSplashUpdateInterval_ms = 1;
// Rest of code omitted
Then add the following method to your main form. This starts the splash screen:
static public void StartSplash()
{
// Instance a splash form given the image names
splash = new frmSplash(kSplashUpdateInterval_ms);
// Run the form
Application.Run(splash);
}
Next, you need a method to close the splash screen:
private void CloseSplash()
{
if (splash == null)
return;
// Shut down the splash screen
splash.Invoke(new EventHandler(splash.KillMe));
splash.Dispose();
splash = null;
}
Then, in your main Form Load, do this:
private void frmMain_Load(object sender, EventArgs e)
{
try
{
Thread splashThread = new Thread(new ThreadStart(StartSplash));
splashThread.Start();
// Set the main form invisible so that only the splash form shows
this.Visible = false;
// Perform all long running work here. Loading of grids, checks etc.
BindSalesPerson();
BindCustomer();
BindBrand();
// Set the main form visible again
this.Visible = true;
}
catch (Exception ex)
{
// Do some exception handling here
}
finally
{
// After all is done, close your splash. Put it here, so that if your code throws an exception, the finally will close the splash form
CloseSplash();
}
}
Then, if the main form is closed, make sure your splash screen is closed also:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
// Make sure the splash screen is closed
CloseSplash();
base.OnClosing(e);
}
The code for the Splash form (In frmSplash.cs) is as follows:
public partial class frmSplash : Form
{
System.Threading.Timer splashTimer = null;
int curAnimCell = 0;
int numUpdates = 0;
int timerInterval_ms = 0;
public frmSplash(int timerInterval)
{
timerInterval_ms = timerInterval;
InitializeComponent();
}
private void frmSplash_Load(object sender, EventArgs e)
{
this.Text = "";
this.MaximizeBox = false;
this.MinimizeBox = false;
this.ControlBox = false;
this.FormBorderStyle = FormBorderStyle.None;
this.Menu = null;
}
public int GetUpMilliseconds()
{
return numUpdates * timerInterval_ms;
}
public void KillMe(object o, EventArgs e)
{
//splashTimer.Dispose();
this.Close();
}
}
I hope this helps you. It might not be the best code ever written, but it worked for me.
You need to create one form that is frmloading here.. you need to create object of that form and called using threading concept..and in FrmLoading put one Picturebox and set .gif image in it.
FrmLoading f2 = new FrmLoading();
using (new PleaseWait(this.Location, () =>MethodWithParameter())) { f2.Show(this); }
f2.Close();
As shown above code you need to create PleaseWait class.
PleaseWait.cs
public class PleaseWait : IDisposable
{
private FrmLoading mSplash;
private Point mLocation;
public PleaseWait(Point location, System.Action methodWithParameters)
{
mLocation = location;
Thread t = new Thread(new ThreadStart(workerThread));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
methodWithParameters();
}
public void Dispose()
{
mSplash.Invoke(new MethodInvoker(stopThread));
}
private void stopThread()
{
mSplash.Close();
}
private void workerThread()
{
mSplash = new FrmLoading(); // Substitute this with your own
mSplash.StartPosition = FormStartPosition.CenterScreen;
//mSplash.Location = mLocation;
mSplash.TopMost = true;
Application.Run(mSplash);
}
}