The Situation
I have a .NET Framework 4.8 Microsoft-Visio-like WPF application that enables the user to place controls on a canvas and modify them. I keep references to all these elements in an ObservableCollection in my ViewModel which is then set as ItemsSource on my Canvas.
To save the controls and various other aspects of the current project I'm serializing everything to JSON using the Newtonsoft serializer and write the result to a file. For this, I have created a dedicated model for serialization which I populate in the Save() method. At one point, I directly serialize the UI elements as content into another object like so:
var designerElements = CanvasElements.Select(element =>
{
var wrapper = element.FindVisualAncestor<DesignerElementWrapper>();
var designerElement = new DesignerElement
{
Size = new Size(wrapper.Width, wrapper.Height),
Position = new Point(Canvas.GetLeft(wrapper), Canvas.GetTop(wrapper)),
ContentJson = JsonConvert.SerializeObject(element, settings),
ContentType = element.GetType(),
<...>
};
return designerElement;
}
Currently, the saving/serialization work is done on the UI thread because this was convenient and worked well for relatively few objects.
The Problem
Now with the projects growing larger and larger saving the project, especially serialization, takes quite a while and starts significantly impacting (freezing) the UI.
I'd now like to relay the serialization or ideally all the saving work to a thread pool thread:
var designerElements = Task.WhenAll(CanvasElements.Select(async element =>
{
<...>
var contentJson = await Task.Run(() => JsonConvert.SerializeObject(element, settings));
var designerElement = new DesignerElement
{
<...>
ContentJson = contentJson,
<...>
};
<...>
}
// Then serialize designerElements and write result to file
or
Task.Run(() =>
{
// Gather data, project data into serializable model,
// serialize model (incl. UI elements) and write result to file
}
The problem is, that my UI elements obviously belong to the UI thread and I now get InvalidOperationExceptions when serializing, due to the background thread trying to access objects owned by the UI thread.
The Question
How do I deal with this in a thread-safe manner without blocking the UI thread?