I found an issue where touch would be disabled.
If you want to create a custom splash screen and creates a new Thread for it, and then create the main window in the Main Thread, touch will no longer work.
This same code works fine in .NET Framework.
Project for reproducing the issue can be found here including description: https://github.com/mbendtsen/touch_issue_net_core
Requires a touch enabled device.
public partial class App : Application
{
private Thread _splashThread;
protected async override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ShowSplash();
// Simulate long running work.
await Task.Delay(2000);
_splashViewModel.SetInitialized();
var window = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
window.Show();
}
private void ShowSplash()
{
_splashViewModel = new SplashViewModel();
_splashThread = new Thread(() =>
{
new Splash { DataContext = _splashViewModel }.Show();
Dispatcher.Run();
});
_splashThread.SetApartmentState(ApartmentState.STA);
_splashThread.IsBackground = true;
_splashThread.Name = "Splash";
_splashThread.Start();
}
}
Found a workaround. Add this line before showing the splash screen: _ = Tablet.TabletDevices;
@mbendtsen This looks like an issue with triggering tablet initialization on the new thread. Thanks for pointing it out. Usually a new window should initialize the stylus thread (via HwndStylusInputProvider I believe), but it doesn't seem like that is happening.
And I think Using win32 to show the splash screen 路 Issue #2031 路 dotnet/wpf can solve it
Just for the record, we do use multiple UI threads for things other than splash screens so just implementing splash screens differently is probably not enough, the actual bug should be fixed. (For example we are showing a notification dialog on a separate thread when the main UI thread is blocked by plugin or scripted operations for too long.)
@mbendtsen Is it the same WPF shows that some windows in multithreading will be locked in the PenThreadWorker constructor 路 Issue #928 路 dotnet/wpf ?
These are not the same issue I think. Nothing is blocking the main Dispatcher thread, it's just not bringing up tablets.
At current, what seems to be happening is that the query for WISP tablets (that runs on the PenThread) is not returning any tablets on the main Dispatcher thread. I am not sure why this is. The PenThread is alive and seems to be processing work. I'm digging into this further.
What the actual issue seems to be is that WPF in .NET Core has to use Registration-Free COM in order to instantiate PenIMC objects. In .NET Framework 4.8 these were globally registered, but that cannot work for .NET Core. For some reason, it seems the use of the PimcManager objects to query tablets are failing to function properly in multi-UI thread scenarios. The calls into these COM objects are failing.
I believe the solution is to create these objects with CoCreateFreeThreadedMarshaler. This way, the object is created by the first thread to allocate the IPimcManager, but the object will be useable no matter what apartment accesses it.
I still have to implement and test this out.
EDIT: Actually the manager should be thread static, so I'll have to look further at why subsequent calls seem to believe the objects are not registered.
Ah, the actual problem is that we were creating a single activation context. We had the context being created in the static constructor for UnsafeNativeMethodsPenImc. This needed to be done per thread so that multiple PenThreads can access the COM objects.
When I make that change (registering a context per initial thread access to the class) everything seems to be working correctly. I have more testing to do to make sure everything looks good and I need to add logic for when the thread dies, but this seems to correct the issue.
@fabiant3 There are other bugs that I have seen that likely are related to this. The PR is in flight and has not been reviewed. I'm moving this bug to triage for now to bump it into view.