Application called an interface that was marshalled for a different thread?
Prefacing that I'm very new to C#: I'm trying to implement a very basic timer but I'm getting an wrong thread error in the event function of
System.Timers.Timer
. An image of the error is attached and the code that I'm working with is below
For reference TimerDisplay
is referring to the label in the xaml
I feel like I might be doing something wrong from a theoretical point of view with how the eventhandler is defined.19 Replies
Just to clarify, the error is happening as soon as I try and start the program, I see things render and then the exception. It's not in relation to clicking a button
as a high-level rule, .NET UI frameworks like winforms and WPF mandate that you only make changes to the UI on the same thread that manages the UI
this is obviously a limitation, so to get around this they offer a marshaller or 'dispatcher' which handles updating the UI from other threads for you
try replacing
TimerDisplay.Text=timeLeft.ToString();
with Dispatcher.Invoke(() => TimerDisplay.Text=timeLeft.ToString());
you may need to add a using statement for Dispatcher
I'll give it a go, really appreciate the help. So just to clarify my understanding, the
System.Timers.Timer
would operate on any thread available and this may differ from the UI thread and so when the Elapsed Event is processed, it is trying to update the UI from the Timer thread throwing the error?i forget the exactly details of how events interact with threads, but yes, i highly imagine that's what is happening
this is a really common thing people encounter when they try out these frameworks
incidentally, when you encounter async/await, one of the big motivations of that was to make it so you could do stuff like this without having to jump through hoops with dispatchers
I see, does that mean there is a more appropriate way of trying to accomplish what I'm doing using async/await?
I'm having trouble trying to get
Dispatcher
to work since it doesn't seem to be in the namespace? Maybe something different with MAUI, I'll keep digging to find a way to fix it but it's a major relief having some direction on how to resolve thisoh - i'm not familiar with MAUI so it may indeed be different, my apologies
assumed you were using WPF
Invoker
may also be a name to tryHi, you could try to get a reference of the UI thread through the SynchronisationContext class
https://learn.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext?view=net-7.0
In the constructor store the SynchronisationContext.Current value in a Private filed. So you can call the Post mehtod in your Event handler to execute the passed method on the UI thread.
SynchronizationContext Class (System.Threading)
Provides the basic functionality for propagating a synchronization context in various synchronization models.
I'll give it a go, thanks for the suggestion
Here is another solution spezific for MAUI
https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/main-thread?view=net-maui-7.0
Run code on the main UI thread - .NET MAUI
In .NET MAUI, event handlers may be called on a secondary thread. The MainThread class allows an application to run code on the main UI thread. This article describes how to use the MainThread class.
Amazing, the
MainThread.BeginInvokeOnMainThread
did the trick!
I think the other one would have worked as well but I was struggling to get the Post method to work. All the delegate stuff was a bit too confusing and my brain is friedI also came across this discussion earlier which I think is useful, it seems like MainThread has the potential to cause issues in certain circumstances. https://github.com/dotnet/maui/discussions/7518
GitHub
Which is better MainThread.Being/Invoke... VS Dispatcher.Dispatch.....
In MAUI, we now have multiple ways to execute code on the Main/Dispatcher/UI thread. The MainThread class was carried over from Xamarin.Essentials and offers a good, generic way to access the main ...
I'm going to try and get a bit more comfortable with Bindable objects and try to learn how the dispatcher works as well. It's kind of insane how something I expected to be simple turned out to have such a nuanced complexity.
I think the class mainthread is the way to go.
Especially when you operate on several different platforms.
Really appreciate the help from both of you. One last question if I may, should I be doing this differently?
I recall notions of maybe publish subscribe update property stuff which sounds like it could keep the publish and subscribe options on separate threads though not sure if that works in a MAUI context. And otherwise is async/await a better solution or sth? I'm sorry if my questions are completely incorrect, I'm like a day or two into the whole C# environment
I see, thanks
Glad I could help.
Don't worry about async/await until you start seeing methods that return a
Task
type - that'll be with stuff that, say, makes HTTP calls or reads to the filesystem. It's useful to know about but it's also a big topic, so it's ok to ignore it now so you don't get overwhelmedFab, hopefully some of my understanding about async/await from JS will carry over. Also it turns out there is a DispatchTimer that fires on the UI thread. Turns out I went about this the hard way
oh, yeah, afaik JS was heavily inspired by C#'s async/await. We just call things Tasks instead of Promises. Best of luck in .NET
Thank you!