-2

I am trying to learn asynchronous programming in WPF/C# and I am stuck on asynchronous programming and using Dispatchers. Are they both different or are they or are to be used in the same scenario? I am willing keeping this question short as to not be vague because I understand that I am messing up between a concept and a function in WPF, but not enough to functionally use it correctly.

My doubt arises after I asked a question here, This is my question, I was told to use Dispatcher for this problem and that WPF runs on a single thread and you need to use BeginInvoke, here was where I first heard Dispatcher and before that I just user async and await with Task.Run these 3 keywords only.

But then this article is Asynchronous programming in C# with WPF.

All I need to do is load a few pages into a Grid and this happens at the start of the application (this part I think deals with Dispatcher as it is a part of the UI), and then CRUD to a Database.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Fiona
  • 77
  • 5
  • async/await and `Invoke()`/`InvokeAsync()`/`BeginInvoke()` are not mutually exclusive, depending on how the code works. That said, in WPF you will almost _never_ need to explicitly use an "invoke" method to marshal to the UI thread. When code running in a non-UI thread needs to interact with an object with thread affinity for the UI thread, data binding automatically handles marshaling property updates, while the `Progress` class handles the vast majority of other cases. In cases where the worker thread itself doesn't need access to UI objects, but you have a method that involves ... – Peter Duniho Jun 21 '21 at 03:10
  • ... one or more asynchronous operations (either running a worker thread with `Task.Run()` or calling some other method that is inherently async), interleaved with code that _does_ interact with a UI object, async/await fills that need. See duplicate for extensive details. – Peter Duniho Jun 21 '21 at 03:10

2 Answers2

3

I will explain in general terms, not exactly.

When you want to execute a method asynchronously, you create a task. This task is performed by an arbitrary thread from the thread pool.

But what if you need to make the method execute in a specific thread?
This is a common task for UI elements - they should always only run on the main thread of the application.

To solve this problem, Dispatchers for threads were created.
Each thread can only have one dispatcher.
It is created the first time you access it.
Using the Thread Dispatcher, you can execute your asynchronous method on this thread.

Invoke - Executes a method synchronously on the Dispatcher thread.
It's like just calling a regular synchronous method.
Execution of the main method (in which Invoke was called) will not continue until the method passed to Invoke is executed.
The thread in which the main method is executed also stops.
Invoke used extremely rarely.

InvokeAsync and BeginInvoke are asynchronous execution.
They differ in small details - I will not go into their explanation now.
These methods return a DispatcherOperation object.
With which you can interact with the task executing your method.
The most common use is BeginInvoke without receiving a DispatcherOperation.

The best way to load data is to create a Model with asynchronous methods (async-await). After loading the data, the ViewModel will receive this data and provide it in its properties. When the ViewModel property changes its value, you need to raise the PropertyChanged event (the ViewModel must necessarily implement INotifyPropertyChanged). In what streams this happens - it does not matter.

UI elements (WPF View) will receive this data through bindings of the ViewModel properties . The mechanism of bindings is designed in such a way that regardless of the thread in which the property has changed, the binding will update the UI element always in their Dispatcher.

With such an implementation, you do not need to worry about the streams in which data is received from the database or other work with data occurs.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
2

You should not use Dispatcher. Instead, you can use await or IProgress<T> to delegate work back to the UI thread.

E.g.:

private async void b1_Click(object sender, RoutedEventArgs e)
{
  // We are starting on the UI thread here.
  txtb1.Text = "";
  var watch = System.Diagnostics.Stopwatch.StartNew();

  await writeintxtbx();

  // After the await completes, we return to the UI thread because await captured the UI context.

  watch.Stop();
  var elapsedtm = watch.ElapsedMilliseconds;

  // Since we're on the UI thread, we can update UI elements.
  txtb1.Text += $"TOTAL TIME {elapsedtm} \n\n\n";
}

If you need to update UI elements with progress updates, then use IProgress<T>:

private async Task writeintxtbx()
{
  var progress = new Progress<string>(x => txtb1.Text += x);
  await Task.Run(() => Task1(progress));
  await Task.Run(() => Task2(progress));
}

private void Task1(IProgress<string> progress)
{
  progress?.Report($"Task 01 Done \n\n");
}

private void Task2() 
{
  progress?.Report($"Task 2 done \n\n");
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810