Showing a Spin wheel progress animated gif while user initiates a long running process.
When i click the start, the process starts and same time wheel starts rotating.
But the problem is, the wheel strucks in-between and resumes, that happen multiple times during the long run process. It should be continuously rotation. I am running both the task and animated gif in same thread (since the indicator is just an animated image not a real progress value).
Code used is,
this.progressPictureBox.Visible = true;
this.Refresh(); // this - an user controll
this.progressPictureBox.Refresh();
Application.DoEvents();
OnStartCalibration(); // Starts long running process
this.progressPictureBox.Visible = false;
OnStartCalibration()
{
int count = 6;
int sleepInterval = 5000;
bool success = false;
for (int i = 0; i < count; i++)
{
Application.DoEvents();
m_keywordList.Clear();
m_keywordList.Add("HeatCoolModeStatus");
m_role.ReadValueForKeys(m_keywordList, null, null);
l_currentValue = (int)m_role.GetValue("HeatCoolModeStatus");
if (l_currentValue == 16)
{
success = true;
break;
}
System.Threading.Thread.Sleep(sleepInterval);
}
}
How do I show uninterrupted continuous display of wheel till the process ends?
If you use framework 4, replace the OnStartCalibration(); // Starts long running process line with the following code:
BackgroundWorker bgwLoading = new BackgroundWorker();
bgwLoading.DoWork += (sndr, evnt) =>
{
int count = 6;
int sleepInterval = 5000;
bool success = false;
for (int i = 0; i < count; i++)
{
Application.DoEvents();
m_keywordList.Clear();
m_keywordList.Add("HeatCoolModeStatus");
m_role.ReadValueForKeys(m_keywordList, null, null);
l_currentValue = (int)m_role.GetValue("HeatCoolModeStatus");
if (l_currentValue == 16)
{
success = true;
break;
}
System.Threading.Thread.Sleep(sleepInterval);
}
};
bgwLoading.RunWorkerAsync();
You can't run the progress indication and the task on the same thread. You should use a BackgroundWorker
Your GUI thread will subscribe to the ProgressChanged event, and will be notified of updates to the task. From here, you can update the progress indication appropriately. There's also events for when the task is finished.
Related
I'm working on a C# WinForms project which implements a password manager. One of the features I want to include is a timeout for how long a password is allowed to stay in the system clipboard. I implemented a thread which updates a progress bar & then clears the clipboard before the thread terminates:
private void getPassword(int lifeInSeconds)
{
int maxLifeBarValue = lifeInSeconds * 10;
Thread t = new Thread
(delegate ()
{
//Initialize the progress bar
Invoke((MethodInvoker)delegate
{
lifeBar.Maximum = maxLifeBarValue;
lifeBar.Value = maxLifeBarValue;
lifeBar.Visible = true;
Clipboard.SetText(pd.getAccountPassword(lstAccounts.Text));
});
//Loop to update the progress bar
for (int x = maxLifeBarValue; x >= 0; x--)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lifeBar.Value = x;
});
}
//Clear the system clipboard
Clipboard.SetText(string.Empty);
//Hide the progress bar when we're done
Invoke((MethodInvoker)delegate
{
lifeBar.Visible = false;
});
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
This works, but the problem I'm having is that if the user triggers an event to copy another password (or the same one; it doesn't matter), we now have 2 threads running in the background. This is apparent by the fact that the progress bar is "flipping out" so to speak as each thread is updating it's value independently.
Is there a way in which I can detect & terminate the original thread (if it exists) when the user clicks the copy password button again?
You could keep a reference to the Thread, and then abort the thread before starting a new one. Like this:
private Thread passwordClearThread = null;
private void getPassword(int lifeInSeconds)
{
int maxLifeBarValue = lifeInSeconds * 10;
if (passwordClearThread != null && passwordClearThread.IsAlive)
{
passwordClearThread.Abort();
passwordClearThread.Join();
}
passwordClearThread = new Thread
(() =>
{
//Initialize the progress bar
Invoke((MethodInvoker)delegate
{
lifeBar.Maximum = maxLifeBarValue;
lifeBar.Value = maxLifeBarValue;
lifeBar.Visible = true;
Clipboard.SetText(pd.getAccountPassword(lstAccounts.Text));
});
//Loop to update the progress bar
for (int x = maxLifeBarValue; x >= 0; x--)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lifeBar.Value = x;
});
}
//Clear the system clipboard
Clipboard.Clear();
//Hide the progress bar when we're done
Invoke((MethodInvoker)delegate
{
lifeBar.Visible = false;
});
});
passwordClearThread.SetApartmentState(ApartmentState.STA);
passwordClearThread.Start();
}
I am showing a modal dialog with my background worker, but it's becoming unresponsive when some long task is done on completed event of worker thread. Can someone tell me why it is happening and how I can fix this?
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var back = new BackgroundWorker();
back.DoWork += delegate
{
int i = 0;
while (i < 100)
{
Thread.Sleep(100);
i++;
}
};
back.RunWorkerCompleted += delegate
{
//running along task on UI therad
int i = 0;
while (i < 10000)
{
int j = 0;
while (j<10000)
{
label.Content = i.ToString();
j++;
}
i++;
}
msgbox.Close();
};
back.RunWorkerAsync();
msgbox.Title = "loading";
msgbox.Owner = this;
msgbox.ShowDialog();
}
You can't run any long-running method on the UI thread without blocking it. A single thread cannot both execute your while loop(s) and respond to user input simultaneously. This is impossible.
That's why you should execute any long-running method on a background, i.e. in the DoWork event handler in this case.
The RunWorkerCompleted event handler should only perform some quick UI related tasks, like for example updating a label or similar. Don't do anything heavy in there because then you will block the UI thread and your application will become unresponsive.
This part is executed in the UI thread:
back.RunWorkerCompleted += delegate
{
//running UI therad
int i = 0;
while (i < 100)
{
Thread.Sleep(100);
label.Content = i.ToString();
i++;
}
msgbox.Close();
};
When you call Thread.Sleep(100); you put the whole UI thread to sleep. Meaning, that your UI will sleep for 10 seconds.
I have a problem with background worker.
I don't know how exactly to describe it.
Actually its a game and with the background worker ever x milisecs i update the progress bar and check if anyone has lost/won or the time is up.
If someome has win the game ends.
If both players have lost/time is up the game goes to the next round.
The ploblem occurs when both players have lost. The method NextRound in the SetTime method,
runs twice.
Here is the code:
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.SetTime(e.ProgressPercentage);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(3000);
BackgroundWorker worker = sender as BackgroundWorker;
int tick = ProgLib.maxTime * 10;
for (int i = 1; i <= 100; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
Thread.Sleep(tick);
worker.ReportProgress(i);
}
}
}
private void SetTime(double k)
{
this.time.Bar1.Value = k;
this.time.Bar2.Value = k;
if (k >= 100 || (Gallery1.hasLost() && Gallery2.hasLost()) || ((Gallery1.isWinner() || Gallery2.isWinner())))
{
if (bw == null)
return;
bw.CancelAsync();
bw.Dispose();
bw = null;
saveData();
ProgLib.isAnyoneWinner(Gallery1.isWinner(), Gallery2.isWinner());
if (ProgLib.gameHasended())
{
gameHasEnded();
}
else
{
next_round();
}
}
}
private void next_round()
{
Thread nextRoundThread = new Thread((Object Send) =>
{
MainThread.Send((Object send) =>
{
Gallery1.hidePanel.Visibility = Visibility.Visible;
Gallery2.hidePanel.Visibility = Visibility.Visible;
ProgLib.nextLetter();//goes to next letter
LetterToPlay1.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));//sets the next letter
LetterToPlay2.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));
}, null);
Thread SoundThread = new Thread((Object send) =>
{
//Here Must Delay enought so the animation stops the hear the bell and the the letter, and then the game starts
Thread.Sleep(1800);
ProgLib.playOtherSound(ProgLib.Sounds.Chimes);//Bell Sound
Thread.Sleep(100);
//ProgLib.PlayLetterSound(ProgLib.getCurentLetter());//Letter Sound
ProgLib.playOtherSound(ProgLib.Sounds.Cat_Yawn);//TestOnly
});
SoundThread.IsBackground = true;
SoundThread.Start();
Thread.Sleep(3000);
MainThread.Send((Object send) =>
{
Gallery1.refresh();//galleries refresh so that the magician hides.
Gallery2.refresh();//
Gallery1.hidePanel.Visibility = Visibility.Hidden;//hide the Big Magician of mistakes
Gallery2.hidePanel.Visibility = Visibility.Hidden;
}, null);
});
nextRoundThread.IsBackground = true;
nextRoundThread.Start();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
There's a threading race in your code. Your worker will call ReportProgress() and immediately iterate the loop. Racing past the CancellationPending property check and falling asleep again.
Your SetTime() method runs later. And calls CancelAsync() but that doesn't have any effect at all since the worker is sleeping. Not until it wakes up again, calls ReportProgress() again, iterates the loop and then sees CancellationPending set to true.
Your SetTime() method will be called again, even though you've already ended the game.
Threading is rife with problems like this. A band-aid is to check for CancellationPending after the Sleep() call. Which works 99.999% of the time. Getting to 100% requires a pretty drastic rewrite that uses proper locking.
I am using threading in my code , thread are created using function:
private void InitializeBackgoundWorkers()
{
for (int f = 0; f < maxThreads; f++)
{
listBox1.Items.Insert(0, "Starting Thread : " + (f + 1));
threadArray[f] = new BackgroundWorker();
threadArray[f].DoWork +=
new DoWorkEventHandler(backgroundWorkerFiles_DoWork);
threadArray[f].RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerFiles_RunWorkerCompleted);
threadArray[f].ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorkerFiles_ProgressChanged);
threadArray[f].WorkerReportsProgress = true;
threadArray[f].WorkerSupportsCancellation = true;
}
}
And the doevent is something like :
private void backgroundWorkerFiles_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
int flag = 0;
while (rowCounter < allPostingRows.Tables[0].Rows.Count && flag == 0)
{
for (int i = 0; i < maxThreads; i++)
{
if (threadArray[i].CancellationPending == true)
{
flag = 1;
threadArray[i].CancelAsync();
worker.ReportProgress(0, "Thread Paused:");
}
}
if (flag == 0)
{
//perform work here
}
}
}
And on button i try to cancel the threads using:
for (int i = 0; i < maxThreads; i++)
{
threadArray[i].CancelAsync();
}
Am i cancelling the thread correctly? As when they get canceled i see the line in listbox saying thread cancelled so it does go to the cancellation code but after some time it restarts
Thank you
I don't think you really understand BackgroundWorker. The DoWork event handler is supposed to be a handler for one unit of work. DoWork is called with one thread. It doesn't make sense to call CancelAsync from within a DoWork handler--that's independent of any and all other BackgroundWorker's. Within the DoWork handler it should only be checking one CancellationPending, the sender's (once cast to BackgroundWorker, in your case worker).
But, otherwise, calling CancelAsync from the UI is the correct way to cancel a particular BackgroundWorker.
Background workers are not "thread"s. You''re not cancelling a thread, you're cancelling the worker--which allows the DoWork handler a chance to exit before it is done it's work.
I have this code:
for(int k = 0; k<11; k++)
{
pBar.Maximum = 10;
pBar.Value = k;
if (pBar.Maximum == k)
pBar.Value = 0;
}
However, the problem is, the progressbar gets reset when it is about 60% full. How can I ensure that the progressbar will fill all the way before being reset?
First: there is no any reason to assign pBar.Maximum on every interarion.
Just do:
pBar.Maximum = 10;
for(int k = 0; k<11; k++)
{
pBar.Value = k;
if (pBar.Maximum == k)
pBar.Value = 0;
}
Second: your code result in blocking iteration. There is no way it could ever behave correctly. Use multi-threading and change the progress value based on some event,tick whatever, not in loop, as it's done here.
If you switch to Classic mode, this glitch will be gone. The progress bar will appear fully drawn before being reset.
This is because in Classic mode, the painting operation is synchronous and completes before the Value setter returns, but in the themed mode, there is some sort of animation played when you increase the value, and that takes some time to play.
On contrary, when you decrease the value, there is no animation; the progress bar is shrinked immediately.
This is why it appears only about 60% full: you decrease the value (which completes immediately) before the progress bar has time to draw the animation for the last several increments.
pBar.Maximum = 10;
int count = 0;
Timer timer = new Timer();
timer.Interval = 1000;
timer.Tick += (source, e) =>
{
pBar.Value = count;
if (pBar.Maximum == count)
{
pBar.Value = 0;
timer.Stop();
}
count++;
}
timer.Start();
Your problem is that you're using a loop. You need to use a timer so that the program has time to both perform the checks/assignments and update the screen.
The code replaces the for loop with a timer that calls the body of the loop. Since a timer does not have an index variable, it is initialized outside of the timer (count) and updated with each tick.
I finally found a solution to this problem, and wrote about it here. The idea was from THIS SO question.
progressBar1.Value = e.ProgressPercentage;
if (e.ProgressPercentage != 0)
progressBar1.Value = e.ProgressPercentage - 1;
progressBar1.Value = e.ProgressPercentage;
if (progressBar1.Maximum == e.ProgressPercentage)
progressBar1.Value = 0;