window manager and multiple UI threads

Topics: UI Architecture
Jan 27, 2014 at 8:38 PM
I have a WPF application in which I'm using caliburn.

My main window utilizes WPF 3D component which runs a machine simulation thats UI intensive.
On the main window, i have a button that opens up a 2nd window (not modal) which contains simple WPF form elements such as buttons, listboxes,etc. In code, i would simply call ShowWindow method via IWindowManager available to me via the ImportingConstructor.

The problem is that the performance on the 2nd window is undesirably slow since the main window is utilizing the UI thread via 3d component. How would i use the ShowWindow method in such a way that it puts the 2nd window on a 2nd UI thread? I have seen some articles that have a solution for this but not using caliburn methods. Any idea or am i going about this the wrong way?
Jan 29, 2014 at 12:13 PM
To spawn a Window on a new thread, you just need to initialize a Dispatcher on a new thread, and run the Window constructor on such a thread (as you probably now).
So, you would just need to spawn the new thread, initialize the Dispatcher and call IWindowManager.ShowWindow on such a thread (or create an extension method to wrap it up). The issue with CM is that it statically refers to a single Dispatcher, when invoking Execute.OnUIThread and alike (in particular, the dispatcher used to build up the bootstrapper), which can lead to lots of issues when dealing with multiple UI threads.

A possible (general) solution to this problem, would be to parametrize every call that is redirected to the dispatcher with a thread affinity context, and store an association between the thread affinity context and an actual Dispatcher. Unfortunally, this is quite a complex modification of CM base-code, and I dubt (haven't tested, tho) that it can be achieved just extending the standard version of CM.

A possible work-around, would be to procede this way:
  • Modify the bootstrapper OnStartup to avoid to call the base code, and create instead a new thread/dispatcher, switch the Dispatcher used in Execute.OnUIThread with the new Dispatcher.
  • On the above thread specified before, call the base.Startup. This would ensure that the main window is created on a different thread while CM mechanics are still working. Thread-affinity should not be a problem, unless the main window tries to access UI objects defined on the first Dispatcher (not: Application-wide resources could be a poblem, unless frozen, but in such a case, you can re-define them as Window resources).
  • Once the 3D window has been created, switch back the Dispatcher on Execute.OnUIThread.
There is an high chance that some CM functions throw errors with this approach, but there is also an high chance that extension points can be used to avoid them (e.g. some parts of the Actions engine are performed on the UI thread using Execute.OnUIThread, and would probably throw if they try to access to UI objects owned by different threads).

Note that these are just speculations, and are not based on a real-life experience. :)

On a side note, I have looked at a couple of SO questions (like this), and it seems to me that it would be far better to follow the proposed idea: build-up the 3D content on a separate thread, freeze the resulting model and pass it to the 3D view-port. This way, the 3D content would keep the UI thread occupied just for the time required for actual rendering. Of course, if the geometry you are dealing with changes at a quite high rate, is dynamic, or requires real-time interaction/modification, I am afraid such an approach is not usable.
Jan 29, 2014 at 4:36 PM
thanks for your response. i'm a bit worried about dispatcher switching as i need constant UI updates on the main thread as there is real time animations going on all the time
Jan 30, 2014 at 11:38 AM
I have never used WPF for 3D visualization, but since premature optimization is the source of all evil, I would still try with the freezing/cloning approach, and measure performances. :)