Windows Show Desktop causes WinForms elements to stop updating - c#

Is there a simple way to have elements on a form keep updating even after I click on Windows Show Desktop? The following code updates the value in textBox1 until I click on Windows Show Desktop (Windows 10 - click on the bottom right of the screen). I prefer not to use Application.DoEvents().
void Button1Click(object sender, EventArgs e)
{
int n = 0;
while(true) {
textBox1.Text = n++.ToString();
textBox1.Refresh();
Update();
// Application.DoEvents();
Thread.Sleep(200);
}
}

Using Thread.Sleep blocks the current thread (UI thread); you can fix it like this:
private async void button1_Click(object sender, EventArgs e)
{
int n = 0;
while (true)
{
textBox1.Text = n++.ToString();
await Task.Delay(200);
}
}

Related

How can I stop/cancel a copy procedure in C# [duplicate]

I have c# application which having 2 buttons. First having for loop which is run 10k times. and each loop code execution take 1 second to finish.
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
}
some time i want to stop this loop/ execution on click on another button "Stop", but its not working. Please suggest me what solution.
It's not good practice to run long running operations in UI Thread (thread where all UI events are handled - such as button click). You should run your loop in another that.
You can use Task Parallel Library (TPL):
private bool stopIt = false;
private void Form1_Load(object sender, EventArgs e)
{
Task task = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000000000; i++)
{
if (!stopIt)
{
Console.WriteLine("Here is " + i);
}
else
{
break;
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
stopIt = true;
}
The simplest solution (not best) is to add Application.DoEvents() into the loop to process button events:
private bool cancel;
public void loop()
{
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
Application.DoEvents();
if (cancel)
break;
}
}
public void cancelButton_Click(object sender, EventArgs e)
{
cancel=true;
}
Much better and still simple solution is to employ async Task (the rest of the code stays the same minus Application.DoEvents() call):
private void loopButton_Click(object sender, EventArgs e)
{
new Task(loop).Start();
}
Beware that you should use this.Invoke(new Action(() => { <your code> } )); to access UI controls from the loop in this case.

C# How to user timers on labels?

I have a label pop up letting the user when they click the copy button that it's been copied using a label in the bottom right on the app. But I want the text to go away after 2 or so seconds. Then come back if they click copy again, this is my copy buttons code:
private void copyBtn_Click(object sender, EventArgs e)
{
labelCopied.Text = "Copied to Clipboard!";
Clipboard.SetText(btcTxtBox.Text);
SystemSounds.Hand.Play();
}
I do know labelCopied.Text.Remove(0); would clear the label but I cannot figure out how to implement it using a timer
Use a Timer for this:
private void copyBtn_Click(object sender, EventArgs e)
{
labelCopied.Text = "Copied to Clipboard!";
Clipboard.SetText(btcTxtBox.Text);
SystemSounds.Hand.Play();
Timer t = new Timer();
t.Interval = 2000; //2000 milliseconds = 2 seconds
t.Tick += (a,b) =>
{
labelCopied.Text = string.Empty;
t.Stop();
};
t.Start();
}
EDIT
Task.Delay uses a Timer internally. So if you don't mind a minimal performance overhead, Task.Delay is good to go. Additionally Task.Delay is more portable since Timer is WinForms specific (In WPF you would use DispatcherTimer)
private async void copyBtn_Click(object sender, EventArgs e)
{
labelCopied.Text = "Copied to Clipboard!";
Clipboard.SetText(btcTxtBox.Text);
SystemSounds.Hand.Play();
await Task.Delay(2000);
labelCopied.Text = "";
}
Assuming WinForms, use async/await with Task.Delay(), like this:
private async void copyBtn_Click(object sender, EventArgs e)
{
labelCopied.Text = "Copied to Clipboard!";
Clipboard.SetText(btcTxtBox.Text);
SystemSounds.Hand.Play();
await Task.Delay(2000);
labelCopied.Text = "";
}

How to stop long running loop using button

I have c# application which having 2 buttons. First having for loop which is run 10k times. and each loop code execution take 1 second to finish.
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
}
some time i want to stop this loop/ execution on click on another button "Stop", but its not working. Please suggest me what solution.
It's not good practice to run long running operations in UI Thread (thread where all UI events are handled - such as button click). You should run your loop in another that.
You can use Task Parallel Library (TPL):
private bool stopIt = false;
private void Form1_Load(object sender, EventArgs e)
{
Task task = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000000000; i++)
{
if (!stopIt)
{
Console.WriteLine("Here is " + i);
}
else
{
break;
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
stopIt = true;
}
The simplest solution (not best) is to add Application.DoEvents() into the loop to process button events:
private bool cancel;
public void loop()
{
for(int i=0;i<10000;i++){
//My running code take 1 sec for each loop
Application.DoEvents();
if (cancel)
break;
}
}
public void cancelButton_Click(object sender, EventArgs e)
{
cancel=true;
}
Much better and still simple solution is to employ async Task (the rest of the code stays the same minus Application.DoEvents() call):
private void loopButton_Click(object sender, EventArgs e)
{
new Task(loop).Start();
}
Beware that you should use this.Invoke(new Action(() => { <your code> } )); to access UI controls from the loop in this case.

Can't make form 'close itself'?

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

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