There is the situation - i made some math modelling app on C#(WPF), showing it's results in realtime vector graphics. Math is doing fine (iterative process, per frame drawing), but there is the problem - while i'm using additional thread to do calculations, to draw result is should use Dispatcher.BeginInvoke (each frame!) which is quite an expensive operation (profiling showed that it tooks almost 30% of time). But if i try to do all in the UI thread, i would see only the last fram开发者_C百科e (as expected).
Calculations and showing the result is the only task of app, so i even don't need GUI to response during the modelling - but i need to show results in realtime...
So i want to avoid BeginInvoke and at the same time to display results, and i don't see a way. Any ideas how to arrange calculations in such way? The graphics are displayed in DrawingVisual in some empty FrameworkElement.
Thx
Option 1: Rendering event
Do your calculations on the separate thread, but queue changes to the UI and update them on the Rendering event. It would look like this:
PresentationSource.FromVisual(window).CompositionTarget.Rendering += (obj, e) =>
{
foreach(var update in _queue)
UpdateUI(update);
}
This code assumes _queue's is a thread safe (synchronized) queue. You can create such a queue class or download one. Alternatively you can surround the "foreach" with a "lock(_queue)".
The reasons this is better than Dispatcher.BeginInvoke() are: 1. It is called just before each frame so if the frame rate goes down it is called less frequently, and 2. It processes the changes in batches.
Option 2: Multiple UI threads
You can run a portion of your UI on a separate thread by using a separate hWnd. You can use WindowsFormsIntegration for this or use some Win32 magic. Here is one way to do it:
- In the new thread, construct the Window and show it (don't forget to Appliation.Run())
- Once the new window is shown, get the hWnd using
((HwndSource)PresentationSource.FromVisual(window)).Handle
- Pass the hWnd back to the main UI thread using a Monitor or ManualResetEvent
- Use WindowsFormsHost to construct a container for the hWnd and add it to your UI
- Handle resize events in the main UI, passing them down to the contained window
Option 3: Animations
You can create your own custom Animation-derived classes to animate your UI items. These can use data precaculated from the model. The advantage of doing this is precise time synchronization: Every time your code is called it knows exactly "when" (in animation time) it is calculating the position for. So if some other program hogs the CPU or GPU for a second and slows down and the frame rate, the animation still proceeds smoothly at the lower frame rate.
To do this, subclass DoubleAnimationBase
and override GetCurrentValueCore() to do your custom calculation, combined with the required Clone() and CreateInstanceCore() overrides. Then use this to animate your properties.
If simply animating Double properties of existing objects is not enough, it is possible to create an animation that produces entire objects such as Geometry, Geometry3D, Drawing, Drawing3D or even UIElement. To do this, subclass AnimationTimeline and override GetCurrentValue() along with the others.
The advantage of using animations is the same as with the rendering event, except you can let WPF handle all the clock synchronization and replay speed issues for you instead of doing it yourself. It may also result in less code if you can animate only the properties that are changing.
At the end of each iterative process you can manually call the InvalidateVisual() method on the visual object that you need to redraw and this will send a paint message to redraw the control/window.
Use a DataBinding and let's to that mechanism to think about refreshing your UI, via ObservableCollection, let's say. Good luck
精彩评论