Can't make form 'close itself'? - c#

I have one form that opens another form upon clicking a button. When the new form opens, I have a progress bar complete via a loop, then I want the form to close. Here is the button click event that launches the new form.
private void calculateButton_Click(object sender, EventArgs e)
{
if (checkFormComplete())
{
ProgressForm proForm = new ProgressForm();
proForm.Show();
}
}
And here is the code for the new form that completes a progress bar that should then close itself.
public ProgressForm()
{
InitializeComponent();
for (int i = 0; i < 101; i++)
calculationProgress.Value = i;
this.Close();
}
However, upon running this I get:
Cannot access a disposed object. Object name: 'ProgressForm'.
And the debugger points to this line of my main form:
proForm.Show();
I'm not sure I understand why, or what the proper way to do this is. How is that line being called after the close statement inside my new form?

The form is trying to close itself before it's even shown (because you have your code in the constructor). Put your progress bar code and Close() call in the FormLoad or FormShown event instead. Example:
private void ProgressForm_Shown(object sender, EventArgs e)
{
for (int i = 0; i < 101; i++)
{
calculationProgress.Value = i;
Application.DoEvents(); // force the form to update itself
}
this.Close();
}

Allow the loading to complete before you try to close the form :-)
You should start your progress bar loop in the Form_Load event.
However note that looping like that will cause your form to lock up until the progress bar completes rendering.
Do the progress loop in a background thread. A BackgroundWorker is ideal for running the progress loop.
public proForm()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
proForm_Load()
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
(int i = 0; i < 101; i++) worker.ReportProgress(i);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}

A constructor is used to initialize an object not destroying the object in the contructor itself.
So a constructor should contain intialization code.
Your code is trying destroy the object with the this.Close(); in the constructor hence the error.
Put your code in Load event of Form.
Change the calculationProgress.Value through a BackgroundWorker's ProgressChanged event

Related

Error with showing a new form in C#

I need your help please
I have created a form and insert a timer & progress bar into it
when the value of progress bar reach to 100% I want to close this form and open the main form of my program
I write this code but when I Run the program it show this error :
( Form that is already displayed modally cannot be displayed as a modal dialog box. Close the form before calling showDialog.)
How I can resolve this problem
Form1 MainForm = new Form1();
public Welcome_window()
{
InitializeComponent();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(10);
if (progressBar1.Value == 100)
{
this.Visible = false;
MainForm.Visible = false;
MainForm.ShowDialog();
this.Close();
}
}
}
I think the problem is that you don't stop the timer, so the tick event will be fired even if the progress already reached 100%.
Form1 MainForm = new Form1();
public Welcome_window()
{
InitializeComponent();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(10);
if (progressBar1.Value == 100)
{
timer1.Stop();
this.Visible = false;
MainForm.ShowDialog();
this.Close();
}
}

Open windows form (MDI) in thread c#

I am having a MDI form, now clicking on the menu in MDI form, I am opening other forms in PnlView (Panel), this acts as container for forms.
What I am thinking is, when the application loads, it opens a default form.
At this point I want to open all forms, but as opening all forms at a time will hamper the performance, other forms opening should run in different thread. Then if all forms are open then user can switch between forms quickly.
private bool IsFormAlreadyOpen(ControlItem _item)
{
bool reutrnValue = false;
foreach (Control ctrl in PnlView.Controls)
{
if (ctrl.Name.ToLower() == _item.Control.Name.ToLower())
{
reutrnValue = true;
break;
}
}
return reutrnValue;
}
This is the function which checks if form is already opened or not. So this will get all the forms open and will just bring that form to front.
Can anyone helps me with opening form in thread so that it will not impact the performance.
I hope this makes sense.
---------------------------------------------------------------------
private void MdiForm1_Load(object sender, EventArgs e)
{
OpenFirstForm(); //This ospens a default form, form name frmDefault
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 2); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
//System.Threading.Thread.Sleep(500);
OpenForms(form1);
OpenForms(form2);
worker.ReportProgress((i * 10));
}
}
}
It throws an exception,
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on
Getting below exception,
No Overload 'OpenForm' matches delegate MethodInvoker
this.Invoke(new MethodInvoker(OpenForm),new object[] { Keys.F12, Keys.Alt });
private void OpenForm(Keys keyPressed, Keys modifier)
{
---------------------------
}
The problem is that you are trying to access a control instantiated in one thread from another thread. Use the following code snippet to solve your issue at hand:
// Paste this snippet in the beginning of your method
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(/*Enter the name of your method here*/));
return;
}
// Method code goes here ......
/*
Example:
private void SomeMethod(object sender, EventArgs e) {
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(SomeMethod)); // Name of current method is 'SomeMethod'
return;
}
// Code continues here
int x,y,z;
// Do something .....
}
*/
Hope this helps.

Manipulate ListView from inside a background worker

I'm using a background worker to loop through each item in a ListView and do work on it after a button is clicked:
private void bParsePosts_Click(object sender, EventArgs e)
{
parseWorker.RunWorkerAsync(this.lvPostQueue);
}
Then, I have:
private void parseWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Loop through each item
for (int i = 0; i < lvPostQueue.Items.Count; i++)
{
string title = lvPostQueue.Items[i].SubItems[0].ToString();
string category = lvPostQueue.Items[i].SubItems[1].ToString();
string url = lvPostQueue.Items[i].SubItems[2].ToString();
lvPostQueue.Items[i].SubItems[3].Text = "Done";
}
}
However, I get this error:
Cross-thread operation not valid: Control 'lvPostQueue' accessed from a thread other than the thread it was created on.
How would I go about manipulating the lvPostQueue control from within that background worker?
Thanks.
The right answer would be:
private void bParsePosts_Click(object sender, EventArgs e)
{
parseWorker.WorkerReportsProgress = true;
parseWorker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
parseWorker.RunWorkerAsync();
}
private void parseWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Loop through each item
for (int i = 0; i < lvPostQueue.Items.Count; i++)
{
string title = lvPostQueue.Items[i].SubItems[0].ToString();
string category = lvPostQueue.Items[i].SubItems[1].ToString();
string url = lvPostQueue.Items[i].SubItems[2].ToString();
parseWorker.ReportProgress(i * 100 / lvPostQueue.Items.Count, i);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var i = (int)e.UserState;
lvPostQueue.Items[i].SubItems[3].Text = "Done";
}
Simply use thread safe calls: http://msdn.microsoft.com/en-us/library/ms171728.aspx
Example:
// This event handler starts the form's
// BackgroundWorker by calling RunWorkerAsync.
//
// The Text property of the TextBox control is set
// when the BackgroundWorker raises the RunWorkerCompleted
// event.
private void setTextBackgroundWorkerBtn_Click(
object sender,
EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();
}
// This event handler sets the Text property of the TextBox
// control. It is called on the thread that created the
// TextBox control, so the call is thread-safe.
//
// BackgroundWorker is the preferred way to perform asynchronous
// operations.
private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
this.textBox1.Text =
"This text was set safely by BackgroundWorker.";
}

How do I display something like "loading.." while I do some checks on form load?

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);
}
}

C# Filling progress bar in separate thread

I have a progress bar and want to fill it in using a separate thread, because the main thread is put to sleep for a few seconds in a loop. I'm using a timer so that the progress bar fills up over a certain amount of time.
Thread creation:
private void PlayButton_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
int playTime = getPlayTime();
int progressInterval = playTime / 100;
Thread progressThread = new Thread(barfiller=>fillBar(progressInterval));
progressThread.Start();
//Loops through the collection and plays each note one after the other
foreach (MusicNote music in this.staff.Notes)
{
music.Play(music.Dur);
Thread.Sleep(music.getInterval(music.Dur));
}
progressThread.Abort();
}
As is, nothing happens to the progress bar, if however I call fillbar() within the main thread, it works BUT it fills after the for loop is complete and not before/during the for loop even though I call fillbar() before the loop.
Thread methods:
private void fillBar(int progressInterval)
{
progressTimer = new System.Windows.Forms.Timer();
progressTimer.Tick += new EventHandler(clockTick);
progressTimer.Interval = progressInterval; //How fast every percentage point of completion needs to be added
progressTimer.Start();
}
public void clockTick(object sender, EventArgs e)
{
if (progressBar1.Value < 100)
{
progressBar1.Value++;
}
else
{
progressTimer.Stop();
}
}
You're doing it the wrong way. The main thread is reponsible of updating the user interface. So if you're blocking it with your calculations, it won't be able to draw the progress bar. Move your computing code in another thread and it should be fine.
always the main thread for manage user interface. use backgroundworker for this purpose.
to enable progress feature in backgroundworker set WorkerReportProgress(property) to true and
set WorkerSupportCancellation for stopping backgroundworker if needed.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// also use sender as backgroundworker
int i = 0;
foreach (MusicNote music in this.staff.Notes)
{
if(backgroundWorker1.CancellationPending) return;
music.Play(music.Dur);
Thread.Sleep(music.getInterval(music.Dur));
int p = (int) (i*100/ staff.Notes.Count); /*Count or Length */
backgroundWorker1.ReportProgress(p);
i++;
}
backgroundWorker1.ReportProgress(100);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}

Categories