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);
});
});
}
}
Related
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?
I have a simple treeview, when click on treeview item i just load a text file
Here is the code:
private async void NotesTreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var clickedModel = e.NewValue as TreeFileItem;
if (clickedModel != null && File.Exists(clickedModel.FilePath))
{
_viewModel.NoteLoadingInProgress = true;
using (var reader = File.OpenText(clickedModel.FilePath))
{
var fileContent = await reader.ReadToEndAsync();
_viewModel.ActiveNote = fileContent;
}
_viewModel.NoteLoadingInProgress = false;
}
}
This works, but when I click on treeview item the ui is frozen, until file read is complete. Why such behavior? I have other async methods in my code and they don't block ui.
EDIT: it seems the issue not in reading file, but in setting large amount of text to textbox.text properti via databinding, though setting it directly also take a lot of time and make ui freeze
the ui is frozen, until file read is complete
Streams opened with File.OpenText are never asynchronous. As I describe on my blog, you must open the file using a method with an isAsync parameter (set to true) or a fileOptions parameter (including the FileOptions.Asynchronous value).
it seems the issue not in reading file, but in setting large amount of text to textbox.text properti via databinding
Yes, UI elements are not intended to be used with huge amounts of data. You need to use virtualization if you have large amounts of data to display.
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 please wait form in a windows application that gets rendered before a long running task, the please wait contains an animated gif that needs to "spin" during this process but I can't get this work.
I finally got the form to display correctly using the following code:
frmChooser chooser = frmChooser.Instance;
chooser.Url = this.UrlTextBox.Text;
frmBuildNotification fb = new frmBuildNotification();
fb.Show(this);
fb.Update();
Application.DoEvents();
chooser.BuildTreeView();
fb.Close();
this.Hide();
chooser.Show(this);
The GIF is contained within a PictureBox control on frmBuildNotification
I've read somewhere that you'll need to repaint the form for the image to animate, but this seems a bit tedious?
You need to keep the DoEvents message pump going and it also helps if you throw your notification form on another thread. Try this:
bool done = false;
frmBuildNotification fb = null;
ThreadPool.QueueUserWorkItem((x) =>
{
using (fb = new frmBuildNotification())
{
fb.Show();
while (!done)
Application.DoEvents();
fb.Close();
}
});
frmChooser chooser = frmChooser.Instance;
chooser.Url = this.UrlTextBox.Text;
chooser.BuildTreeView();
done = true;
this.Hide();
chooser.Show(this);
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.