Using the C# Dispatcher in Wpf Applications

Using the C# Dispatcher in WPF Applications

Your app has a main UI thread (usually ManagedThreadId==1). Typically in a chat app your events will come in on other threads (either dedicated socket listen threads or thread pool threads from listening code). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:

using System.Windows.Threading; // For Dispatcher.

if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}

If you're in the main window you can use:

Dispatcher.BeginInvoke(...

If you're in someother context eg a view model then use:

Application.Current.Dispatcher.BeginInvoke(  

Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.

MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.

Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.

CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.

Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:

public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}

Which can be called as:

DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});

Multi-threaded WPF Application: Dispatcher Invoke. A more efficient way?

You can start by being less verbose in your calls, i.e.

Application.Current.Dispatcher.Invoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));

Another trick that I like to use is to make a shortcut method like this:

public static void UiInvoke(Action a)
{
Application.Current.Dispatcher.Invoke(a);
}

Then you have even less to do, as in:

UiInvoke(() =>_aCollection.Add(new Model(aList[i], aSize[i])));

Using dispatcher.Invoke() is really just how you get the action back onto the UI thread, which is probably where these objects (_aCollection) were created in the first place. If the items in question don't have direct interaction with the UI thread, then you can create / manipulate them on a different thread, removing the need to use the dispatcher. Of course this approach could become more complicated depending on what you are doing.

Use of Application.Current.Dispatcher for events outside dispatcher thread

Should "Application.Current.Dispatcher.Invoke" be put in the worker thread raising an event or in the UI code handling the event?

The latter I would say. Mainly because the worker thread may be started in a class library that has no knowledge about any System.Windows.Application object.

So the worker thread should simply raise the event and then the client application is responsible for only updating the UI elements on the dispatcher thread on which they were originally created on. A class library shouldn't have to care about or even know about any dispatcher.

Dispatcher to Thread relationships in WPF

WPF application has 2 threads (one
for input, the other for UI)

This statement is not entirely correct. A WPF application has only one UI thread that handles all the UI interaction and user input. There is also a "hidden" thread responsible for rendering, but normally developers don't deal with it.

Dispatcher / Thread relationship is one to one, i.e. one Dispatcher is always assoticated with one thread and can be used to dispatch execution to that thread. Dispatcher.CurrentDispatcher returns the dispatcher for the current thread, that is, when you call Dispatcher.CurrentDispatcher on a worker thread you get a dispatcher for that working thread.

Dispatchers are created on demand, which means if you access Dispatcher.CurrentDispatcher and there is no dispatcher associated with the current thread, one will be created.

That being said, the number of dispatchers in the application is always less or equal to the number of threads in the application.

WPF Dispatcher for dotnet 5

System.Windows.Threading.Dispatcher is certainly available in .NET 5.

If you are creating a class library, the project file should look like this:

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>

</Project>

Prevent using Dispatcher.Invoke in WPF code

Regarding this:

This works, but this is not the only event and thus makes my code
horrible ugly

Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.

Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.

This means that there should be nothing like this:

UserWindow.Visibility = Visibility.Hidden;

anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.

Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:

<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>

Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.

Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.

When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.

Therefore our first step is to have all our ViewModels inherit from a class like this:

(taken from this answer):

public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}

Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:

public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}

Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:

public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.

DataContext = new UserViewModel(); //here we set the DataContext
}

As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.

This same concept applies to EVERYTHING in WPF.

One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.

You can read more about that in the link and also the MSDN DataBinding page linked above.

Let me know if you need further details.



Related Topics



Leave a reply



Submit