EDIT I removed my code as it was incomplete and confusing, but added a minimal reproducible exemple. All the text below has been slightly rewritten to clarify my question, adding information from the comment, but without adding anything I didn't know when asking the question, in order to keep the comments relevant for a future reader. I also changed the title.
I'm trying to perform some calculations in the background to make UI more responsive.
Before trying that, I made sure my Simulator
class works fine. My calculations are launched by calling a class method from Simulator. When I try to perform calculations (whether with await, TaskFactory or other such tools), I can't access the MeshGeometry3D
class member that stores the 3D model I need : 'The calling thread cannot access this object because a different thread owns it.'
I could overcome the difficulty by passing arguments to the task, but the volume of data is quite big, so if that's feasable I would prefer working on a existing object.
I have read this, this, this, this, this and this but those answers all assume an UI element, and that's not the case : my MeshGeometry3D member is not used in the UI, before, during or after the background calculation.
I read that this is a common problem with people getting started, and that I should invoke the dispatcher when trying to access data that belong to the main thread, but I don't have access to the dispatcher from my simulator class (side question, is there a way to access the dispatcher that created an element ?)
So I'm trying to understand what is happening, in order to decide the best way to handle the problem. From what I read so far, I see 2 options :
- Use a dispatcher to access the object
- Create the culprit MeshGeometry3D each time I perform the calculations
Minimal reproducible example
MainWindow.xaml.cs
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Media3D;
namespace DispatcherObject
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Simulator Simulator;
public MainWindow()
{
InitializeComponent();
Simulator = new Simulator();
}
private void CreateMesh_Click(object sender, RoutedEventArgs e)
{
Simulator.mesh = new MeshGeometry3D();
}
private void AccessMesh_Click(object sender, RoutedEventArgs e)
{
CallSimulatorMethod();
}
private async void CallSimulatorMethod()
{
await Task.Run(() =>Simulator.AccessMesh());
}
}
}
Simulator.cs
using System.Windows.Media.Media3D;
namespace DispatcherObject
{
class Simulator
{
public MeshGeometry3D mesh;
public void AccessMesh()
{
var foo = mesh.Normals;
}
}
}
With the previous code, if you click the AccessMesh button first, you get a System.NullReferenceException, of course.
If you click CreateMesh first, you get this exception :
System.InvalidOperationException HResult=0x80131509 Message=The calling thread cannot access this object because a different thread owns it. Source=WindowsBase StackTrace: at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.DependencyObject.GetValue(DependencyProperty dp) at System.Windows.Media.Media3D.MeshGeometry3D.get_Normals() at DispatcherObject.Simulator.AccessMesh() in C:\Users\geraud\source\repos\DispatcherObject\DispatcherObject\Simulator.cs:line 11 at DispatcherObject.MainWindow.b__4_0() in C:\Users\geraud\source\repos\DispatcherObject\DispatcherObject\MainWindow.xaml.cs:line 33 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.Execute()
This exception was originally thrown at this call stack: [External Code] DispatcherObject.Simulator.AccessMesh() in Simulator.cs DispatcherObject.MainWindow.CallSimulatorMethod.AnonymousMethod__4_0() in MainWindow.xaml.cs [External Code]