I have a List<Polyline> that I need to generate in a second thread so as not to lose GUI responsiveness. After The thread is complete I need to access the List<Polyline> in the GUI thread to display. This is where I am running into a problem. I am invoking the copy logic on the UI thread with an anonymous method, copying the polylines into a List<Polyline> already initialized on the UI thread. I am getting “The calling thread cannot access this object because a different thread owns it.”
I have tried Clone and DeepCopy extension methods to copy from the thread List to the UI list but Polyline is not serializable.
Class ThreadedExecuter<T> where T : class
{
public delegate void CallBackDelegate(T returnValue);
public delegate T MethodDelegate();
private CallBackDelegate callback;
private MethodDelegate method;
private Thread t;
public ThreadedExecuter(MethodDelegate method, CallBackDelegate callback)
{
this.method = method;
this.callback = callback;
t = new Thread(this.Process);
//XPSDocument requires STA
t.SetApartmentState(ApartmentState.STA);
}
public void Start()
{
t.Start();
}
private void Process()
{
T stuffReturned = method();
callback(stuffReturned);
}
}
void StartXPStoPolyThread()
{
ThreadedExecuter<List<Polyline>> executer = new ThreadedExecuter<List<Polyline>>(GeneratePolyFromXPS, PolyFromXPSComplete);
executer.Start();
}
List<Polyline> GeneratePolyFromXPS()
{
string file = @"C:\Users\Company\Desktop\shapes6.xps";
Company.XPStoPolyline.XPStoPolylineHelper myXPSPoly = new Company.XPStoPolyline.XPStoPolylineHelper(); //init brushes
List<Polyline> LocalList = myXPSPoly.ReadFileOutputPolylineList(file, CurrentConfig.ImportTolerance);
return LocalList;
}
void PolyFromXPSComplete(List<Polyline> PolylistIn)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() =>
{
PolylineList.Clear(); //already initialized in UI thread
PolylineList = new List<Polyline>(PolylistIn.Capacity);
foreach (Polyline currentPolyline in PolylistIn)
{
Polyline correctedPolyLine = new Polyline();
PointCollection myPointCollection = new PointCollection();
Point pointToAdd = currentPolyline.Points[0]; //"The calling thread cannot access this object because a different thread owns it."
myPointCollection.Add(pointToAdd);
for (int i = 1; i < currentPolyline.Points.Count; i++)
{
....copy points
}
correctedPolyLine.Points = myPointCollection;
correctedPolyLine.Stroke = currentPolyline.Stroke;
correctedPolyLine.StrokeThickness = 1;
PolylineList.Add(correctedPolyLine);
}
//display for testing
VectorGridCanvas.Children.Clear();
foreach (Polyline myPolyLine in PolylineList)
{
VectorGridCanvas.Children.Add(myPolyLine);
}
}));
}
Because Polyline is a WPF UIElement class, it is bound to the UI thread. If you try to access any of its properties from another thread, it will throw an exception.
Be mindful that Polyline is a display class, not a data type. You shouldn’t be fiddling with WPF controls or other display elements from background threads. WPF will fight you every step of the way.
Your best bet is to perform your XPS to polyline conversion calculations using an intermediate data type of your own design. Anything but a UIElement will do. A value type struct might be appropriate. Once you’ve done whatever intensive computational gymnastics warrant a background thread, spit out your data and signal the UI thread that the data is good to go. The UI thread can then read the intermediate data and construct Polylines as needed on the UI thread.
If you have tens of millions of polylines to construct and observe an objectionable UI hiccup, you could break the work into smaller batches to keep from blocking the UI for too long at a time.
Polyline (and WPF in general) is bound to the UI thread, and no amount of background threading is going to change that.
Don’t assume threading will save you.
First, do you actually have a demonstrable UI blocking issue? If not, you’re done. No threading needed.
If you do have a UI blocking issue, can it be broken up into smaller batches and still execute on the UI thread? If yes, you’re done. No threading needed.
If using a background thread really is the most effective way to solve your problem, then implement it without using WPF classes like Polyline.