Wait for animation, render to complete - XAML and C# - c#

I have a situation where I am animating part of my XAML application, and I need to wait for the animation AND rendering to complete before I can move on in my code. So far the tail end of my function looks like:
ProcExpandCollapse.Begin();
while (ProcExpandCollapse.GetCurrentState() != ClockState.Stopped) { }
}
Which, in theory, will wait until the animation is finished. But it will not wait until the rendering is finished - the thread drawing the application might still not have re-drawn the animation.
The animation is expanding a UIElement, and then the next part of my code uses it's rendered size to do some things. My question then is, how do I wait until my UI Element is re-rendered before moving on?

I finally found an answer (although I had to pay for consulting services)! Essentially what I ended up doing was putting the bit of code which uses the rendered controls on the control dispatcher's queue with very low priority, so that it naturally renders before handling that task. For example:
mycontrol.Dispatcher.BeginInvoke((Action)delegate{textbox1.Text = "Grazie";});
mycontrol.Dispatcher.Invoke((Action)delegate{GetScreenshot();}, DispatcherPriority.Background);
The code will then procede after GetScreenshot is called and finished, which will be after the rendering is completed (because rendering has higher priority than background).

The Storyboard object has a 'Completed' event you can subscribe to. It will be called when the animation is done. This what you are looking for?
Basically I think you want "ProcExpandCollapse.Completed +=" (then hit tab a view times, VS will generate a snippet for you)

Related

Multithreaded PictureBox controls rendering

So, the subject is describes my question itself but I'll add some notices here. I have around 20 PictureBox controls and when the form appears it takes like 0,5 second to load them all. I tried to attach nvapi.dll to enable HW rendering, but it looks like that the problem is in something else. So basically I need to render 20 images in multithreaded mode so it'll be faster.
You can use LoadAsync method of PictureBox to load your images asynchronously:
this.pictureBox1.LoadAsync(imagePath);
A call to the LoadAsync method sets the
ImageLocation
property to the value of url. Besides calling the LoadAsync method,
you must set the
WaitOnLoad property to false (default value) to load an image asynchronously.
Simple answer is that you cannot multi-thread rendering images to picturebox controls, however;
(1) you mention loading 20 1024x768 images - your user cannot see all those at once, so is there a way of loading "just in time" instead if "just in case"
(2) you can definitely multi-thread load Images from files, and then use Invoke() on the UI thead to assign the image created in the loading thread to the pictureBox.Image.

How can i handle LayoutUpdated or stop a control rendring in WPF?

i want to cancel or stop or handle rendering(or drawing) a WPF Element for lower cpu usage(cancel all forever animations and rendering in background or collapsed visibility items for use best performance in my app).
i read all Optimizing WPF Application Performance
void MainWindow_LayoutUpdated(object sender, EventArgs e)
{
e.Handled = true;//can i handle an object rendering?
}
LayoutUpdated is not going to help you at all. It is an event that fires on all UI elements associated with a Dispatcher after a full layout pass has completed, meaning the work is already done.
You have a few options, depending on what your goal is.
Hide the Element
If you don't want the element to be visible or perform any layout, then you can set its Visibility property to Collapsed. It should skip most of its layout logic and all of its rendering logic while it is collapsed.
Remove the Element
Removing the element from the visual tree is a sure way to keep it from doing anything. You can add it back later if you need to. The process for how to do this depends a lot on how your code is currently setup.
Reduce the Performance Cost of the Element
If the element is expensive because it is doing a lot of work, you could look into optimizing it in general, and/or look into disconnecting its event listeners and bindings during the time when you don't want it doing work.
Pause UI Processing
If you want to temporarily pause all UI processing while you perform some operation (to prevent reentrancy), you can use Dispatcher.DisableProcessing. Note that this will disable UI processing for the entire thread, not just the processing of a specific element.
using (Dispatcher.DisableProcessing())
{
// Do work while the dispatcher processing is disabled.
}
A Static Picture of the Element
If you want to permanently disable all processing of an element, I don't know of a way to do that while still keeping it visible on the screen. However, you could use a RenderTargetBitmap to render the element to an ImageSource and then replace the original element with an Image element that has its Source property set to the bitmap.
RenderTargetBitmap target = new RenderTargetBitmap((int)element.RenderSize.Width, (int)element.RenderSize.Height, 96.0, 96.0, PixelFormats.Default);
target.Render(element);
image.Source = target;
Now you basically have a "picture" of the element in place of where the original element was.

Using another GameComponent to measure the elapsed times

Okay, so I started developing a game. The first thing I wanted to set up was an extensive Debug viewer, which can ultimately show things like buttons pressed (only those that I want to listen to of course) and a graph containing information about frametime, a histogram of sorts. However, I wanted to do this the correct way. It occurred to me that I wanted to use the DrawableGameComponent class for the debugviewer, and draw the debug information there. However it seems that it can't measure the Update and Draw times correctly, as I start the update timer at the beginning of the update and end the update at the beginning of the draw. However, I shortly thereafter realized that first the main game is executed (update - draw - wait) and then are all the other components executed (update component 1, draw component 1), so they aren't intertwined. This means that I can't calculate the elapsedtime (for both Update, Draw and overhead). As we can read in this blog it's better to measure frametime as opposed to frames per seconds.
So enough with the back story and on to my main question: How do I measure the frametimes needed for both the Update, the Draw and the overhead from the main game in another DrawableGameComponent, or should I just use a class and update that in the game?
I hope that everything is clear, have a great day.
i found this http://gamedevwithoutacause.com/?p=876 which explaines exactly what you need.
If you can't measure the Update and Draw times correctly, using the update timer, can't you simply use DateTime.Now? You get it at the beginning of your Update, then every next cycle (after Update, Draw and overhead) you just subtract the DateTime.Now measured previously.

I want to force a render, but only draw as fast as possible (InvalidateVisual / CompositionTarget.Rendering)

I'm working on a real-time WPF/Silverlight (and soon WP7) visualization component and I'm looking for the best solution to force a redraw of the entire component in a Game-loop style. Redraw should be on-demand, but I don't want to back up the message pump with re-draw calls. Most of the drawing in my component is done using non-WPF primitives (e.g. Bitmap Interop, Direct2D) so my code does not use InvalidateVisual, and as a result, currently looks like this
// Pseudocode, doesnt compile, just to convey the meaning
public void InvalidateElement()
{
if (CurrentlyDrawing)
return;
Dispatcher.BeginInvoke(() =>
{
CurrentlyDrawing = true;
DoDrawInternal();
CurrentlyDrawing = false;
}
}
Ok so this is great. If I call InvalidateElement lots of times I get good responsiveness. However, what I want to do is ensure I can push data to my visualization component as fast as possible but only draw when the component is able to draw, and not keep drawing to catch up with the data once the input stream completes.
No I can't override OnRender, I'm using non-WPF drawing inside WPF ;-)
Basically what I want is something like the old Invalidate() / OnPaint in WindowsForms, or better yet, a game loop in DirectX.
At the moment I get the situation where if I have an external thread that pushes data to the visualization component at a high rate then if I Stop pushing data I get another 20 seconds worth of refreshes to get through before the component stops drawing. I want to stop drawing as soon as data has gone in.
Another idea I had was to handle CompositionTarget.Rendering in the visualization component then implement some sort of rudimentary Queue to push data to and the Rendering event consumes this data as fast as it can.
In Summary
Given a WPF visualization component, V, and a datasource which pushes it data every 1ms, D, how can I ensure that no matter the datarate of D, V draws data at 30FPS (or whatever it can do) and updates itself in chunks, sort of how a game render loop does in DirectX?
When the data stops, V should redraw everything it has up to now in one go. When the data is too fast, V draws larger chunks at a time to compensate.
If you need more information I'd be happy to share it. Right now I've just posted a synopsis to gauge if there are any quick fixes but a fuller Q with code examples can be provided on request.
Best regards,
You might want to consider rendering on the CompositionTarget.Rendering event and throttling on the invalidated state.
Silverlight game loop example (F#):
/// Run game
let runGame () =
let state = gameState.GetEnumerator()
let rate = TimeSpan.FromSeconds(1.0/50.0)
let lastUpdate = ref DateTime.Now
let residual = ref (TimeSpan())
CompositionTarget.Rendering.Add (fun x ->
let now = DateTime.Now
residual := !residual + (now - !lastUpdate)
while !residual > rate do
state.MoveNext() |> ignore
residual := !residual - rate
lastUpdate := now
)
Play the game: http://trelford.com/blog/post/LightCycles.aspx
Read the source: https://bitbucket.org/ptrelford/lightcycles
You can listen to the CompositionTarget.Rendering event, which is triggered right before WPF renders the UI, and do your drawing in there.
Another tidbit.. InvalidateVisuals() is nothing like Form.Invalidate(), as it also causes re-layout which is expensive. If you want something like Form.Invalidate(), then create a DrawingGroup (or bitmap image) "backingStore", place it in the DrawingContext during OnRender(), and then update it whenever you want. WPF will automatically update and repaint the UI.
Have you thought of using a dispatch timer running at 30FPS, then take a snapshot of the current data and rendering it at each timer tick? If you want to avoid redrawing if nothing has changed, you can simply keep timestamps for LastChanged and LastRendered, only performing an actual redraw if LastChanged > LastRendered. Basically updating the data and rendering the data are decoupled from one-another; the main trick is making sure you can somehow get a coherent snapshot of the data when the rendering thread wants to render it (i.e. you'll need some sort of locking.)
I was recently working with a project that required a game loop like style. Although my example is purely in F#, you can figure it out how you can do that way in C# too, may be use some interop code to initialize the timer and hooking up events as given in this below link,
http://dl.dropbox.com/u/23500975/Demos/loopstate.zip
The sample doesn't show how to redraw, it just updates the underlying stock data for every 500ms, It should pretty much work for any kind of drawing mechanisms with WPF. The core idea is to use composable events, in F# an event is a first-class citizen + an IObservable (reactive extensions for C#), so we can easily compose functions that in-turn return a set of events or a single event. There is a function Observable.await, which takes in an Observable and also has a state to return.
eventSource
|> Observable.await(fun (t:State.t) e ->
// return the modified state back on every check or in the end
match e with
// start button click
| Choice1Of3(_) ->
{t with start=true}
// stop button click
| Choice2Of3(_) ->
{t with start=false}
// timer tick event,
| Choice3Of3(_) ->
if t.start = true then
handleStockUpdate(t)
else t
) (state)
I just used some of FP terms here, but it should work just fine with normal C# (OO) way of doing things here.
Hope this helps!
-Fahad
I'm not sure why you would use WPF for your front-end if you're drawing using non-WPF elements and require the Invalidate() method that was provided by WinForms? Can't you just switch the UI to use WinForms?

Draw image on a form from a separate thread

I am currently working on a Windows.Forms application. It's basically a simple motion detection problem.
I have a button on a form, that when pressed launches a background worker that does the following :
Fetch an image from disk
Create a new bitmap, to be used as the buffer.
Perform Motion Detection
From the results of Motion Detection, update the buffer (using the buffer's drawing surface)
Fire the Progress Changed Event with an argument consisting of a clone of the buffer, basically (sender as BackgroundWorker).ReportProgress((Bitmap)buffer.Clone())
In the Progress Changed Event, I then draw the buffer to screen.
if (!PnlImage.IsDisposed)
PnlImage.CreateGraphics().DrawImageUnscaled(buffer, 0, 0);
I can't help wondering if this is the best way to draw the updated image on the screen. Can anyone suggest any improvements I can make?
Thanks.
EDIT :
I have since updated the program to use the .NET Framework 4, and we're no longer using a BackgroundWorker. Instead, we are now using the System.Threading.Tasks namespaces, and using Invoke to update the background image from within the task.
Thanks to all replies.
I believe the root of any problems you may be experiencing is the fact that any GUI updates must be done on the UI thread. You cannot safely update the UI from another thread. So, basically, you need to do something like the following (I'm just changing the background color as an example, but you can do whatever you like):
private void SomethingCalledFromBackgroundThread()
{
panel1.Invoke(new DoUpdatePanel(UpdatePanel), Color.Blue);
}
private delegate void DoUpdatePanel(Color aColor);
private void UpdatePanel(Color aColor)
{
panel1.BackColor = aColor;
}
============ Update =======>
#Ash you have mischaracterized my answer. I did not say to call Invoke from within ProgressChanged. #Jean keep in mind that ReportProgress/ProgressChanged is being run asynchronously--which is why you find yourself making a clone of your image. This would not be necessary if you use Invoke from within your background thread, rather than ReportProgress.
I'm not sure if this is strictly true but I'm sure you can't cross thread GUI/Control operations on a separate thread as it is handled by default on a dedicated GUI thread.
I tried to do something similar to this before and in the end i decided on an entirely different approach to it as setting a property to false was the worst way to get it to work.
The ProgressChanged and RunWorkerCompleted Events allow you to update the UI directly. It is only the DoWork event handler that you must not access the from. See MSDN:
You must be careful not to manipulate
any user-interface objects in your
DoWork event handler. Instead,
communicate to the user interface
through the ProgressChanged and
RunWorkerCompleted events.
This is one of the major benefits in using the BackgroundWorker over creating your own thread. So TheObjectGuy is not correct, you do not need to use BeginInvoke/Invoke in ProgressChanged.
As long as your image is not too large, cloning it should not cause any serious performance issues. Run some performance tests with bigger images if you have concerns.
Otherwise, to avoid tricky synchronization issues such as using lock, I think making a clone of the image is a good way to keep things simple.
Using the ProgressChanged event is fine. What is not fine is drawing directly to the screen. Your image will disappear when you minimize and restore the form. The workaround is simple:
PnlImage.BackgroundImage = buffer;

Categories

Resources