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;
Related
I have a program which needs to do two separate functions simultaneously.
The first is updating the picturebox with a new Bitmap, the second function is a simple function which is effectively instantaneous.
However, because of the delay in updating the picturebox, there is a noticeable delay between the two. The basic code is as follows:
this.pictureBox1.Image = bmp;
this.Invalidate();
OtherFunction();
I have disabled double buffering, and tried all kinds of .Update(), .Refresh(), Application.DoEvents(), but nothing seems to make it any faster.
From my research it seems that setting the Invalidate Flag just means that next time something comes round looking for invalidated forms, it will update it. Is there a way to trigger this manually?
I would rather not introduce a Thread.Sleep() line unless absolutely necessary
Have you seen this question? Maybe you haven't tried the exact magic sequence of calls that force the control to update.
I have a very fast loop which renders animation in a Bitmap buffer and adds filter to it (by using LockBits/UnlockBits to access to the raw data and Marshaling changes to it.) in an independent thread.
I wanted to figure out a way to display the render on the Form, real-time, so I created a PictureBox and linked its Image to the bitmap I created. Everytime immediately after the bitmap is unlocked, I refreshed the PictureBox (using delegate, to do cross-threading) so that the rendering is updated properly.
It's totally fine and works very fast, but one big problem came out as I tried dragging the form to the border of the screen, to see if any bug would appear, and oops, the app collapse..saying 'the bitmap is being locked' This happens when either there's other window moving above the PictureBox or the PictureBox is dragged partially out of the screen. I suspice it because PictureBox can refresh itself when redraw is neccessary, and it does when the bitmap is still being locked. So...any way to sovle this problem? Or anyother ways to render the my animation better?
One of possible solutions could be is create your custom MyPictureBox : PictureBox (say) class which override OnPaintBackground, like this:
protected override OnPaintBackground(...)
{
// nothing, an empty method
}
But I'm not very sure that this will work, you should to check this by yourself.
What I would do, personally, considering your comment:
I have a very fast loop which renders animation in a Bitmap buffer and
adds filter to it (by using LockBits/UnlockBits to access to the raw
data and Marshaling changes to it.) in an independent thread
just forget about PictureBox, cause I found it, personally, too generic and non suitable for high performance rendering. Just write a simple class that handles the drawing of specified bitmap on specified surface.
Imo, this is a best choice.
You can't do that.
Instead, you should copy the image (on the background thread) and put the copy in the PictureBox.
For better performance, you can swap between two images to avoid creating too many images.
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?
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)
I have a Windows form application written in C#. I update the title of the form frequently, but there's a substantial lag between the title changing and the title dislayed in the taskbar being updated.
What's a clean way to force an update / redraw of the task bar's entry for my program? Failing that, how can I force a redraw of the entire task bar?
Elaboration: It turns out that the delay in updating the taskbar is fixed at about 100ms, however this seems to be a delay based on when the Form.Text was last modified. If you modify the text faster then that - say, every 10ms, the taskbar is not updated until the Form.Text has been left unchanged for at least ~100ms.
OS: Vista 32.
Did you try to call Form.Refresh() after updating the title?
Edit:
If you are doing the title updates in a loop you might have to do something along the line of:
this.Invalidate();
this.Update();
Application.DoEvents();
A task bar update more than every 100ms is going to be too fast for the user to resolve anyway. Presumably you're showing some sort of progress or status indicator to the user?
If so, you're crippling the app needlessly doing so many UI updates. That processing time is better used getting the customer's job done.
I think you need to revisit the UI design aspects of what you're trying to do.
I just did a simple test. The changes are quite instantaneous. From the look of it, it's definitely less than 500ms. If you need to update the title at a higher rate, I won't really recommend it. Generally I've seen the fastest update rate of twice per second.
EDIT:
I tested using keypress event. When I hold down the key for a fast repeat, it won't update until I've release my key. Thus, same scenario as your setup.
Btw, why do you need to update every 10ms? Just keep in mind that Thread.Sleep(timeout) with timeout of less than 50ms is not accurate. Also, 10ms timeout will equal to 100Hz, unless you're using high end display, you'll have miss a few frame. Most general LCD have a refresh rate of 60Hz. And our eye can't differentiate anything faster than 25Hz. Thus 40ms delay is more than enough, if you want to animate. Generally I would recommend 15Hz (67ms) for simple animation. If just want to scroll some text, 2Hz is more than enough. Anything faster will make the user dizzy.
Are you using code similar to this in your form?:
private void Form1_Load(object sender, EventArgs e)
{
Timer t = new Timer();
t.Interval = 10;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
int aa = 0;
void t_Tick(object sender, EventArgs e)
{
this.Text = aa++.ToString();
}
Works fine for me - no lag between form and taskbar at all.
Are you sure you aren't locking up the GUI thread and not calling Application.DoEvents on your loop?
I'm using the new Windows 7 beta, so it's a small chance that it's different behavior.