-
Notifications
You must be signed in to change notification settings - Fork 697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: WinUI3 dispatchers (And why is Window.Current null in Desktop?) #2609
Comments
Your stated expected behavior (WinUI UWP) does not match your bug description (WinUI Desktop). Is that intended?
The CoreDispatcher is only available for UWP apps currently. Can you use the Dispatcher class from .NET instead? |
@Felix-Dev sorry I probably confused you, I didn't state that it's a bug, it was just my expectation. I tried to get the dispatcher from .NET as you kindly suggested me. This returned null: var dispatcher = Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread); but I was able to get the dispatcher like this: var dispatcher = Windows.Threading.Dispatcher.CurrentDispatcher; I have to admit that I'm confused, because I checked that for example a text box returns a CoreDispatcher from it's Dispatcher property: Windows.UI.Core.CoreDispatcher dispatcher = MyTextBox.Dispatcher I'm used from WPF that this is true: System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread) == MyTextBox.Dispatcher Isn't that CoreDispatcher confusing in WinUI Desktop apps, if it's only used in UWP apps currently? Thank you, Tomas |
Your steps to reproduce are
But you say your expectation is
As such you are mixing up UWP/Desktop packaging in your issue description and were asked to clarify which of them is actually your problem. |
Packaged (WinUI in Desktop) app is my actual problem. |
Yes it was intended, but it is obviously confusing, sorry again. So my main issue was to get the correct current dispatcher for switching from a background thread to the main UI thread. I wanted to use Window.Current.Dispatcher for that purpose in a Packaged (WinUI in Desktop) app, but it was null. |
@tomasfabian Interesting that you get a valid |
In a UWP WinUI app there's guaranteed to be one Window on the UI thread, because multiple windows aren't supported. To create a second Window you need to create a new thread. So Window.Current returns the Window on the calling thread. In a Desktop WinUI app, in the current preview only one Window can be created, but the plan is to allow multiple Windows to be created on the same thread. So Window.Current doesn't make sense any longer, and now just returns null. (It's behavior is unchanged in a UWP app.) About dispatchers, CoreDispatcher only works in a UWP app, but DispatcherQueue always works. DispatcherQueue isn't new to the WinUI preview, it's been around for a while, but what is new in the preview is that Xaml elements (and Window) have a DispatcherQueue property so that you can get one for any element. |
@Felix-Dev that's alright. Your constructive explanation was actually very helpful for me to learn that there is another (not used?) core dispatcher in the Desktop version which misled me. For all I'm going to change the title to WinUI 3 dispatchers (Why is Window.Current null?) if it's not a problem for anyone. I realize now, based on the information gathered by you, that my original question should be split to two issues (these issues are coupled via Dispatcher property):
Originally I used Window.Current only to get the dispatcher, but it was null (and of type CoreDispatcher).
public async void Init()
{
await MyTextBox.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
});
} @MikeHillberg DispatcherQueue is not new in WinUI, but it is new for new/adapting developers. Is this the way how to call DispatcherQueue? DispatcherQueueSyncContext.Current.Post(c =>
{
}, null); How is it possible to get the "current" Dispatcher for ticks for example? var timer = DispatcherQueue.CreateTimer(); I wasn't able to find this in the documentation. |
You're right that the DispatcherQueue isn't new, what's new to WinUI3 is that Xaml elements have a DispatcherQueue property. This works whether in a UWP or Desktop app, whether C++ or C#. .Net has the similar but abstract concept of a SynchronizationContext, and in WinUI3 an implementation is provided that wraps the current thread's DispatcherQueue. As a result, in a c# app there's two ways to post: public MainWindow()
{
this.InitializeComponent();
SynchronizationContext.Current.Post(
(o) => Debug.WriteLine("Sync Context"),
null);
this.DispatcherQueue.TryEnqueue(() => Debug.WriteLine("Dispatcher Queue"));
} |
I found var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
dispatcherQueue.TryEnqueue(() => Debug.WriteLine("Dispatcher Queue"));
var timer = dispatcherQueue.CreateTimer();
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += Timer_Tick;
timer.Start(); @MikeHillberg you wrote that DispatcherQueue works also in UWP. I tried it and the methods are marked as Experimental. I would usually do this with MVVM, but I tried the code bellow to verify where will be the continuation executed and in UWP it was on a worker thread, not on main thread: await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
myButton.Content = "Updated content";
}); In spite of that it was successfully updated. Interesting... I'm used to that the main thread is the main UI thread. Thank you |
@MikeHillberg , you commented that "CoreDispatcher only works in a UWP app". However, it is available on a WinUI 3 desktop app that is in an MSIX package. This is consistent with the WinUI documentation, which says that the CoreDispatcher (presumably one that works) can be obtained from a ui element (https://docs.microsoft.com/en-us/windows/winui/api/microsoft.ui.xaml.dependencyobject.dispatcher?view=winui-3.0-preview) To access the CoreDispatcher, create a Window in OnLaunched, assign the Content of the window to be a UI element ("rootElement"), and subscribe to the Activated event of the Window. Then in the Activated event, test that the WindowActivationState is CodeActivated or PointerActivated and access rootElement.Dispatcher (not Window.Dispatcher). Another way to test this is to compile the Xaml Controls Gallery branch for WinUI 3 - Desktop. In ButtonPage.xaml.cs, add the following lines to the Button_Click method (and make the method async):
It works ... NOT! When you click the button it prints "Dispatching..." and "Dispatched!" but not "Awaited". |
Seems like it might be mighty sensible to make UWP and desktop behave the same way and have the same capabilities with regard to Windows. UWP is just a security model after all, not a model for application design. |
@MikeHillberg what I don’t understand about using DispatcherQueue instead of CoreDispatcher is that DispatcherQueue.TryEnqueue returns synchronously but CoreDispatcher.RunAsync returns an awaitable. How should I achieve the same effect as RunAsync using DispatcherQueue? |
I would use a Semaphore and wait on it from the non-UI thread. CoreDispatcher.RunAsync can be a bit difficult to use, because it returns/completes early if the code itself makes an async call. @scott-moore-ms FYI |
@MikeHillberg I did it with a TaskCompletionSource, which is a bit lighter-weight than Semaphore. As a philosophical comment, it seems funny to have the Windows.UI.Core namespace mixed up in the signatures in WinUI. |
@sjb-sjb, about WinUI referencing types in Windows.UI.Core: WinUI isn't meant to be a complete stack; it's a layer. So it references types in Windows.UI.Core, Windows.Storage, Windows.Media, etc. |
I witness too Win UI desktop is launched as a Uno Head. |
@MikeHillberg Can I ask, in WPF all controls and window inherit DispatcherObject so we have a reference & can call it quite simply from anywhere. How do I get a dispatcher for WinUI 3 desktop, at the moment DIspatcherQueue in my VM is always null? |
In WinUI2 most UI objects have a CoreDispatcher, like DependencyObject.Dispatcher. In WinUI3 we've moved to CoreDispatcher's successor: DispatcherQueue. So you should be able to get that for WinUI3? |
@MikeHillberg yeah thanks appreciate that, ive managed to sus it out. I added the DispatcherQueue as a property on my VM. Was a little to used to 'the WPF way' |
Is the difference that in WPF your ViewModel was deriving from DispatcherObject? (WinUI doesn't have DispatcherObject, instead essentially DispatcherObject got merged onto DependencyObject.) |
@MikeHillberg I was generally using MVVM LIght's DispatcherHelper & forgot just how much it was doing & how helpful it was 😉 |
Is there an alternative for Window.Current.Content for WinUI 3? |
I don't understand.
this is protected How do I use it? Why did you make this? |
With Reflection |
I found my way around my issue by getting the XamlRoot. |
I wanted to create a Rx DispatcherScheduler for WinUI 3 Preview 1 based on UWP implementation, but the static readonly property Window.Current is null. How should I or the Rx team get the corresponding CoreDispatcher?
Steps to reproduce:
Steps to reproduce the behavior:
Create new Blank App, Packaged (WinUI in Desktop) app
Open MainWindow.xaml.cs and try get Window.Current
Expected behavior:
Window.Current in Blank App, Packaged (WinUI in UWP) returns a window instance
The text was updated successfully, but these errors were encountered: