I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I've tried to use this code:
await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This doesn't work, since Windows.UI.Core.CoreWindow.GetForCurrentThread()
returns null
.
My second try was:
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This fails, because Windows.ApplicationModel.Core.CoreApplication.MainView
throws a System.InvalidOperationException
.
Another way should be:
await System.Windows.Threading.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
But the System.Windows.Threading
namespace is not available for me, since I'm using .Net 6 and it's not longer supported in it.
Any idea, how I can execute some code from a non-main thread inside the main-thread (UI thread)?
CodePudding user response:
I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I strongly recommend that you don't. It's far cleaner to have your async
methods use something like IProgress<T>
to indirectly update the UI as necessary. If you structure your code so that the main thread calls the background threads instead of the background threads manipulating the UI through the UI thread, then you'll end up with a much cleaner design where your logic is less tied to your UI controls.
That said, if you really want to, then the solution is to capture the dispatcher on the UI thread before the background work begins, and have the background work use that dispatcher (not the "current dispatcher") when posting work to the UI thread.
CodePudding user response:
I found a bad solution by using the Windows messages:
internal class WndMessages : Control
{
public const int UM_RUNONUITHREAD = WM_USER 0;
private static ConcurrentQueue<DispatchedHandler> _runOnUIThreadQueue;
private static WndMessages Instance;
private static IntPtr hWnd;
public WndMessages()
{
Debug.Assert(Instance == null);
Instance = this;
hWnd = Handle;
Text = ToString();
Width = 0;
Height = 0;
Visible = false;
_runOnUIThreadQueue = new();
}
public static void RunOnUIThread(CoreDispatcherPriority priority, DispatchedHandler handler)
{
Debug.Assert(hWnd != default);
Debug.Assert(priority == CoreDispatcherPriority.Normal || priority == CoreDispatcherPriority.Low);
_runOnUIThreadQueue.Enqueue(handler);
if (priority == CoreDispatcherPriority.Normal)
SendMessage(hWnd, UM_RUNONUITHREAD, 0, 0);
else
PostMessage(hWnd, UM_RUNONUITHREAD, 0, 0);
}
private void RunOnUIThreadInvoke()
{
if (_runOnUIThreadQueue.TryDequeue(out DispatchedHandler handler))
handler.Invoke();
}
protected override void WndProc(ref Message message)
{
switch (message.Msg)
{
case UM_RUNONUITHREAD:
RunOnUIThreadInvoke();
break;
}
base.WndProc(ref message);
}
}
The WndMessages class needs to be created on the main thread. After this, the following code works from any thread:
WndMessage.RunOnUIThread
(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
... a bad solution, I prefer to get a better one.