For Dotnet Maui, what the difference between Application.Current?.Dispatcher.Dispatch(async()) and MainThread.InvokeOnMainThreadAsync
Asked Answered
W

1

15

Title says it all, docs don't seem to say which should be preferred. They have the same arguments you can pass basically too.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.maui.applicationmodel.mainthread.invokeonmainthreadasync?view=net-maui-7.0

https://learn.microsoft.com/en-us/dotnet/api/microsoft.maui.dispatching.dispatcherextensions.dispatchasync?view=net-maui-7.0#microsoft-maui-dispatching-dispatcherextensions-dispatchasync(microsoft-maui-dispatching-idispatcher-system-action)

I think they both do the same thing functionally but I need to know best practice for my company's app. I don't see this question around already either.

Walhalla answered 14/12, 2022 at 16:23 Comment(1)
Does this help? Which is better MainThread.Being/Invoke... VS Dispatcher.Dispatch.Clipping
S
19

tl;dr #1: If an app has only one window, and you are not writing test cases that need the UI to be "mocked", it doesn't matter. You can use MainThread for convenience. [By default, an app only has one window. Currently, the only way to have more than one window, is to run on a Desktop OS, and use platform-specific code to open a second window.]

tl;dr #2: "best practice" is to use Dispatcher on some UI object. this.Dispatcher.Dispatch in code-behind. Application.Current.Dispatcher.Dispatch in non-UI code -- IF the app has only one window, or code needs to run on the first window opened.


mattleibow's explanation. Thanks to Ligun Shen-MSFT for this link as a comment on question.

... The MainThread is static so it is mostly correct. Let me explain.

When you have a UI component (BindableObject, Button, Page, Window), it knows which thread it is assigned to. Some platforms, like Windows and iOS, require that UI objects be created on the UI thread. It will throw otherwise. And, the dispatcher is initialized to that thread.

MainThread has no concept of the UI thread so picks the first one. In most cases, it is correct because most platforms only have 1 "main thread". Windows is an outlier to this because it supports windows on separate threads. ...


From senior Microsoft techie Rob Caplan's answer (google Rob Caplan Microsoft for more info about who he is):

"InvokeOnMainThread was an oversimplification - not all apps have a main thread or a singular UI thread. Associating the Dispatcher to a UI object (which will in turn be tied to a thread) is more general and better supports multi-window applications."

That is: accessing the Dispatcher on some UI object is "more general" than using MainThread.


Nitty-gritty details:

IF this is a Single Window app, AND you are writing code that is not code-behind (not part of a UI object), THEN Application.Current.Dispatcher is convenient and safe. So is MainThread.BeginInvokeOnMainThread. These two behave identically; it doesn't matter which you use in this case. However, consider various points below, such that Dispatcher is slightly favored as "best practice".

  • MOCK TESTING: its easier to make a mock IDispatcher, than to replace MainThread static class.

MORE DETAILS:

  • If you are in code-behind (code associated with some UI object), this.Dispatcher... is "preferred". (You can omit this.; I just added it for clarity.) It is simple to code, and will keep working if the code is ever needed inside a second window.

  • If you are NOT in code-behind, and ARE in a single window app, Application.Current.Dispatcher and MainThread methods behave identically. HOWEVER, Application.Current.Dispatcher is preferred, because it is an IDispatcher object. Thus, it has the same syntax as the above "code-behind" case. And you can easily refactor some logic, if you need to pass to a different IDispatcher. E.g. for MOCK TESTING. OR if in the future your code gets used in an app with multiple windows.

  • DO NOT use EITHER MainThread nor Application.Current.Dispatcher in an app with multiple windows. You MUST get hold of a UI object on the window being touched. If you are writing code in a UI element, use this.Dispatcher. [this. is optional; shown for clarity.] OR pass in to your method a reference to any UI element of that window (someUIElement.Dispatcher).


UPDATE: DispatcherQueue alternative for Microsoft-Windows-ONLY app that opens multiple windows, to pick correct dispatcher when not in code-behind. (If you only have one window, MainThread is simpler, when not in code-behind. And works cross-platform.)

Subatomic answered 14/12, 2022 at 23:38 Comment(5)
This seems correct and thank you for posting the correct answer. But then I am upset with MS for their abysmal documentation. It's absurd their official docs here learn.microsoft.com/en-us/dotnet/maui/platform-integration/… called "Create a thread on the .NET MAUI UI thread" is wrong then. I have been so so so frustrated with the documentation and bugs of Maui. I guess add this to the list. Why do the official docs have NO mention of the correct way, and the correct way is only found on some forum post? Obviously not your fault, ty again.Walhalla
Ah, good observation re docs. Looks like what happened is they updated the doc from Xamarin Forms (where it was Device.BeginInvokeOnMainThread), without full feedback from the devs. MainThread was the way to do it, a bit earlier in Maui. Before "multiple window" support on desktop. And before anyone realized Dispatcher was better for "testability". It isn't too bad; MainThread.BeginInvokeOnMainThread will work fine for most apps. There has been a push to "streamline/simplify" Maui for new programmers, to make it easier to get into. I think MainThread is part of this.Subatomic
I can't be the only one, so what do you mean with "Multi-Window app"? Personally I use shell with multiple pages but there is only one window open at a time.Wesley
@Wesley on any Desktop OS (Windows, MacOS, Linux), an app can open multiple windows at the same time. A window typically has a title bar with which it can be dragged around the screen. Inside that window, there may be multipleviews or panes or panels - these are all part of one window. Only if you are running on a desktop app, and make platform-specific calls to open a second window, does it become a "Multi-Window app". Most apps won't do this; then all of the above becomes moot; MainThread works fine.Subatomic
Note that this is not related to mobile OS support for multi-window mode. That refers to the OS showing at least two apps on screen at the same time. Each app only has one "window"; Maui's MainThread will be the thread that is displaying inside your app's "window". Your app is still what I call a "single window app".Subatomic

© 2022 - 2024 — McMap. All rights reserved.