views:

72

answers:

3

Hi, all. 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 frame (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

A: 

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.

LnDCobra
Will it actually redraw if after this call i immediatly begin a new iteration?
ALOR
Is InvalidateVisual thread-safe?
Henk Holterman
**InvalidateVisual is not thread-safe.** Calling it from multiple threads can corrupt your visual's internal flags and/or the arrange queue for that thread. There is no way to prevent this using lock() because WPF uses these structures internally in dispatcher callbacks. Thus InvalidateVisual must be called only from the thread that owns that visual or when the owning thread is absolutely guaranteed to be waiting on a lock outside of any WPF code.
Ray Burns
Well if he doesn't need responsive UI(as he said he doesn't he wouldn't need to multi thread, he cant do the calculations on the same thread as currently its working multi threaded BUT the Invoke function is taking 30% of the time hence why eh doesn't want to use that.
LnDCobra
A: 

Use a DataBinding and let's to that mechanism to think about refreshing your UI, via ObservableCollection, let's say. Good luck

Tigran
+1  A: 

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:

  1. In the new thread, construct the Window and show it (don't forget to Appliation.Run())
  2. Once the new window is shown, get the hWnd using ((HwndSource)PresentationSource.FromVisual(window)).Handle
  3. Pass the hWnd back to the main UI thread using a Monitor or ManualResetEvent
  4. Use WindowsFormsHost to construct a container for the hWnd and add it to your UI
  5. 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.

Ray Burns
Thanks. I tried both option 3 and 1 - and... It works )
ALOR