Reputation: 21341
For many Maui apps, class Mainthread
has convenient methods to run code on the main UI thread, from code that may be running in a background thread.
However, MainThread
lacks the equivalent of method IDispatch.DispatchDelayed
.
How do DispatchDelayed
in Maui (.Net 8 or above)?
Upvotes: 2
Views: 210
Reputation: 21341
MainThread
, use the UI object's Dispatcher
:public partial class MainPage : ContentPage
{
...
public void SomeMethod()
{
// "this." added for clarity. Can be omitted.
this.Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(100), () =>
{
// Your code here.
});
}
}
MainThread
is Application.Current.Dispatcher
: Application.Current.Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(100), () =>
{
// Your code here.
});
As noted in the "See caveats" link in question, if an app opens more than one window (on a desktop OS), both MainThread
and Application.Current.Dispatcher
refer to the UI thread of the first window opened by the app.
To dispatch on a second window's UI thread, from non-UI-code, it is necessary to pass the window's Dispatcher
to that code, so that DispatchDelayed
can be called on it. Something like:
// Code in some UI code-behind class:
SomeModel someModel = ...;
someModel.MyMethod(this.Dispatcher, ...);
// code in "class SomeModel"
public void MyMethod(IDispatcher dispatcher, ...)
{
dispatcher.DispatchDelayed(...);
}
DispatchDelayed
, using MainThread
: MainThread.BeginInvokeOnMainThread(async () => {
await Task.Delay(100); // Time in milliseconds.
// Your code here
});
An Example
I ran into a case in UI code behind, where there was a bug because I invoked code to run on UI thread, BUT that code needed to NOT RUN until the current UI method finished.
MainThread.BeginInvokeOnMainThread
runs code IMMEDIATELY, if already on the MainThread! It runs BEFORE returning from the current UI method.
This code was bugged (ran too early):
MainThread.BeginInvokeOnMainThread(async () => {
// My code here -- it ran before the containing method returned!
});
This code fixes it:
MainThread.BeginInvokeOnMainThread(async () => {
await Task.Delay(1); // A tiny delay, to queue the following code.
// My code here
});
I didn't want to have to write a separate line for the delay. The code was in a separate class. I did not want to bother passing around a dispatcher. I used this solution:
Application.Current.Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(1), () =>
{
// My code here.
});
I decided it was worth having convenience methods. Added these to my App
class:
public partial class App : Application
{
public static App It => (Application.Current as App);
/// <summary> Dispatches on the Dispatcher associated with first Window created; same as MainThread. </summary>
public static bool DispatchDelayed(TimeSpan timeSpan, Action action)
=> It.Dispatcher.DispatchDelayed(timeSpan, action);
/// <summary> Small delay to force "action" to be queued, even if already on MainThread.
/// This is useful if inside a method that needs to return, even when already on MainThread, before running "action".
/// </summary>
public static void QueueOnMainThread(Action action)
=> It.Dispatcher.DispatchDelayed(TimeSpan.FromMilliseconds(1), action);
Usage:
App.QueueOnMainThread(() =>
{
// My code here.
});
Alternate Usage, if need to call an async
method:
App.QueueOnMainThread(async () =>
{
await SomeMethod(...);
});
Where SomeMethod is defined:
public async Task SomeMethod(...)
{
}
Upvotes: 3