Last active
April 18, 2024 17:46
-
-
Save smourier/d1961e2a8d18e762746cebe5948d36db to your computer and use it in GitHub Desktop.
Create a WinUI3 Xaml window in another thread
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.UI.Xaml; | |
namespace WinUIAppFx | |
{ | |
public partial class App : Application | |
{ | |
private Window m_window; | |
public App() | |
{ | |
InitializeComponent(); | |
} | |
protected override void OnLaunched(LaunchActivatedEventArgs args) | |
{ | |
if (m_window != null) // WindowsXamlManager.InitializeForCurrentThread will call this too, show the main only once | |
return; | |
m_window = new MainWindow(); | |
m_window.Activate(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Window | |
x:Class="WinUIAppFx.MainWindow" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> | |
<Grid> | |
<Button Click="Button_Click">click</Button> | |
</Grid> | |
</Window> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.Versioning; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Microsoft.UI.Dispatching; | |
using Microsoft.UI.Xaml; | |
using Microsoft.UI.Xaml.Hosting; | |
[assembly: SupportedOSPlatform("windows10.0.17763.0")] | |
namespace WinUIAppFx | |
{ | |
public sealed partial class MainWindow : Window | |
{ | |
private MyOtherWindow _myOtherWindow; | |
public MainWindow() | |
{ | |
InitializeComponent(); | |
} | |
private void Button_Click(object sender, RoutedEventArgs e) | |
{ | |
if (_myOtherWindow != null) | |
return; | |
Title = "On thread " + Environment.CurrentManagedThreadId; | |
var thread = new Thread(state => | |
{ | |
// create a DispatcherQueue on this new thread | |
var dq = DispatcherQueueController.CreateOnCurrentThread(); | |
// initialize xaml in it | |
WindowsXamlManager.InitializeForCurrentThread(); | |
// create a new window | |
_myOtherWindow = new MyOtherWindow(); // some other Xaml window you've created | |
_myOtherWindow.AppWindow.Show(true); | |
// run message pump | |
dq.DispatcherQueue.RunEventLoop(); | |
}); | |
thread.IsBackground = true; // will be destroyed when main window is closed, behavior can be changed | |
thread.Start(); | |
// send some message to the second window to check it's handled from another thread | |
// note: real code should wait for _myOtherWindow to be fully initialized... | |
Task.Run(async () => | |
{ | |
for (var i = 0; i < 10; i++) | |
{ | |
await Task.Delay(1000); | |
_myOtherWindow.DispatcherQueue.TryEnqueue(() => | |
{ | |
_myOtherWindow.Title = "#" + i + " on thread " + Environment.CurrentManagedThreadId; | |
}); | |
} | |
}); | |
} | |
} | |
} |
Everything was done and works by design, there are multiple non-STA threads indeed and there may be a race condition especially if you remove the 1000 sec wait, this is why there is a "real code should wait for _myOtherWindow to be fully initialized..." comment.
Do you understand what SynchronizationContext
is and its role in await
ing something on a UI thread? Trust me, you need my line or something like it, or else any UI code run on the 2nd window that includes async/await will break.
Or don't believe me, I'm fine either way.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Try putting
await Task.Delay(1)
before_myOtherWindow.Title = "#" + i + " on thread " + Environment.CurrentManagedThreadId;
. I believe you'll get a thread exception. You'll start on the second UI thread but afterTask.Delay
you'll be on a background worker.