I use ConcurrentQueue<Action> FrameActions where I add image frames that come from a high-speed camera - (60-90 FPS)
The Action contains methods to update the WritableBitmap image in UI.
For updating frames I use a separate thread which code is below
void FrameUIThread()
{
do
{
if (FrameActions.TryDequeue(out Action currentAction))
{
Application.Current.Dispatcher?.BeginInvoke(DispatcherPriority.Loaded, currentAction);
}
else
{
// DoEvents
Application.Current.Dispatcher?.Invoke(DispatcherPriority.Loaded, new Action(() => { }));
}
} while (true);
}
There was a problem that sometimes WritableBitmap is updated and a new image comes in, but the rest of the application - (all the buttons, counters and stuff are completely blocked, I can't click anything).
Found that using DoEvents - helps in this situation.
But now the flow of images is not smooth, but interrupted and goes spurts in some moments of time.
Is there any way to solve this problem?
Or could there be a better solution?
Related
I know there is a bunch of threads about initializing stuff in a different thread so you dont need to freeze your UI. But in my case this initialization involves creating a lot of plots (polylines in a canvas) so it seems to need to freeze the UI.
It could be good enough to hide the frame where things are being initialized (I already let a "loading.." message in below) and freeze the UI then (couple of seconds) and then show again the frame.
This is what I have so far. But is not working... it freezes the UI before hiding nothing and it unfreezes after loading the fully initializes frame.
Otherwise the thing works like a charm.
void Historics_showExperimentResults(object sender, EventArgs e)
{
aepPage = new AEPPage();
resultsPage = new AEPResultSet();
// I try to hide the frame. Below there is a "Loading..." nice text.
// not sure if it's the best way but it works if I dont show anything at the end
ParadigmFrame.Dispatcher.Invoke((Action)delegate
{
ParadigmFrame.Content = null;
ParadigmFrame.UpdateLayout();
});
// This is the initialization that needs to have the GUI thread
//because it draw some plots and polylines
aepPage.Dispatcher.Invoke((Action)delegate
{
aepPage.init(resultSet);
});
//Then I want to go and visualize the initialized page with the plots
ParadigmFrame.Dispatcher.Invoke((Action)delegate
{
ParadigmFrame.Navigate(aepPage);
});
}
Any clue??? As I said I tried to put the init in a different thread and add a event when finished, but this threads needs the control over the UI for initializing the polylines in the canvas so .. it doesn't work :(
Thanks in advance !
It looks like Historics_showExperimentResults is already running on UI thread. Try this:
void Historics_showExperimentResults(object sender, EventArgs e)
{
aepPage = new AEPPage();
resultsPage = new AEPResultSet();
new Thread(_ =>
{
// I try to hide the frame. Below there is a "Loading..." nice text.
// not sure if it's the best way but it works if I dont show anything at the end
ParadigmFrame.Dispatcher.Invoke((Action)delegate
{
ParadigmFrame.Content = null;
ParadigmFrame.UpdateLayout();
});
// This is the initialization that needs to have the GUI thread
//because it draw some plots and polylines
aepPage.Dispatcher.Invoke((Action)delegate
{
aepPage.init(resultSet);
});
//Then I want to go and visualize the initialized page with the plots
ParadigmFrame.Dispatcher.Invoke((Action)delegate
{
ParadigmFrame.Navigate(aepPage);
});
}).Start();
}
I won´t mark this as an answer since it is not .. but still is the workaround I´m using right now.
What I did is split the fade out, the init and the fade in into pieces.
I created a storyboard, fade out and attach the next step to the Finished event so, in some kind of pseudocode it would be:
StoryBoard sb = new StoryBoard;
OnClick(object sender blabla)
{
storyBoard.add(fade out animation over ParadigmFrame);
storyBoard.Completed += performInit;
storyBoard.Begin();
}
So this part is executed and the paradigmFrame disappears showing the ¨Loading...¨ message that´s bellow.
Then ..
private blabla performInit()
{
aepPage.Dispatcher.Invoke((Action)delegate
{
aepPage.Finished += initFinished;
aepPage.init(resultSet);
});
}
For sure I created the Finished event in my aepPage class and fired it when the initialization is finished. So during all this process the UI is freeze. The "Loading..." message is visible and it is not awful but the REAL solution should not freeze the UI in here...
And then I show it up
private void initFinished()
{
storyBoard.add(fade in animation over ParadigmFrame);
storyBoard.Completed -= performInit;
storyBoard.Begin();
}
This is my long and ugly workaround ... I´m still open to new solutions !!!
Thanks !
I have a Silverlight 5 application that uses ImageTools for Silverlight to save a Canvas to a PNG image. I understand that I need to work with the Canvas on the UI thread and have the following code, which works:
if (saveFileDialog.ShowDialog() == true)
{
var stream = saveFileDialog.OpenFile();
writeableBitmap.Dispatcher.BeginInvoke(delegate
{
ExtendedImage extendedImage = writeableBitmap.ToImage();
new PngEncoder().Encode(extendedImage, stream);
});
}
The problem is that if the Canvas is very large it can take a noticeable time for the code in the BeginInvoke to complete. Since this is running on the UI thread it freezes the browser window during its execution.
After the user selects the location of where to save the exported image, I'd like to popup some child window that tells the user, "Please wait...", then run the image saving code posted above, and afterwards hide the child window automatically, but I'm not having much luck accomplishing that.
For starters, the BeginInvoke code runs asynchronously, so how do I know when it has completed?
If you need to call ToImage() on the UI Thread thats fine, but it doesnt mean you have to encode the image too.
Something like this will ensure the UI stays responsive.
if (saveFileDialog.ShowDialog() == true)
{
using (var stream = saveFileDialog.OpenFile())
{
writeableBitmap.Dispatcher.BeginInvoke(delegate
{
ExtendedImage extendedImage = writeableBitmap.ToImage();
System.Threading.ThreadPool.QueueUserWorkItem(item =>
{
new PngEncoder().Encode(extendedImage, stream);
});
});
}
}
When you have a button, and do something like:
Private Function Button_OnClick
Button.Enabled = False
[LONG OPERATION]
End Function
Then the button will not be grayed, because the long operation prevents the UI thread from repainting the control. I know the right design is to start a background thread / dispatcher, but sometimes that's too much hassle for a simple operation.
So how do I force the button to redraw in disabled state? I tried .UpdateLayout() on the Button, but it didn't have any effects. I also tried System.Windows.Forms.DoEvents() which normally works when using WinForms, but it also had no effect.
The following code will do what you're looking for. However I would not use it. Use the BackgroundWorker class for long time operations. It's easy to use and very stable.
Here the code:
public static void ProcessUITasks() {
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate(object parameter) {
frame.Continue = false;
return null;
}), null);
Dispatcher.PushFrame(frame);
}
Here you will find a sample on how to use the BackgroundWorker.
InvalidateVisual(); #HCL is right... don't do this
Like you say, it is better to start use a background thread / dispatcher and keep the UI thread unblocked. Consider looking at the Reactive Extensions library from Microsoft for high level asynchronous ui programming
In Windows.Forms, you can Button.Refresh().
In Windows.Forms or WPF, you can yield to the message pump to let it redraw. Async/Await were designed to allow you to do this without the nastiness of HCL's answer.
Private Async Function Button_OnClick
Button.Enabled = False
Await Task.Yield
[LONG OPERATION]
End Function
There is a website that contains a single image from a webcam. Each time the site is hit, the most current image of the webcam is displayed. I want to make a real time video by hitting the site continuously.
I have searched and tried several things but cannot get it to refresh at a reasonable rate.
public MainWindow()
{
InitializeComponent();
this.picUri = "http://someurl";
this.thWatchVideo = new Thread(new ThreadStart(Watch));
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.None;
_image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri(this.picUri);
_image.EndInit();
this.imgVideo.Source = _image;
this.thWatchVideo.Start();
}
public void Watch()
{
while(true)
{
UpdateImage();
}
}
public void UpdateImage()
{
if (this.imgVideo.Dispatcher.CheckAccess())
{
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.None;
_image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri(this.picUri);
_image.EndInit();
this.imgVideo.Source = _image;
}
else
{
UpdateImageCallback del = new UpdateImageCallback(UpdateImage);
this.imgVideo.Dispatcher.Invoke(del);
}
}
Problem is, this is too slow and takes too long to refresh and the app just hangs.
I got this to work in Windows Forms with the PictureBox control but cannot get it to work in WPF. I refuse to believe that WPF is inferior to forms.
This app will always just hang (whether winforms or WPF) because you've got an infinite loop running everything it does on the UI thread. Your app hangs because you're not allowing the UI thread any time to process user input (such as resizing the window or trying to close the app).
With regard to your performance: have you tried profiling your code? I suspect that the problem is to do with you repeatedly hammering a webserver for an image, since you're never likely to get enough requests-per-second to make any kind of real-time video from static images. (There's a reason that we have video streaming codecs!)
instead of recreating whole image try to change only UriSource property.
Check out my answer to this: Showing processed images from an IP camera
Also, make sure the communication is done on a separate thread.
I suggest that the Bitmap image is a dependency object being created on a non-GUI thread. You then invoke UpdateImage on the GUI thread. Since the bitmap image dependency object wasn't created on/(owned by) the GUI thread, you get the "different thread owns it" error.
How about this as a workaround?
Copy the image temporarily to a local file location in your Watch routine.
Add a Thread.Sleep to the watch routine so that you don't hammer the CPU with the endless loop on this thread.
Use BeginInvoke instead of Invoke.
Load and update the image in the UpdateImage routine so that the image and the imgVideo objects are on the GUI thread. Update the image by reading it from your local file copy.
Without knowing the specifics of how you make Watch run on its own thread (using Background worker?) I think this approach will work for you.
I am a bit of a newbie when it comes to windows client programming. I have a background worker that has a DoWork event and a RunCompleted event wired up. If an exception gets thrown in DoWork, I want to make changes to my UI, however, I cant because it is in a different thread. I can communicate the error to RunCompleted, but that doesn't help me either.
call Dispatcher.BeginInvoke. Basically, you want code like this:
void UpdateState(WhatEverType someObject)
{
if (! Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>UpdateState(someObject));
}
else
{
//make the UI changes here.
}
}