I am currently writing my first program on C# and I am extremely new to the language (used to only work with C so far). I have done a lot of research, but all answers were too general and I simply couldn't get it t work.
So here my (very common) problem: I have a WPF application which takes inputs from a few textboxes filled by the user and then uses that to do a lot of calculations with them. They should take around 2-3 minutes, so I would like to update a progress bar and a textblock telling me what the current status is. Also I need to store the UI inputs from the user and give them to the thread, so I have a third class, which I use to create an object and would like to pass this object to the background thread. Obviously I would run the calculations in another thread, so the UI doesn't freeze, but I don't know how to update the UI, since all the calculation methods are part of another class. After a lot of reasearch I think the best method to go with would be using dispatchers and TPL and not a backgroundworker, but honestly I am not sure how they work and after around 20 hours of trial and error with other answers, I decided to ask a question myself.
Here a very simple structure of my program:
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
I would be very grateful if someone had a simple explanation how to update the UI from inside the testmethod. Since I am new to C# and object oriented programming, too complicated answers I will very likely not understand, I'll do my best though.
Also if someone has a better idea in general (maybe using backgroundworker or anything else) I am open to see it.
Thank God, Microsoft got that figured out in WPF :)
Every
Control
, like a progress bar, button, form, etc. has aDispatcher
on it. You can give theDispatcher
anAction
that needs to be performed, and it will automatically call it on the correct thread (anAction
is like a function delegate).You can find an example here.
Of course, you'll have to have the control accessible from other classes, e.g. by making it
public
and handing a reference to theWindow
to your other class, or maybe by passing a reference only to the progress bar.You're right that you should use the
Dispatcher
to update controls on the UI thread, and also right that long-running processes should not run on the UI thread. Even if you run the long-running process asynchronously on the UI thread, it can still cause performance issues.It should be noted that
Dispatcher.CurrentDispatcher
will return the dispatcher for the current thread, not necessarily the UI thread. I think you can useApplication.Current.Dispatcher
to get a reference to the UI thread's dispatcher if that's available to you, but if not you'll have to pass the UI dispatcher in to your background thread.Typically I use the Task Parallel Library for threading operations instead of a
BackgroundWorker
. I just find it easier to use.For example,
where
If this is a long calculation then I would go background worker. It has progress support. It also has support for cancel.
Here I have a TextBox bound to contents.
First you need to use
Dispatcher.Invoke
to change the UI from another thread and to do that from another class, you can use events.Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:
UPDATE:
As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:
@1) How to start the "synchronous" calculations and run them in the background
@2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the
async
/await
the UI is not blocked (BTW: such event handlers are the only valid usages ofasync void
as the event handler must returnvoid
- useasync Task
in all other cases)@3) Instead of a new
Thread
we now use aTask
. To later be able to check its (successfull) completion we save it in the globalcalcTask
member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.@4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create
async Task CalcAsync()
and thenawait Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false);
(FYI: theConfigureAwait(false)
is to avoid deadlocks, you should read up on this if you useasync
/await
as it would be to much to explain here) which would result in the same workflow, but as theTask.Run
is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.@5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily
@6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got
null
after theif
-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside theRaise
method.Felt the need to add this better answer, as nothing except
BackgroundWorker
seemed to help me, and the answer dealing with that thus far was woefully incomplete. This is how you would update a XAML page calledMainWindow
that has an Image tag like this:with a
BackgroundWorker
process to show if you are connected to the network or not:For more information: https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx
I am going to throw you a curve ball here. If I have said it once I have said it a hundred times. Marshaling operations like
Invoke
orBeginInvoke
are not always the best methods for updating the UI with worker thread progress.In this case it usually works better to have the worker thread publish its progress information to a shared data structure that the UI thread then polls at regular intervals. This has several advantages.
Invoke
imposes.BeginInvoke
were used from the worker thread.Invoke
.Invoke
andBeginInvoke
are expensive operations.So in your
calcClass
create a data structure that will hold the progress information.Then in your
MainWindow
class use aDispatcherTimer
to periodically poll the progress information. Configure theDispatcherTimer
to raise theTick
event on whatever interval is most appropriate for your situation.