Skip to content
Shiny.NET

Main Thread

Often when you publish an event, it can often be from a background thread or timer. As such, if you want to update your .NET MAUI, you need to be on the main thread. Transitioning on the main thread is not difficult, but you need an error trap to prevent your app from crashing out and you generally want to log those errors so you can fix them. This is where the [MainThread] attribute comes in.

There may also be cases where you want to ensure your request handler executes on the main thread as well. Maybe you want to display a dialog to a user to confirm an action or request permission to use some device hardware. Well… you need to be on the mainthread to do that for Android & iOS.

Only the handler runs on the main thread. Whatever awaits the Publish/Send/Request — and, for events, the rest of the handler fan-out — is not resumed on the main thread when the handler finishes; it continues where it would have without the attribute (its captured synchronization context, or the thread pool under ConfigureAwait(false)). A publish made from a background thread therefore stays in the background after a [MainThread] handler has run.

If the CancellationToken is already cancelled by the time the main thread picks up the work, the handler is skipped and the caller receives a TaskCanceledException.

public partial class MyViewModel : BaseViewModel, IEventHandler<MyEvent>, IEventHandler<MyOtherEvent>
{
[MainThread]
public async Task Handle(MyEvent myEvent, IMediatorContext context, CancellationToken cancellationToken)
{
// this will execute on the main thread, you are safe to update labels/collections/etc
}
public async Task Handle(MyOtherEvent myOtherEvent, IMediatorContext context, CancellationToken cancellationToken)
{
// this event handle is not marked with [MainThread] so it will execute on the thread
}
}
public partial class MyRequestHandler : IRequestHandler<MyRequest, MyResponse>
{
[MainThread]
public async Task<MyResponse> Handle(MyRequest request, IMediatorContext context, CancellationToken cancellationToken)
{
// this will execute on the main thread, from here, you may want to
// * request a dialog
// * navigate somewhere using navigation service
// * request user permissions to use device hardware on mobile
}
}