I'm having an application with a progress bar and a buttom.
When the button clicked the progress bar value will get increased, here is the source code,
private void Run()
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
private void button1_Click(object sender, EventArgs e)
{
Run();
}
so when i run the application, the progress bar value is getting increased, but when i try to move the window its not responding.
I can not run it in a normay thread way - it will throw Cross-Thread error.
so i changed the code like,
private void Run()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.Run));
}
else
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(new ThreadStart( Run));
myThread.Start();
}
Now i can able to move the winodow, but when i move the progress bar is stopped, and when i release the mouse button its resuming. So still the execution is in UI Thread.
How to handle it in a better way.Please help me to do this .
Invoke() works by running the given delegate from the UI thread. So if you use Invoke() to run your entire method, then your entire method runs from the UI thread.
Instead, you should be doing your actual work in the other thread, and just performing UI updates in the UI thread, by just Invoke()ing the little bits of code that perform the updates.
One easy way to do this is to use the BackgroundWorker class built into the standard library.
This has been answered here - in your case the code should look something like:
this.BeginInvoke((Action)(() => progressBar1.Value++));
Related
Hi,guys, I don't understand how to handle the UI thread, so I try to create many UI elements in a child thread, but,
How to modify the following code so that the window don't block:
public void CreateCtr(string name)
{
Button btn = new Button();
btn.Content = name;
btn.Width = 10;
btn.Height = 10;
content.Children.Add(btn);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(()=> {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
});
}
You need to give the UI thread some space to breathe. With the current code, the UI thread is busy processing all the create button tasks and cannot get to processing user input. Put a minimal pause between creating each individual button. A few milliseconds are enough:
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(() => {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(() => {
CreateCtr(i.ToString());
}));
Thread.Sleep(5);
}
});
}
The longer the pause, the more responsive the form becomes.
You will note that the form becomes less responsive again after some time, because after a certain amount of components on a form, adding another component takes very long. This is where my original comment becomes relevant: There is no usecase for having that many components on a form, so the framework is not designed to handle it well.
What might work is not putting all buttons on the same parent container but creating a tree of components where each component has no more than say 100 children. But again: I don't see any relevant usecase.
Might work to disable the dispatcher when adding the controls.
int count = 100000;
Task.Factory.StartNew(() => {
using (var d = Dispatcher.DisableProcessing())
{
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
}
});
The usual routine, progress bar, user clicks the button, process runs, progress bar is updated. Classic.
However I would like to run the process the moment entire app starts, so I run it in Loaded handler for Window (the only window in my app).
And now, the progress bar is not updated. How to overcome this problem?
Tester for updating progress bar.
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
Progress.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Render, new Action(delegate() { ++Progress.Value; }));
}
Because you put all the work on the UI thread, which is used to update the Progressbar, so the Progressbar is not updated until the work is done. Put the work on a background thread, and update the Progressbar from the background thread using Dispatcher.BeginInvoke.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
Progress.Dispatcher.BeginInvoke(new Action(delegate() { ++Progress.Value; }), null);
}
});
}
If you are using .NET 2.0 (not using TPL), you can use ThreadPool,
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem( (o) =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
Progress.Dispatcher.BeginInvoke(new Action(delegate() { ++Progress.Value; }), null);
}
});
}
Or using await-async (.NET 4.5), see, you can call DoWork() from any method that marked as async.
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
await DoWork();
}
async Task DoWork()
{
await Task.Run(async () =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
await Progress.Dispatcher.BeginInvoke(new Action(delegate() { ++Progress.Value; }), null);
}
});
}
i copied the following example Microsoft Thread Example
Which gives the code below
but i get an error on the line "this.progressBar1.Value = newval;" stating that "Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on."
what could be the issue?
thanks
damo
C# Code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("This is the main thread");
}
}
First of all I strongly suggested to use some higher level techniques like Tasks rather than using Thread class directly. Task classes not only easier to use, they're more effective, easier to compose and easier to avoid such issues that you faced recently.
The main issue with your code that you trying to update UI objects from non-UI threads. UI technologies (like Windows Forms or WPF) requires that only thread that creates an UI object will access to their properties.
To fix this you should marshal control from non-UI thread to UI thread. And there is a plenty of options to do this (but all of them only a syntactic sugar around concept called SynchronizationContext):
Use synchronization context directly:
.
// storing SynchronizationContext in the private field of your form
private SynchronizationContext _syncContext = SyncrhonizationContext.Current;
private void MethodFromTheSeparateThread()
{
// Marshaling control to UI thread
_syncContext.Post(d =>
{
// Put all your code that access UI elements here
}, null);
}
Use InvokeRequired/Invoke as Gregor mentioned already
Use TaskScheduler.FromSynchronizationContext
.
private void ImplementLongRunningOperation()
{
int id;
string name;
Task.Factory.StartNew(() =>
{
// our long-runing part. Getting id and name
id = 42;
name = "Jonh Doe";
}).ContinueWith(t =>
{
// Handling results from the previous task.
// This callback would be called in UI thread!
label1.Text = id.ToString();
label2.Text = name;
}, TaskScheduler.FromSynchronizationContext);
}
As I mentioned, last approach (using Tasks) is a preferable way if you're working on .NET 4.0+. This not only saves you from some low-level classes but also lead to more clear design because you can clearly separate separate steps like getting the data and processing them.
You have to invoke new delegate:
delegate void ThreadTaskDelegate();
private void ThreadTask()
{
if (this.InvokeRequired)
{
ThreadTaskDelegate del = new ThreadTaskDelegate(ThreadTask);
this.Invoke(del, null);
}
else
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
}
Happy coding! :)
The example is a poor one. You must access controls in the thread in which they are created. This is almost always the main UI thread. (It is possible to have separate UI threads for different forms each with their own message pumps. But don't worry about that right now.)
The background thread must use Control.Invoke(Delegate) to change to the main UI thread before accessing Controls. Then, when the UI work is done, get out of the UI thread as soon as possible.
For example:
private void ThreadTask()
{
// This code runs in the background thread.
while (true)
{
if (this.InvokeRequired)
{
// In order to access the UI controls, we must Invoke back to the UI thread
this.Invoke(new ThreadStart(SetRandomProgress));
}
else
{
// We are already in the UI thread, so we don't have to Invoke
SetRandomProgress();
}
// Wait briefly. This wait happens in the background thread.
// During this time, the UI is still responsive, because it is not blocked.
// You can verify this by tweaking the duration to something longer (say, 5000 ms).
Thread.Sleep(100);
}
}
private void SetRandomProgress()
{
Random rnd = new Random();
int stp = this.progressBar1.Step * rnd.Next(-1, 2);
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
You could rewrite your code like this, your progressBar will be updated in the UI thread, Invoke a method that does access to the progressBar through delegate. Check the code:
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
Random rnd = new Random();
while (true)
{
int randValue = rnd.Next(-1, 2);
progressBar1.Invoke(new updater(UpdateProgressBar), new object[] {randValue});
Thread.Sleep(100);
}
}
private delegate void updater(int value);
private void UpdateProgressBar(int randValue)
{
int stp = this.progressBar1.Step * randValue;
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
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;
}
I am using WPF trying to run a Thread in the background that updates a progress bar. I don't wan to block the UI thread so I am running the following code. However the UI still blocked. It seems so simple, what am I doing wrong?
Dispatcher.BeginInvoke(
(ThreadStart) delegate(){
for(double i = progressBar_ChangeProgress.Minimum;
i < progressBar_ChangeProgress.Maximum;
i++)
{
for (int b = 0; b < 100000000; b++) { }
progressBar_ChangeProgress.Value = i;
}
EnableAllInputControls();
}, DispatcherPriority.Background);
Why not leverage the BackgroundWorker in this scenario...
void Go()
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar_ChangeProgress.Value = e.ProgressPercentage;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int b = 0; b < 100; b++)
{
Thread.Sleep(100);
worker.ReportProgress(b);
}
}
UPDATE:
If you are wanting to use the Dispatcher; set the priority to Normal and perform the processing on the background thread then calling a method on the UI thread to provide the update.
void Go()
{
ThreadStart start = delegate()
{
//this is taking place on the background thread
for (int i = 0; i < 100; i++)
{
//this is slowing things down; no real relevance
Thread.Sleep(100);
//this will marshal us back to the UI thread
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<int>(Update), i
);
}
};
new Thread(start).Start();
}
void Update(int value)
{
//this is taking place on the UI thread
_progressBar.Value = value;
}
The Dispatcher is just a mechanism to run a bit of code on the UI thread at a later time. The priority you're passing in controls when it will get executed, not any type of thread priority. The contents of your delegate in this case are getting run on the UI thread. Using a BackgroundWorker as mentioned by Aaron would certainly help here.
Also I might point out that usually a progress bar shows how close a task is to completing. If you don't know how long something is going to take or have no way of measuring progress, you can use an Indeterminate progress bar. Only update the value if you have some meaningful information. (though you may have just provided this for demonstration purposes)
Everything inside BeginInvoke is being run on the UI thread and so it will block.
What you need to do is run any time intensive code in your thread and then just update the UI inside the Invoke.
You want something more like this:
for (double i = progressBar_ChangeProgress.Minimum;
i < progressBar_ChangeProgress.Maximum;
i++)
{
for (int b = 0; b < 100000000; b++) { }
Dispatcher.Invoke((ThreadStart) delegate(){
progressBar_ChangeProgress.Value = i;
});
}