C# Progressbar value in foreach() - c#

I have this code:
private void button1_Click(object sender, EventArgs e)
{
var max = 0;
foreach (var address in textBox2.Text.Split(','))
{
max = +1;
}
var maxp = 100 / max;
foreach (var address in textBox2.Text.Split(','))
{
SendMessage(address);
progressBar1.Value = +maxp;
}
}
It calculates how many emails are in the textbox, and then makes a proportion. To each email sent adds the value of progress, the problem is that when I press the button, the progressbar does not move. When all emails are sent the progressbar moves to the end of stroke.
How can I do?

This happens because your loop and the ui are executing on the same thread. While the loop is busy, it can't update the ui
You can use a BackgroundWorker to run the loop on a different thread in the background, then use the BackgroundWorker's ProgressChanged event to update your progressbar. You can learn more about BackgroundWorkers here

Again you are executing code on a thread other than the UI thread. In WinForms you must invoke back to the UI thread and in WPF you must use application dispatcher.
WinForms:
Invoke((MethodInvoker) delegate {
DoSomethingOnUiThread();
});
WPF:
Application.Current.Dispatcher.Invoke(() =>{
DoSomethingOnUiThread();
});

You block the UI thread so it cannot refresh the UI until the processing leaves the method.
See three different solutions here with explicitly used threads, BackgroundWorker and async-await techniques.

Related

Updating progressbar created on different thread

I am currently trying to update a progress bar that is defined in another window that is created by a different thread.
I need to send progress updates from the main thread in order to have the correct value displayed on progress bar.
Any help is appreciated, thanks in advance. Below is the code:
private void StartLoadingWindow(object sender, RoutedEventArgs e)
{
var t = new Thread(ThreadLoadingWindow);
t.SetApartmentState(ApartmentState.STA); //Mandatory
t.Start();
}
private void ThreadLoadingWindow()
{
var w = new LoadingWindowsControl();
w.Closed += (sender, args) =>
{
//Exit Dispatcher when Window closes
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.SystemIdle);
Dispatcher.Run();
};
w.ShowDialog();
}
Inside the LoadingWindowsControl I have created a simple mvvm progress bar.
The typical way would be to use a Progress<T> object. If this is created on the UI thread it will invoke the ProgressChanged on the UI thread. Attach an event handler to this that updated your progress bar.
It looks however like you are creating multiple UI threads, and this is probably not a good idea. If you want to do something in the background you should use Task.Run, await the result, and update the UI. If you need continuous update you should follow the Progress<T> example. Create an object that you inform of the updates, and let it post the update work to the the UI thread.

Code in button click c# happens after the function finishes

I have some simple C# code that is triggered on a Button Press. The button press first clears some ListBoxes, then changes the text of a label, and then calls a function.
private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
listBox2.Items.Clear();
listBox3.Items.Clear();
listBox4.Items.Clear();
label5.Text = "Getting links...";
process(url);
label5.Text = "Finished";
}
But the Lists are cleared and the label is changed after process() is finished executing. This ruins the purpose as I'm changing the label so that the user is aware that some action is taking place. How can I make the initial label change before the function process() finishes?
If your process method is long-running, it can cause UI freezing and prevent UI from redrawing - that's why you don't see your label text immediate update.
Simpliest way to achieve your goal - is call label5.Refresh() right after label5.Text = "Getting links...";, this will immediately cause invalidation and redrawing of label.
Or even you can call this.Refresh() if more than one control should be updated - this will update whole usercontrol or form owns your controls.
But note - if your process method runs a very long time (more than a 2-3 seconds for example) - you should consider doing it asyncroniously in thread separate from UI. It is considered as "good style" because it will not cause UI freezing.
You can use async keyword. Only when accessing the UI from different thread you have to use the Invoke
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(
() =>
{
this.Invoke((MethodInvoker)delegate
{
listBox1.Items.Clear();
listBox2.Items.Clear();
listBox3.Items.Clear();
listBox4.Items.Clear();
myLabel.Text = "Getting links...";
});
});
this.process(url);
await Task.Run(
() =>
{
this.Invoke((MethodInvoker)delegate { myLabel.Text = "Finished"; });
});
}
What about using a Task to run process(url) in a "thread" separated from GUI?
In this way GUI will stay responsive to the user ang got "refreshed", then, when your task ends, you just have to update GUI label.
When you have ops that must take a bit of time, you should always separate them from GUI. My two cents.
You have 2 options. either use a different thread to run process(url), or add before it Application.DoEvents().

Progress Bar (marquee) disappears while code is running

I have a simple form with a button and a progress bar. User clicks the button to begin processing and the progress bar animates while the code runs. In this case, it's just a for loop. But the problem is that the progress bar only appears for a fraction of a second then disappears.
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 100;
for (int i = 0; i < 3; i++) { Thread.Sleep(1000); } // sleep 3 times for 1 second
}
I assume this happens because the frame is redrawn after the code continues? So I tried running the for loop on another thread but it didn't seem to matter.
How can I get the bar to animate while the loop runs?
Thanks
You can simply workaround this by including a call to Application.DoEvents() on each iteration of your loop.
But this is not the best way to achieve this.
The recommended way is processing your loop inside another thread, like a BackgroundWorker, keeping your progressbar animating on the mainthread.
You can get some explanation here: Keeping your UI Responsive and the Dangers of Application.DoEvents

Is there any way to update a progress bar without using a background thread?

I have a long calculation that happens in a button click event, and after each iteration, I want the progress bar to update. I was wondering if there is any way to do this without using a background thread:
private void btnGetData_Click(object sender, RoutedEventArgs e)
{
progressBarGetData.Minimum = 0;
progressBarGetData.Maximum = recordCount;
progressBarGetData.Value = 0;
while(long iteration going on)
{
//do work
progressBarGetData.Value += 1;
}
}//end button click
The progress bar doesn't increment slowly on the screen. Do I have to use a background worker?
Thank you.
It's not the progress bar that's the main problem - it's the fact that you're performing a long-running synchronous operation in the UI thread. That will freeze the whole UI, giving a terrible user experience.
If you can convert your loop to use asynchronous calls (e.g. if it's only long-running because it's talking to web services or a database) then that's one option - and one which is made much easier in .NET 4.5.
Otherwise, you will need a separate thread - either started explicitly or using BackgroundWorker.

WPF TabControl tab changes not refreshed

I have a WPF TabControl with two TabItems. I am trying to change the selected tab on code behind on a Button click event and execute some other code. In this example:
private void Button_Click(object sender, RoutedEventArgs e)
{
ConvertDataTabControl.SelectedIndex = 1;
System.Threading.Thread.Sleep(2000);
...
}
I would expect the UI to refresh and move from Tab 0 to Tab 1 and only then execute the Sleep method, but the UI is refreshed only after Button_Click finishes execution. I tried calling InvalidateVisual, but it does not work.
Is there a way to force the UI to refresh before executing Sleep?
Your code runs on the UI thread by default, so nothing else can be executed on the UI thread (such as updating the layout) until the thread finishes executing.
There are many ways of releasing control of the UI thread before the code finishes executing, but I find the simplest is to use a Task from the Task Parallel Library which can be used to run code on a separate thread.
For example,
Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
// Other code here
});
It should be noted that UI objects can only be modified on the UI thread, so if your "other code here" updates a UI object, you'll probably want to use the Dispatcher to execute code on the UI thread, like this:
Dispatcher.BeginInvoke(() =>
{
// Code to update the UI
});
try
Dispatcher.BeginInvoke(()=>
{
ConvertDataTabControl.SelectedIndex = 1;
});
The problem is you are doing your work (sleep) on the UI thread. You can use a task/backgroundworker/etc to do the work in an other thread and then do set the ui changes back to the UI thread:
private void Button_Click(object sender, RoutedEventArgs e)
{
Dispatcher callback = Dispatcher.CurrentDispatcher;
ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
{
//Do some work
System.Threading.Thread.Sleep(2000);
//callbackk to ui thread to do ui work. You can also use BeginInvoke...
callback.Invoke(new Action(() => {ConvertDataTabControl.SelectedIndex = 1;}));
//Do some more work
System.Threading.Thread.Sleep(2000);
...
}
}
It is just a example to get the idea.

Categories